meocord 4.0.0 → 4.1.0-beta.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.
- package/CHANGELOG.md +209 -0
- package/README.md +1024 -106
- package/THIRD_PARTY_NOTICES.md +10 -1
- package/dist/cjs/_shared/cooldown-store-DTqJP31S.cjs +1398 -0
- package/dist/cjs/_shared/explained-error-dAMLbuqv.cjs +32 -0
- package/dist/cjs/_shared/input-runner-CucAPIqF.cjs +1194 -0
- package/dist/cjs/_shared/metadata-key.enum-CXFdtEX9.cjs +34 -0
- package/dist/cjs/_shared/shard-context-84GuH11q.cjs +710 -0
- package/dist/cjs/_shared/translator-DEV2jyFH.cjs +254 -0
- package/dist/cjs/common/index.cjs +54 -40
- package/dist/cjs/core/index.cjs +1510 -212
- package/dist/cjs/decorator/index.cjs +481 -86
- package/dist/cjs/enum/index.cjs +3 -31
- package/dist/cjs/testing/index.cjs +496 -27
- package/dist/esm/bin/app-template/README.md.template +41 -6
- package/dist/esm/bin/app-template/_env.example.template +2 -0
- package/dist/esm/bin/app-template/meocord.config.ts.template +7 -0
- package/dist/esm/bin/app-template/package.json.template +2 -1
- package/dist/esm/bin/app-template/src/app.ts.template +3 -0
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template +40 -9
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template +15 -8
- package/dist/esm/bin/app-template/src/controllers/context-menu/sample.context-menu.controller.spec.ts.template +8 -10
- package/dist/esm/bin/app-template/src/controllers/context-menu/sample.context-menu.controller.ts.template +4 -4
- package/dist/esm/bin/app-template/src/controllers/modal-submit/sample.modal-submit.controller.spec.ts.template +8 -10
- package/dist/esm/bin/app-template/src/controllers/modal-submit/sample.modal-submit.controller.ts.template +5 -4
- package/dist/esm/bin/app-template/src/controllers/select-menu/sample.select-menu.controller.spec.ts.template +13 -10
- package/dist/esm/bin/app-template/src/controllers/select-menu/sample.select-menu.controller.ts.template +5 -2
- package/dist/esm/bin/app-template/src/controllers/slash/sample.slash.controller.spec.ts.template +21 -9
- package/dist/esm/bin/app-template/src/controllers/slash/sample.slash.controller.ts.template +8 -6
- package/dist/esm/bin/app-template/src/guards/owner.guard.spec.ts.template +36 -0
- package/dist/esm/bin/app-template/src/guards/owner.guard.ts.template +14 -0
- package/dist/esm/bin/app-template/src/main.ts.template +5 -2
- package/dist/esm/bin/app-template/src/presenters/app.presenter.spec.ts.template +24 -0
- package/dist/esm/bin/app-template/src/presenters/app.presenter.ts.template +16 -0
- package/dist/esm/bin/builder-template/builder/context-menu.builder.template +1 -3
- package/dist/esm/bin/builder-template/controller/autocomplete.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/button.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/channel-select-menu.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/context-menu.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/mentionable-select-menu.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/modal-submit.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/primary-entry-point.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/role-select-menu.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/select-menu.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/slash.controller.template +0 -1
- package/dist/esm/bin/builder-template/controller/user-select-menu.controller.template +0 -1
- package/dist/esm/bin/builder-template/filter.spec.template +53 -0
- package/dist/esm/bin/builder-template/filter.template +21 -0
- package/dist/esm/bin/builder-template/guard.spec.template +17 -11
- package/dist/esm/bin/builder-template/guard.template +12 -3
- package/dist/esm/bin/builder-template/interceptor.spec.template +38 -0
- package/dist/esm/bin/builder-template/interceptor.template +20 -0
- package/dist/esm/bin/builder-template/pipe.spec.template +29 -0
- package/dist/esm/bin/builder-template/pipe.template +12 -0
- package/dist/esm/bin/builder-template/service.template +1 -1
- package/dist/esm/bin/generator.js +64 -2
- package/dist/esm/bin/helper/filter-generator.helper.js +45 -0
- package/dist/esm/bin/helper/guard-generator.helper.js +8 -1
- package/dist/esm/bin/helper/interceptor-generator.helper.js +45 -0
- package/dist/esm/bin/helper/pipe-generator.helper.js +45 -0
- package/dist/esm/bin/helper/service-generator.helper.js +5 -0
- package/dist/esm/bin/meocord.js +144 -50
- package/dist/esm/build/load-config.pre-entry.js +23 -0
- package/dist/esm/build/native-addons.js +19 -2
- package/dist/esm/build/rsbuild-config.js +41 -3
- package/dist/esm/common/cooldown-store.js +76 -0
- package/dist/esm/common/decorator.js +27 -1
- package/dist/esm/common/errors.js +105 -0
- package/dist/esm/common/execution-context.js +150 -0
- package/dist/esm/common/explained-error.js +29 -0
- package/dist/esm/common/index.js +8 -0
- package/dist/esm/common/metadata.js +43 -0
- package/dist/esm/common/response/attachments.js +129 -0
- package/dist/esm/common/response/components.js +101 -0
- package/dist/esm/common/response/flags.js +35 -0
- package/dist/esm/common/response/install-context.js +53 -0
- package/dist/esm/common/response/presenter.js +52 -0
- package/dist/esm/common/response/response-state.js +596 -0
- package/dist/esm/common/theme.js +1 -0
- package/dist/esm/common/translator.js +175 -0
- package/dist/esm/core/command-localizations.js +72 -0
- package/dist/esm/core/command-registration.js +278 -0
- package/dist/esm/core/cooldown-runner.js +74 -0
- package/dist/esm/core/defer.js +40 -0
- package/dist/esm/core/event-requirements.js +334 -0
- package/dist/esm/core/fallback.js +82 -0
- package/dist/esm/core/filter-runner.js +66 -0
- package/dist/esm/core/guard-runner.js +160 -0
- package/dist/esm/core/handler-input.js +90 -0
- package/dist/esm/core/handler-pipeline.js +248 -0
- package/dist/esm/core/handler-registry.js +156 -0
- package/dist/esm/core/index.js +2 -0
- package/dist/esm/core/input-runner.js +89 -0
- package/dist/esm/core/interceptor-runner.js +70 -0
- package/dist/esm/core/lifecycle-order.js +33 -0
- package/dist/esm/core/login-failure.js +29 -0
- package/dist/esm/core/meocord-factory.js +121 -15
- package/dist/esm/core/meocord.app.js +371 -181
- package/dist/esm/core/shard-context.js +178 -0
- package/dist/esm/core/shard-manager.js +188 -0
- package/dist/esm/core/shard-messages.js +5 -0
- package/dist/esm/core/stage-scope.js +55 -0
- package/dist/esm/decorator/app.decorator.js +22 -4
- package/dist/esm/decorator/command-builder.decorator.js +17 -7
- package/dist/esm/decorator/controller.decorator.js +44 -12
- package/dist/esm/decorator/cooldown.decorator.js +64 -0
- package/dist/esm/decorator/defer.decorator.js +50 -0
- package/dist/esm/decorator/event.decorator.js +71 -0
- package/dist/esm/decorator/filter.decorator.js +76 -0
- package/dist/esm/decorator/guard.decorator.js +86 -46
- package/dist/esm/decorator/index.js +6 -0
- package/dist/esm/decorator/interceptor.decorator.js +81 -0
- package/dist/esm/decorator/service.decorator.js +2 -5
- package/dist/esm/decorator/validation.decorator.js +101 -0
- package/dist/esm/enum/metadata-key.enum.js +2 -1
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/catalog.js +68 -0
- package/dist/esm/testing/execution-context.js +30 -0
- package/dist/esm/testing/index.js +5 -0
- package/dist/esm/testing/inspect-handler.js +55 -0
- package/dist/esm/testing/meocord-testing-module.js +226 -15
- package/dist/esm/testing/mock-interaction.js +20 -5
- package/dist/esm/testing/modal-fields.js +38 -0
- package/dist/esm/testing/response.js +66 -0
- package/dist/esm/util/bundle-entry.util.js +14 -0
- package/dist/esm/util/common.util.js +34 -5
- package/dist/esm/util/generator-cli.util.js +5 -0
- package/dist/esm/util/injectable.util.js +20 -0
- package/dist/esm/util/json.util.js +42 -7
- package/dist/esm/util/meocord-cli.util.js +1 -3
- package/dist/esm/util/meocord-config-validation.util.js +79 -0
- package/dist/esm/util/meocord-source-config.util.js +25 -11
- package/dist/esm/util/registration-mode.util.js +11 -0
- package/dist/esm/util/runtime.util.js +3 -1
- package/dist/esm/util/sharding-mode.util.js +53 -0
- package/dist/esm/util/stop-request.util.js +17 -0
- package/dist/esm/util/tsconfig.util.js +45 -31
- package/dist/types/common/index.d.cts +37 -2
- package/dist/types/common/index.d.ts +37 -2
- package/dist/types/cooldown-store-D1_CYKwv.d.cts +66 -0
- package/dist/types/cooldown-store-D1_CYKwv.d.ts +66 -0
- package/dist/types/core/index.d.cts +215 -88
- package/dist/types/core/index.d.ts +215 -88
- package/dist/types/decorator/index.d.cts +408 -19
- package/dist/types/decorator/index.d.ts +408 -19
- package/dist/types/enum/index.d.cts +2 -1
- package/dist/types/enum/index.d.ts +2 -1
- package/dist/types/errors-DwvuoFVM.d.cts +472 -0
- package/dist/types/errors-DwvuoFVM.d.ts +472 -0
- package/dist/types/interface/index.d.cts +414 -4
- package/dist/types/interface/index.d.ts +414 -4
- package/dist/types/testing/index.d.cts +341 -6
- package/dist/types/testing/index.d.ts +341 -6
- package/dist/types/translator-DeDoBQH7.d.cts +189 -0
- package/dist/types/translator-DeDoBQH7.d.ts +189 -0
- package/meocord.eslint.cjs +1 -1
- package/meocord.eslint.mjs +1 -1
- package/package.json +18 -7
- package/dist/cjs/_shared/component-routes-RTZZ3t5i.cjs +0 -55
- package/dist/cjs/_shared/controller.decorator-DnKZ29W4.cjs +0 -453
- package/dist/cjs/_shared/theme-C8w8ivsR.cjs +0 -116
- package/dist/esm/bin/app-template/src/guards/rate-limit.guard.spec.ts.template +0 -13
- package/dist/esm/bin/app-template/src/guards/rate-limit.guard.ts.template +0 -52
- package/dist/esm/util/embed.util.js +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,214 @@
|
|
|
1
1
|
# meocord
|
|
2
2
|
|
|
3
|
+
## 4.1.0-beta.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#66](https://github.com/meocord/meocord/pull/66) [`5b49d3e`](https://github.com/meocord/meocord/commit/5b49d3e2e827d4a3db88e3e554693957f1082ade) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Choose where commands are registered, and register without starting the bot.
|
|
8
|
+
|
|
9
|
+
- `commands` in `meocord.config.ts`: `guilds` registers to guilds instead of globally, `developmentGuild` sends everything to one test guild under `start --dev`, `register: false` leaves registration to `meocord register`, and `clearOther` removes commands left in a scope no longer used, which are otherwise reported as a warning; a development run sending to `developmentGuild` only warns, since production may share the application.
|
|
10
|
+
- `@CommandBuilder(type, { guilds })` keeps one command in its own guilds, for staff commands.
|
|
11
|
+
- `meocord register [--build] [--dev] [--guild <id>]` registers over REST and exits, without logging in, and exits non-zero on failure.
|
|
12
|
+
- In development, a scope whose commands are unchanged since the last start is not sent again; `meocord start --dev --force-register` sends it anyway.
|
|
13
|
+
- Commands are read from the controllers' prototypes, so registering constructs no controller.
|
|
14
|
+
|
|
15
|
+
With no `commands` setting, an existing bot still registers every command globally at each start. One thing does change: a builder whose `toJSON()` throws, such as a slash command missing its description, now stops that start's registration. You see an error naming the builder, and no commands are sent, where before the rest were registered and the broken one was dropped. A bulk update without it would delete it from Discord. Fix the builder and the next start registers everything.
|
|
16
|
+
|
|
17
|
+
New applications get `developmentGuild` wired to `DEV_GUILD_ID` in `.env`.
|
|
18
|
+
|
|
19
|
+
- [#76](https://github.com/meocord/meocord/pull/76) [`6bfe0e3`](https://github.com/meocord/meocord/commit/6bfe0e3660664d834c23404c0dcac0477a469edb) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Limit how often a handler runs with `@Cooldown`.
|
|
20
|
+
|
|
21
|
+
- `@Cooldown({ seconds, uses, per, bypass })` on an interaction or message handler, or a controller, allows `uses` calls within `seconds` (a sliding window), counted per `'user'`, `'guild'`, `'channel'` or `'global'`. Stack several for layered limits: each call counts against every one of them in order, so a call a later limit blocks has still used the earlier ones; put the shortest window first. `bypass` exempts callers such as owners. Counts are kept under the class name, so two same-named classes are refused at startup when either has a cooldown or a `@Once` handler.
|
|
22
|
+
- It is counted after guards, validation and pipes, so a denied call or bad input spends nothing. A blocked call throws `CooldownError` (from `meocord/common`), which the built-in fallback answers only to the caller with `cooldownMessage()`: "Slow down: try again in 12s."
|
|
23
|
+
- Calls are counted in memory by default. `@MeoCord({ cooldownStore })` takes a class extending `CooldownStore`, such as one on Redis, to share the count across shards and processes; with process sharding and the in-memory store, the bot warns that `'user'` and `'global'` cooldowns count per shard.
|
|
24
|
+
- `inspectHandler(...).cooldowns` lists a handler's cooldowns.
|
|
25
|
+
|
|
26
|
+
- [#81](https://github.com/meocord/meocord/pull/81) [`017ec6c`](https://github.com/meocord/meocord/commit/017ec6cae7ac0510260e441bbef51bfe379f1ea9) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add `@Defer()`, which acknowledges an interaction for its handler in two steps: a deferred reply (or an invisible deferred update, for a component) before guards run, so slow guards and handlers never miss Discord's three seconds; then, once guards, validation and pipes allow the call, a lock on the component's message — its controls disabled, the clicked button showing the loading emoji, the presenter's loading view added. `respond(interaction).send()` without `components` puts the message back as it was, including buttons that were disabled on purpose, and a handler that never answers has it put back when it returns. A guard that returns `false` leaves nothing behind, and one that throws `GuardDeniedError` is answered privately.
|
|
27
|
+
|
|
28
|
+
Options: `ephemeral`, `disable` (`'all'`, `'clicked'` or `'none'`), `mode: 'auto'` to acknowledge only when the handler has not answered after `after` milliseconds (1500 by default, and never later than 2.5 seconds after the interaction was created), and `suppressNotifications`. `@Defer` on a message, reaction, event or autocomplete handler throws.
|
|
29
|
+
|
|
30
|
+
- [#69](https://github.com/meocord/meocord/pull/69) [`f00309f`](https://github.com/meocord/meocord/commit/f00309f6df7e0e42cd3a04c6ba8e7119889abfa2) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add exception filters, which decide what happens when a handler, its interceptors or its guards throw.
|
|
31
|
+
|
|
32
|
+
- `@Catch(ErrorType, ...)` marks a class implementing `ExceptionFilter`: `catch(error, context)` receives the error and the call's `ExecutionContext`. With no types, it handles every error.
|
|
33
|
+
- `@UseFilter(...)` applies filters to a method or a controller, and `@MeoCord({ filters })` to every handler. The method's filters are tried first, then the controller's, then global ones; within one level, the first whose `@Catch` matches. `{ provide, params }` passes options, read with `context.getParams()`. One instance serves every call.
|
|
34
|
+
- An interaction no handler matches raises `CommandNotFoundError`, which global filters receive with no handler in the context.
|
|
35
|
+
- `GuardDeniedError`, thrown from a guard, denies with a message the built-in fallback shows only to the user who made the call. Returning `false` still denies silently.
|
|
36
|
+
- An error no filter handles goes to the built-in fallback, which logs it and answers the user: "An error occurred while executing the command.", or "Command not found!".
|
|
37
|
+
- Testing: `overrideFilter(Class).useValue(stub)`, `inspectHandler(...).filters`, and `MeoCordTestingModule.create({ app })` includes global filters. `invoke` resolves with `error` set when a filter handled one, and rejects with an error no filter handles, since the fallback does not run in tests.
|
|
38
|
+
- `meocord generate filter <name>` (alias `f`) writes a filter, the error it handles, and a spec.
|
|
39
|
+
|
|
40
|
+
- [#60](https://github.com/meocord/meocord/pull/60) [`f3ea0df`](https://github.com/meocord/meocord/commit/f3ea0df560d0deca8f6fc3b73b64b4abc56ca59d) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add typed handler metadata and `ExecutionContext`, so guards can read what they guard.
|
|
41
|
+
|
|
42
|
+
- `createMetadata<T>(description)` in `meocord/common` makes a typed decorator for a controller or a handler, with a unique key.
|
|
43
|
+
- `ExecutionContext` in `meocord/common` describes the handler call being run. A guard receives it by constructor injection and reads metadata with `context.get(Roles)`, where the handler's value wins over the controller's. It also gives the handler's arguments, the controller and method, the call type, and the guard's `{ provide, params }` through `getParams()`. `SetMetadata` keys are read with `context.get('roles')`.
|
|
44
|
+
- `createExecutionContext(Controller, 'method', { args, params })` in `meocord/testing` builds that context for a guard's unit test.
|
|
45
|
+
- `meocord generate guard` now generates a guard that reads a `createMetadata` decorator through `ExecutionContext`, with a spec using `createExecutionContext`.
|
|
46
|
+
|
|
47
|
+
Existing guards and tests work unchanged: guards run in the same order, once each, whether a handler is dispatched or called directly in a test. A controller or service, which is shared across calls, cannot inject `ExecutionContext`; the bot and `MeoCordTestingModule` refuse to start with a clear error rather than handing one call's context to another.
|
|
48
|
+
|
|
49
|
+
- [#110](https://github.com/meocord/meocord/pull/110) [`5d45fc4`](https://github.com/meocord/meocord/commit/5d45fc4380baf8410beeaf62094f9090b4127507) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `isExplainedError(error)` in `meocord/common` tells whether MeoCord has already logged what went wrong with an error `app.start()` rejects with, and what to do about it, such as a privileged intent Discord refused. New apps' `main.ts` uses it to skip logging such an error a second time with its stack trace; an existing app can do the same:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
bootstrap().catch(error => {
|
|
53
|
+
if (!isExplainedError(error)) logger.error('Error during startup:', error)
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- [#68](https://github.com/meocord/meocord/pull/68) [`1eb5f7f`](https://github.com/meocord/meocord/commit/1eb5f7f067018b659f1dec0e3e69f8fd3632f1ad) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add gateway event handlers and handler discovery.
|
|
58
|
+
|
|
59
|
+
- `@On(event)` and `@Once(event)` from `meocord/decorator` handle any discord.js client event on a controller or service, with the handler's parameters typed from `ClientEvents`. Event handlers run through the same pipeline as commands, so guards, interceptors and exception filters apply; an error no filter handles is logged with the event and handler, without stopping the bot. At startup MeoCord warns about intents and partials the handlers need that `clientOptions` lacks, for `@MessageHandler` and `@ReactionHandler` too.
|
|
60
|
+
- `HandlerRegistry` from `meocord/core` lists every registered handler — commands (one entry per subcommand path), components, modals, autocomplete, message, reaction and event handlers — with the metadata declared on it. Inject it into a service to build a `/help` command or generated docs.
|
|
61
|
+
- `TestingModule.emit(event, ...args)` sends an event to a testing module's handlers through the same pipeline, and `MeoCordTestingModule` binds `HandlerRegistry`.
|
|
62
|
+
- A guard's `canActivate` now also receives an event handler's arguments, so its first parameter accepts any value, and `@UseGuard` no longer throws when a method's first argument is not an interaction, message or reaction.
|
|
63
|
+
- Global guards and interceptors from `@MeoCord({ guards, interceptors })` also run on event handlers. `@Guard({ types })` and `@Interceptor({ types })` limit a guard or interceptor to the context types it is written for, at every level, and a subclass inherits them unless it declares its own. An empty list, or `'autocomplete'` for an interceptor, which never runs there, throws when the class is decorated. At startup MeoCord names each global one without `types` that will also run on events. With no stage that applies to a call, no execution context is built.
|
|
64
|
+
|
|
65
|
+
- [#65](https://github.com/meocord/meocord/pull/65) [`939e206`](https://github.com/meocord/meocord/commit/939e206660d9ad902a7571faeb145b80044e96b8) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add global guards: `@MeoCord({ guards })` runs guards before every dispatched handler — commands, components, modals, autocomplete, message and reaction handlers — ahead of the controller's and the method's own guards. Entries take the same forms as `@UseGuard`, a guard class or `{ provide, params }`, and guards that inject `ExecutionContext` receive it. A controller method called directly still runs only its own guards.
|
|
66
|
+
|
|
67
|
+
For tests, `MeoCordTestingModule.create({ app: App, ... })` reads the application's global guards, so `module.invoke` runs them first, and `inspectHandler(Controller, 'method', { app: App })` lists them first. Controllers and providers are still listed as before.
|
|
68
|
+
|
|
69
|
+
- [#78](https://github.com/meocord/meocord/pull/78) [`99a8bd4`](https://github.com/meocord/meocord/commit/99a8bd4f953e7dc54dd4d385afe66db8085dfb79) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add `respond(interaction)`, one place to answer an interaction, and presenters to style MeoCord's answers.
|
|
70
|
+
|
|
71
|
+
- `respond(interaction)` in `meocord/common` returns the interaction's response state, typed as the `ResponseState` interface: `acknowledge()`, `send()`, `edit()`, `followUp()`, `delete()`, `modal()` and `error()`. Each picks the Discord call from where the answer stands — reply, update or edit — re-read from the interaction on every call, so answers made directly with discord.js still count. Flags are computed per call, so an ephemeral follow-up never leaks into the next message, and one sent while a public deferred reply is still empty stays private instead of becoming that reply; Components V2 edits keep their flag; re-sent Discord attachment images are pointed at `attachment://`.
|
|
72
|
+
- Answers go through the interaction's own methods, which work wherever a user-installed app is used. The channel is used only once the interaction's token has expired and the bot is present. `getInstallContext(interaction)` in `meocord/common` reports where an interaction happened and whether the bot is there.
|
|
73
|
+
- `error(error, { message, visibility })` shows an error and never throws. The built-in fallback now answers through it, so an error on a private (ephemeral) component message is added to that message rather than sent separately.
|
|
74
|
+
- `@MeoCord({ presenter })` takes a `ResponsePresenter` that styles the error and loading views. Without one, errors look as before, and the loading view is "⏳ Working on it…" in the new `Theme.primaryColor`.
|
|
75
|
+
- Interceptors and filters reach the state as `context.response`.
|
|
76
|
+
- Testing: `getResponse(interaction)` reports what `respond()` sent; `createDiscordError(code)` builds the error discord.js throws; mock messages carry real, empty `flags`, `components`, `embeds` and `attachments`; mock `showModal()` and a modal submission's `deferUpdate()` answer the interaction as the real ones do.
|
|
77
|
+
|
|
78
|
+
- [#67](https://github.com/meocord/meocord/pull/67) [`6c47f09`](https://github.com/meocord/meocord/commit/6c47f0924c278277985c045bdca9e301bc7f5420) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add interceptors, which run around a handler once its guards allow the call — for timing, logging, caching or mapping errors.
|
|
79
|
+
|
|
80
|
+
- `@Interceptor()` marks a class implementing `InterceptorInterface`: `intercept(context, next)` receives the call's `ExecutionContext` and continues with `next.handle()`, which resolves to what the handler returns. An interceptor can act before and after the handler, skip it, or replace the error it throws.
|
|
81
|
+
- `@UseInterceptor(...)` applies interceptors to a method or a controller, including inherited handlers, and `@MeoCord({ interceptors })` to every handler. Global interceptors are outermost, then the controller's, then the method's. `{ provide, params }` passes options, read with `context.getParams()`.
|
|
82
|
+
- One instance serves every call. An interceptor that injects `ExecutionContext` is refused at startup.
|
|
83
|
+
- Interceptors run for dispatched handlers and under `TestingModule.invoke`; a controller method called directly runs its guards but no interceptors, and autocomplete handlers run none.
|
|
84
|
+
- Testing: `overrideInterceptor(Class).useValue(stub)`, `inspectHandler(...).interceptors`, and `MeoCordTestingModule.create({ app })` includes global interceptors. `invoke` resolves `{ ran: false }` when an interceptor skips the handler.
|
|
85
|
+
- `meocord generate interceptor <name>` (alias `i`) writes an interceptor and its spec.
|
|
86
|
+
|
|
87
|
+
- [#64](https://github.com/meocord/meocord/pull/64) [`37170f3`](https://github.com/meocord/meocord/commit/37170f3abea1c148acfe0891828abb8743f68c0d) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add lifecycle hooks. A controller or service that implements `OnReady` from `meocord/interface` has `onReady(client, { primary })` called once the bot is ready; one that implements `OnShutdown` has `onShutdown()` called on SIGINT or SIGTERM, before the client is destroyed. Hooks run on every controller and service the app binds, including services no handler has used yet. `onReady` hooks run one at a time in dependency order, each class after the classes it injects, and never wait for command registration; `onShutdown` hooks run in reverse order. A hook that throws is logged and the next one still runs. A signal that arrives while `onReady` hooks are running starts no further `onReady` and shuts down only the classes whose `onReady` finished, and those without one. Shutdown waits for the `onShutdown` hooks up to the new `shutdownTimeout` option in `meocord.config.ts` (10 seconds by default), then destroys the client and exits 0.
|
|
88
|
+
|
|
89
|
+
A process now adds one SIGINT and one SIGTERM listener however many apps it starts, so a test suite that creates many apps no longer triggers Node's `MaxListenersExceededWarning`.
|
|
90
|
+
|
|
91
|
+
- [#73](https://github.com/meocord/meocord/pull/73) [`42b62a1`](https://github.com/meocord/meocord/commit/42b62a19947daccef86930bf769880025a626b29) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Translate commands and replies from typed catalogs.
|
|
92
|
+
|
|
93
|
+
- `createTranslator({ default, locales })` in `meocord/common` builds a translator from one catalog per discord.js `Locale`. Keys, `{name}` params and plural forms (`{ one, other, … }`, chosen through `Intl.PluralRules`) are type-checked against the default catalog, which `defineCatalog(...)` or `as const` keeps literal; other locales may leave messages out, and fall back to a locale of the same language, then the default.
|
|
94
|
+
- `t.default(key)` and `t.localizations(key)` fill command builders; `t.for(interaction)`, `t.for(interaction, { public: true })`, `t.forGuild(guild)` and `t.locale(locale)` translate replies.
|
|
95
|
+
- `@MeoCord({ i18n: t })` injects it as `Translator`.
|
|
96
|
+
- `expectCompleteCatalog` in `meocord/testing` reports missing messages and plural forms per locale.
|
|
97
|
+
- Registration refuses localised names and descriptions Discord would reject, listing each field, and a builder that throws while building now names itself and the command.
|
|
98
|
+
|
|
99
|
+
Nothing changes for a bot that does not use it.
|
|
100
|
+
|
|
101
|
+
- [#84](https://github.com/meocord/meocord/pull/84) [`b21b0ed`](https://github.com/meocord/meocord/commit/b21b0ed11c102ac5cc01b86aa90fbb4f035c1323) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `createMockInteraction` accepts `authorizingIntegrationOwners` as the plain map Discord sends — `{ [ApplicationIntegrationType.UserInstall]: userId }` — and builds the `AuthorizingIntegrationOwners` object discord.js would, so testing a user-installed command no longer needs `as never`.
|
|
102
|
+
|
|
103
|
+
- [#75](https://github.com/meocord/meocord/pull/75) [`b11d77b`](https://github.com/meocord/meocord/commit/b11d77b0e92fe819725fb387d3f27d35581f11cc) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add `optionalExternals` to `meocord.config.ts`, for packages a dependency tries to load and runs without, such as `supports-color`, which `debug` probes for inside a `try` and which axios brings in. With `bundleDependencies` on, such a package made every build warn, and listing it in `externals` made the bot fail at startup when it was missing, because an external becomes an import that runs before the bot's code. A name listed in `optionalExternals` stays a `require` where the dependency calls it, so a missing package is caught by the dependency, and it is copied into `dist/node_modules` when it is installed. The build warns when a name is also in `externals`. discord.js's optional accelerators, `zlib-sync`, `bufferutil` and `utf-8-validate`, are handled the same way, as before.
|
|
104
|
+
|
|
105
|
+
- [#72](https://github.com/meocord/meocord/pull/72) [`d9ad59b`](https://github.com/meocord/meocord/commit/d9ad59b3972d7671a58b008efc67f666d57f7a79) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add sharding, configured by `sharding` in `meocord.config.ts`.
|
|
106
|
+
|
|
107
|
+
- `sharding: { shards: 'auto' }` (or a number) runs every shard in one process, in one client, with nothing else changing. Unset, `clientOptions.shards` works as before.
|
|
108
|
+
- `mode: 'process'` runs each shard in its own process. `meocord start`, `node dist/main.js`, bun and process managers such as pm2 all start a manager that registers the commands once, spawns the shards from the built bundle, restarts a shard that exits with a growing delay, stops everything with exit code 1 when a shard's token is invalid or Discord refuses its intents, and on SIGINT or SIGTERM shuts every shard down through its `onShutdown` hooks before killing any left after `shutdownTimeout` plus five seconds. Under `meocord start --dev` every shard runs in one process unless `sharding.development` is `true`.
|
|
109
|
+
- `ShardContext` from `meocord/core` gives the shards of the current process and calls a service method in every shard with `call(Service, 'method', ...args)`, one result per process. Each process runs the class passed in, or, for a call from another process, the class of that name, so with process sharding the bot refuses to start when two controllers or services share a name. `onReady`'s `primary` is `true` only in the process running shard 0.
|
|
110
|
+
- `MeoCordFactory.create()` now returns the new `MeoCordApplication` type, with the same `start()` and `registerCommands()` as before.
|
|
111
|
+
- The generated `meocord.config.ts` shows the `sharding` option, commented out.
|
|
112
|
+
|
|
113
|
+
- [#63](https://github.com/meocord/meocord/pull/63) [`9e27912`](https://github.com/meocord/meocord/commit/9e27912fc8f026fc3f532eddefcc8fd144712610) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add `TestingModule.invoke` and `inspectHandler` to `meocord/testing`, for testing a handler the way the bot runs it.
|
|
114
|
+
|
|
115
|
+
- `module.invoke(Controller, 'method', ...args)` runs the handler through the same pipeline dispatch uses, starting with its guards, class guards first and each once. Guards resolve from the testing module, so `overrideGuard` stubs apply and guards that inject `ExecutionContext` receive it. It resolves to `{ ran }`, `false` when a guard denied the call, and the method name and arguments are type-checked against the handler. An interaction dispatch could not route to the handler, such as a customId its pattern does not match, is rejected before anything runs.
|
|
116
|
+
- `inspectHandler(Controller, 'method')` reports the guards that run for a handler, in order, and reads its metadata as `ExecutionContext` does, without building a module.
|
|
117
|
+
|
|
118
|
+
Calling a controller method directly in a test still runs its guards, as before.
|
|
119
|
+
|
|
120
|
+
- [#70](https://github.com/meocord/meocord/pull/70) [`ade2be6`](https://github.com/meocord/meocord/commit/ade2be6a1dcb953f89d7f3954833540485bd2192) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Validate a handler's input, and transform it with pipes.
|
|
121
|
+
|
|
122
|
+
- `@Validate(schema)` checks an interaction handler's input with any [Standard Schema](https://standardschema.dev) library (zod, valibot, arktype and others) before it runs; a handler takes one, and a second throws when decorated. The handler receives the schema's output, and its second parameter is type-checked against it. Invalid input throws a `ValidationError` (from `meocord/common`) listing each issue, which the built-in fallback answers privately with that list; an exception filter can phrase it otherwise.
|
|
123
|
+
- Pipes turn one validated value into what the handler works with: `@Validate(schema, { pipes: { uid: AccountPipe } })` keeps the handler fully typed, and `@UsePipe(key, ...pipes)` works on its own or beside `@Validate`, where the value it produces is marked `Piped<T>` (from `meocord/interface`). Mark a class `@Pipe()` and implement `PipeInterface`.
|
|
124
|
+
- `meocord generate pipe <name>` (alias `pi`) writes a pipe and its spec.
|
|
125
|
+
- A modal handler's second argument now also carries the submitted fields, keyed by customId, next to the customId params; a param wins over a field of the same name, with a warning in development.
|
|
126
|
+
- `TestingModule.invoke` builds the params from the interaction when a test passes none, and `createModalFields` gives a mock modal its fields.
|
|
127
|
+
|
|
128
|
+
Existing handlers keep working: modal handlers receive extra keys, and nothing is validated until you add `@Validate`.
|
|
129
|
+
|
|
130
|
+
### Patch Changes
|
|
131
|
+
|
|
132
|
+
- [#65](https://github.com/meocord/meocord/pull/65) [`5df8fa3`](https://github.com/meocord/meocord/commit/5df8fa3d019af0eb2514602cd26c3fec5aa378cd) Thanks [@l7aromeo](https://github.com/l7aromeo)! - A class-level `@UseGuard` now also guards the controller's `@Autocomplete` handlers, including inherited ones, as it does commands, components, message and reaction handlers. Global guards from `@MeoCord({ guards })` run there too. A guard sees an `AutocompleteInteraction` and `ExecutionContext.getType() === 'autocomplete'`, and must not reply; when a guard denies, the menu is closed with an empty list instead of being left loading.
|
|
133
|
+
|
|
134
|
+
This changes which guards run for autocomplete. If a class guard assumes a command interaction or replies on denial, see [Class guards now cover autocomplete handlers](https://github.com/meocord/meocord/blob/main/docs/MIGRATING.md#class-guards-now-cover-autocomplete-handlers).
|
|
135
|
+
|
|
136
|
+
- [#80](https://github.com/meocord/meocord/pull/80) [`cc2fb29`](https://github.com/meocord/meocord/commit/cc2fb2923cd46cb9f44c7ebdc108bfa66d357716) Thanks [@l7aromeo](https://github.com/l7aromeo)! - The CLI stops sooner and says what to do when something is wrong.
|
|
137
|
+
|
|
138
|
+
- `build`, `start` and `register` check `meocord.config.ts` first. One that fails to load stops them, naming the file and line, instead of building on. Options of the wrong type, such as `sharding.mode: 'bogus'` or `optionalExternals: 'sharp'`, stop them with a list of every problem, where before some built silently and others failed with an internal error. An option MeoCord does not know is reported as a warning. `start --prod` without `--build` checks the built config the same way, and says when there is no config at all.
|
|
139
|
+
- The compiled config is written only once it has built, so a failed build no longer leaves a broken `dist/meocord.config.mjs` for every later command to trip over.
|
|
140
|
+
- `meocord generate` refuses names that leave its folder (`..`, a leading `/`, a drive letter), which could write outside `src/` or the project, and asks to be run from a project's root. On Windows, `\` separates folders in a name.
|
|
141
|
+
- `meocord create` refuses a name with no letters or digits, which was reported as `Directory "" already exists`.
|
|
142
|
+
- A missing token is reported with where it comes from: `discordToken` in `meocord.config.ts`, which a new app reads from `DISCORD_TOKEN` in `.env`.
|
|
143
|
+
- `start --dev --build` builds once, as the watcher does, instead of twice.
|
|
144
|
+
|
|
145
|
+
- [#95](https://github.com/meocord/meocord/pull/95) [`86301f7`](https://github.com/meocord/meocord/commit/86301f7bce5ec2529e7271bb8e808c88fee29485) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `meocord start --dev` restarts the bot on the next rebuild after it exited on its own, such as after an error at startup, and Ctrl+C then stops the watcher at once instead of waiting for a second Ctrl+C. The bot started through `npm run` from a script bun runs is launched on node again, rather than handed to npm.
|
|
146
|
+
|
|
147
|
+
- [#105](https://github.com/meocord/meocord/pull/105) [`8dc19f4`](https://github.com/meocord/meocord/commit/8dc19f49fea3c896de35918c7be5a8ac09f2ac6f) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `meocord --help` and each command's help no longer print "No available choices." for arguments that have no choices, and `meocord create --help` describes its `<app-name>` argument instead of saying "No description provided".
|
|
148
|
+
|
|
149
|
+
- [#103](https://github.com/meocord/meocord/pull/103) [`35d2276`](https://github.com/meocord/meocord/commit/35d22764a5b011c29f1e0fa5b927cf674d23f6c6) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `meocord start` and `meocord register` pass SIGINT and SIGTERM on to the bot. A signal sent to the CLI alone, as Docker, pm2 and systemd send one, used to stop the CLI and leave the bot running, or, with SIGINT, not stop it at all; the bot now shuts down through its own shutdown path and the CLI exits with its code. One Ctrl+C that reaches a process twice within a second counts once, so it no longer force-kills the shards in process sharding; a second signal after that still stops everything at once.
|
|
150
|
+
|
|
151
|
+
- [#93](https://github.com/meocord/meocord/pull/93) [`18db29e`](https://github.com/meocord/meocord/commit/18db29ece8640bff65daf7a58d288d76f03ce9ff) Thanks [@l7aromeo](https://github.com/l7aromeo)! - The warning about two component `customId` patterns that can match the same id, such as `a/{x}/c` and `a/b/{y}`, is logged when the bot starts, as the README describes, rather than at the first button, select menu or modal interaction. The routes are built once at startup and reused by every interaction.
|
|
152
|
+
|
|
153
|
+
- [#69](https://github.com/meocord/meocord/pull/69) [`f00309f`](https://github.com/meocord/meocord/commit/f00309f6df7e0e42cd3a04c6ba8e7119889abfa2) Thanks [@l7aromeo](https://github.com/l7aromeo)! - A command that throws after deferring its reply is now answered instead of left showing "thinking…" until it times out: the deferred reply is edited into the error message. A command or component that throws after it already replied now gets a private follow-up with the error, where it used to get nothing. Buttons, select menus and modals submitted from a public message are answered with a private follow-up, never by editing the message the user clicked; on a private (ephemeral) message, the error is added to that message. Unanswered interactions are answered exactly as before.
|
|
154
|
+
|
|
155
|
+
If you want a different answer in these cases — a different text, no answer, or a log to an error service — register an exception filter with `@Catch()` in `@MeoCord({ filters })`: filters run before this built-in answer and replace it.
|
|
156
|
+
|
|
157
|
+
- [#56](https://github.com/meocord/meocord/pull/56) [`c2c09d7`](https://github.com/meocord/meocord/commit/c2c09d7b703408b2813f6088c2f09e9dc4bad20c) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Update `@rsbuild/core` to 2.2.9, and generate new applications with `prettier` 3.9.9.
|
|
158
|
+
|
|
159
|
+
- [#59](https://github.com/meocord/meocord/pull/59) [`7cfea8d`](https://github.com/meocord/meocord/commit/7cfea8d383680e30e1d80d62c6a14e337563c897) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `meocord/eslint` ignores `coverage/`. Flat config does not read `.gitignore`, so running `eslint` after `test:coverage` in a generated application linted the istanbul report and failed with three warnings about unused `eslint-disable` directives. Nothing to do after upgrading.
|
|
160
|
+
|
|
161
|
+
- [#110](https://github.com/meocord/meocord/pull/110) [`213bd8b`](https://github.com/meocord/meocord/commit/213bd8bdc555777d840b20ec74fb603dfe05eca4) Thanks [@l7aromeo](https://github.com/l7aromeo)! - When Discord refuses a privileged intent at login, the bot now says which privileged intents it requests (`GuildMembers`, `GuildPresences`, `MessageContent`) and where to enable them — Developer Portal → your application → Bot → Privileged Gateway Intents — and that a verified bot in 100 or more servers needs Discord's approval for them. Before, it printed only "Used disallowed intents" and a stack trace. Intents Discord refuses as invalid are explained too. The stack trace moves to debug level; the exit code and the error `app.start()` rejects with are unchanged.
|
|
162
|
+
|
|
163
|
+
- [#59](https://github.com/meocord/meocord/pull/59) [`d4612b3`](https://github.com/meocord/meocord/commit/d4612b31817ca35292b2290804e60882ca10d312) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Generated controllers, context menu builders and services pass the application's lint as written. `meocord g` output carried a blank line at the start of each controller class, a split context menu builder chain and a semicolon in the service, which failed prettier whenever `eslint --fix` had not already rewritten the file. Files you generated earlier are unaffected; `eslint --fix` corrects them.
|
|
164
|
+
|
|
165
|
+
- [#58](https://github.com/meocord/meocord/pull/58) [`88cb6f0`](https://github.com/meocord/meocord/commit/88cb6f0916628f3a6915a44908c5fec299df3629) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Fix handlers of a controller that extends another controller being added to the parent class too. A subclass's `@Command`, `@MessageHandler`, `@ReactionHandler` and `@Autocomplete` handlers were written into the base class's metadata, so the base controller listed, and could be routed to, handlers it does not have. Each class now keeps its own copy, including the handlers it inherits.
|
|
166
|
+
|
|
167
|
+
Fix the guard list stored under `MetadataKey.Guards` when `@UseGuard` is used on both a class and its methods. The class-level list replaced the method's own guards; it now holds every guard that runs, class-level guards first, in the order they run. Which guards run is unchanged.
|
|
168
|
+
|
|
169
|
+
- [#109](https://github.com/meocord/meocord/pull/109) [`2d4738a`](https://github.com/meocord/meocord/commit/2d4738ae09798732616ea7f356a148fe23dc7eda) Thanks [@l7aromeo](https://github.com/l7aromeo)! - A handler may take fewer parameters than dispatch passes, as any TypeScript callback can. `@Command`, `@Autocomplete`, `@MessageHandler` and `@ReactionHandler` on a method with no parameters, such as `async refresh() {}`, failed to compile with TS1241 ("Unable to resolve signature of method decorator"), and `@Validate` or `@UsePipe` on a handler that ignores its input was refused as a mismatch. Both now compile. A parameter of the wrong type is still refused.
|
|
170
|
+
|
|
171
|
+
- [#65](https://github.com/meocord/meocord/pull/65) [`53eac95`](https://github.com/meocord/meocord/commit/53eac95ee9f637cb644f950527eedc17ef1e0fe5) Thanks [@l7aromeo](https://github.com/l7aromeo)! - A class-level `@UseGuard` now also guards the handlers a controller inherits. On a controller that extends another, the subclass's guards were applied only to the handlers it declared itself, so inherited commands, components, message and reaction handlers ran without them. They now run the subclass's guards first, then the base class's, then the method's, whether dispatched, called directly or run with `TestingModule.invoke`.
|
|
172
|
+
|
|
173
|
+
This changes which guards run for inherited handlers. If your bot relied on an inherited handler skipping the subclass's guards, see [Class guards now cover inherited handlers](https://github.com/meocord/meocord/blob/main/docs/MIGRATING.md#class-guards-now-cover-inherited-handlers).
|
|
174
|
+
|
|
175
|
+
- [#64](https://github.com/meocord/meocord/pull/64) [`a42ca18`](https://github.com/meocord/meocord/commit/a42ca18332f843408dce6ac373b738f7e1af4961) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Fix `MeoCordFactory.create()` for a controller or service that injects a dependency with `@inject(Token)` on a parameter typed as an interface. The factory followed only the parameter's type, which for an interface is `Object`, so it bound `Object` instead of the token and resolving the class failed with "missing metadata on type Object". It now binds the `@inject` token, and skips built-in constructors such as `Object`.
|
|
176
|
+
|
|
177
|
+
- [#62](https://github.com/meocord/meocord/pull/62) [`04c7333`](https://github.com/meocord/meocord/commit/04c7333ccb608afa425db93a0bfb7b6beb58f217) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `process.env` values loaded by `meocord.config.ts` are set before the application's modules run. `main.ts` imports `App` before anything else, so an option such as `@MeoCord({ activities: [{ name: process.env.STATUS! }] })` read the environment before the config's `dotenv` import had loaded `.env`, and got `undefined`. The build now loads `dist/meocord.config.mjs` ahead of `main.ts`, for every way of starting the bundle. Rebuild to pick it up; no code changes. The README shows how to choose a `.env` file per environment.
|
|
178
|
+
|
|
179
|
+
- [#69](https://github.com/meocord/meocord/pull/69) [`baa94ed`](https://github.com/meocord/meocord/commit/baa94ed1819fbc7d5ea5a179af7220d614226766) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `createMockInteraction(ModalSubmitInteraction)` now runs the real `isFromMessage()`, so it returns `true` only when the mock has a `message`, as a real modal does. It returned `undefined`.
|
|
180
|
+
|
|
181
|
+
- [#113](https://github.com/meocord/meocord/pull/113) [`9b54a71`](https://github.com/meocord/meocord/commit/9b54a71a0ee0fe42181fc18ef03ff03b2ed1dd28) Thanks [@l7aromeo](https://github.com/l7aromeo)! - MeoCord's repository is now `meocord/meocord` on GitHub. The package's repository, homepage and issue links point there, and so does the README that `meocord create` writes for a new application. Links to the old `l7aromeo/meocord` address redirect, so nothing needs changing in your code or bookmarks.
|
|
182
|
+
|
|
183
|
+
- [#61](https://github.com/meocord/meocord/pull/61) [`1f09800`](https://github.com/meocord/meocord/commit/1f098002964d21e3d5c367188cc8403d8dcd8a43) Thanks [@l7aromeo](https://github.com/l7aromeo)! - The `RateLimitGuard` that 4.0 copied into generated applications never limited anything: a new guard instance is created for every call, so the counts it kept on the instance started empty each time. New applications no longer get it; they limit their sample commands with `@Cooldown`.
|
|
184
|
+
|
|
185
|
+
Upgrading `meocord` does not change the copy in your application. If your app still has `src/guards/rate-limit.guard.ts`, move its `rateLimits` map out of the class to module level, so every instance shares it, or replace the guard with `@Cooldown({ uses, seconds })`.
|
|
186
|
+
|
|
187
|
+
The README now explains that a guard instance is created for every call, and shows how to pass options to a guard with `@UseGuard({ provide, params })`.
|
|
188
|
+
|
|
189
|
+
- [#84](https://github.com/meocord/meocord/pull/84) [`ed07729`](https://github.com/meocord/meocord/commit/ed07729ef2ca4b9b26583c9630a32b601b2b79f6) Thanks [@l7aromeo](https://github.com/l7aromeo)! - New applications from `meocord create` start with the 4.1 patterns. Existing applications are not changed: upgrading `meocord` never touches your code.
|
|
190
|
+
|
|
191
|
+
- The sample controllers answer through `respond(interaction)`. The button, select menu and modal samples use `@Defer`, and the button sample's second handler is guarded by an `OwnerGuard` that denies with `GuardDeniedError`.
|
|
192
|
+
- The slash, button, modal and context menu samples limit how often they run with `@Cooldown({ uses: 5, seconds: 60 })`, and the `RateLimitGuard` and its spec are gone.
|
|
193
|
+
- `src/presenters/app.presenter.ts` styles the loading and error views, registered with `@MeoCord({ presenter })`.
|
|
194
|
+
- Each sample's spec runs its handler with `invoke` and checks what `respond()` sent.
|
|
195
|
+
|
|
196
|
+
- [#88](https://github.com/meocord/meocord/pull/88) [`53517cc`](https://github.com/meocord/meocord/commit/53517cce6384d6f718f4831ab67d2cb7f49f1f40) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `SetMetadata` refuses the keys MeoCord stores its own metadata under, such as `'guards'` and `'commandType'`, and throws when the decorator is created, naming the key. A value under `'guards'` replaced the guard list dispatch runs, so a handler decorated with `@SetMetadata('guards', …)` above its `@UseGuard` ran with none of its guards. Choose another key, or declare the decorator with `createMetadata`, whose key is unique; see [`SetMetadata` refuses MeoCord's own keys](https://github.com/meocord/meocord/blob/main/docs/MIGRATING.md#setmetadata-refuses-meocords-own-keys).
|
|
197
|
+
|
|
198
|
+
A guard class listed in `@MeoCord({ services })` is warned about at startup: one shared instance takes every call's `{ provide, params }`.
|
|
199
|
+
|
|
200
|
+
- [#107](https://github.com/meocord/meocord/pull/107) [`aca4436`](https://github.com/meocord/meocord/commit/aca4436aba0c4400ca9f95aa78f428999f6283bd) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `@UseGuard`, `@UseInterceptor`, `@UseFilter`, `@UsePipe`, `@Validate`'s pipes and `@MeoCord({ guards, interceptors, filters })` all take the same entries: a class, or `{ provide: Class, params? }`. `params` is now optional for guards, interceptors and filters too, as it already was for pipes, so `{ provide: ChannelGuard }` works as the class alone; before, a guard or interceptor given that way failed inside the container on its first call.
|
|
201
|
+
|
|
202
|
+
Anything else, such as `null`, a `provide` that is not a class, or `params` that are not an object, is refused when the decorator applies, with an error naming the decorator and the class or handler, instead of failing when a call first reaches it.
|
|
203
|
+
|
|
204
|
+
- [#65](https://github.com/meocord/meocord/pull/65) [`1c4ed6f`](https://github.com/meocord/meocord/commit/1c4ed6f2671f9932c8940f560d0a4f01c51ec8b8) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Fix dependency injection for a controller, service or guard that extends another decorated class. The subclass was treated as already set up because its base class was, so its own constructor's dependencies were never injected: a subclass with its own constructor failed to resolve, and one without a constructor was built with no arguments. A subclass now gets its own constructor's dependencies, or its base class's when it declares no constructor, and inherits its base class's injected properties. No change is needed in your code.
|
|
205
|
+
|
|
206
|
+
- [#92](https://github.com/meocord/meocord/pull/92) [`a531fcb`](https://github.com/meocord/meocord/commit/a531fcbc7bafb2f4a88d329ce5cec0efd358a722) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Fix building an application whose `tsconfig.json` uses `extends`, `files`, or `compilerOptions.typeRoots`. MeoCord builds from a copy of `tsconfig.json` in a temporary directory, and a relative `extends` or `files` entry in that copy pointed at files that are not there; a package in `extends`, such as `@tsconfig/node22/tsconfig.json`, could not be found from there either. The copy now carries absolute paths, with a package resolved from the project's `node_modules`. `typeRoots` is read from `compilerOptions`, where TypeScript declares it, and `include` and `exclude` are resolved even without `compilerOptions`.
|
|
207
|
+
|
|
208
|
+
- [#92](https://github.com/meocord/meocord/pull/92) [`16d9a59`](https://github.com/meocord/meocord/commit/16d9a59d47ca85b47a608498e8b01174342d7772) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `meocord build` no longer rewrites your `tsconfig.json`. When the file had comments or trailing commas, which TypeScript allows, the build "repaired" it and wrote the result back, deleting your comments, and the repair broke on a `//` inside a string such as `"$schema": "https://json.schemastore.org/tsconfig"`, failing the build. MeoCord now reads comments and trailing commas as TypeScript does, leaves strings alone, and only ever writes its own temporary copy. A `tsconfig.json` it cannot parse fails the build with the file and the fix named.
|
|
209
|
+
|
|
210
|
+
- [#74](https://github.com/meocord/meocord/pull/74) [`5f32513`](https://github.com/meocord/meocord/commit/5f32513e508e37679e3cb5d9762527f107ad3572) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Fix builds that run at the same time, such as CI jobs sharing a runner, failing with a JSON parse error in `modified-tsconfig.json`. Every build wrote its copy of the tsconfig to one fixed file in the system temp directory, so two builds could read each other's half-written file. Each build now writes to a directory of its own, removed when the build exits.
|
|
211
|
+
|
|
3
212
|
## 4.0.0
|
|
4
213
|
|
|
5
214
|
MeoCord 4 builds with Rsbuild instead of webpack, requires Node.js 22.13 and dotenv 18, and can
|