meocord 3.0.0 → 3.1.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 (34) hide show
  1. package/AUTHOR.md +2 -3
  2. package/README.md +224 -41
  3. package/dist/cjs/_shared/controller.decorator-MUHA_A3z.cjs +529 -0
  4. package/dist/cjs/core/index.cjs +246 -86
  5. package/dist/cjs/decorator/index.cjs +3 -1
  6. package/dist/cjs/enum/index.cjs +17 -4
  7. package/dist/cjs/testing/index.cjs +88 -4
  8. package/dist/esm/bin/builder-template/builder/primary-entry-point.builder.template +26 -0
  9. package/dist/esm/bin/builder-template/controller/autocomplete.controller.template +16 -0
  10. package/dist/esm/bin/builder-template/controller/channel-select-menu.controller.template +12 -0
  11. package/dist/esm/bin/builder-template/controller/context-menu.controller.template +2 -2
  12. package/dist/esm/bin/builder-template/controller/mentionable-select-menu.controller.template +12 -0
  13. package/dist/esm/bin/builder-template/controller/modal-submit.controller.template +1 -1
  14. package/dist/esm/bin/builder-template/controller/primary-entry-point.controller.template +12 -0
  15. package/dist/esm/bin/builder-template/controller/role-select-menu.controller.template +12 -0
  16. package/dist/esm/bin/builder-template/controller/slash.controller.template +1 -1
  17. package/dist/esm/bin/builder-template/controller/user-select-menu.controller.template +12 -0
  18. package/dist/esm/bin/generator.js +4 -9
  19. package/dist/esm/bin/helper/controller-generator.helper.js +24 -7
  20. package/dist/esm/core/meocord.app.js +247 -86
  21. package/dist/esm/decorator/controller.decorator.js +73 -10
  22. package/dist/esm/decorator/index.js +1 -1
  23. package/dist/esm/enum/controller.enum.js +23 -4
  24. package/dist/esm/testing/mock-interaction.js +89 -5
  25. package/dist/esm/util/interaction.util.js +174 -0
  26. package/dist/types/controller.enum-DYfhYaat.d.ts +36 -0
  27. package/dist/types/core/index.d.ts +73 -0
  28. package/dist/types/decorator/index.d.ts +60 -48
  29. package/dist/types/enum/index.d.ts +1 -1
  30. package/dist/types/interface/index.d.ts +87 -3
  31. package/dist/types/testing/index.d.ts +3 -21
  32. package/package.json +2 -2
  33. package/dist/cjs/_shared/controller.decorator-CC6BjHkS.cjs +0 -288
  34. package/dist/types/controller.enum-QA-IuReF.d.ts +0 -18
package/AUTHOR.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Authors
2
2
 
3
3
  - **Ukasyah Rahmatullah Zada**
4
- - Primary creator and maintainer of the MeoCord Framework.
5
- - Contact: [ukasyahrz@outlook.com](mailto:ukasyahrz@outlook.com)
4
+ - Primary creator and maintainer of the MeoCord Framework.
5
+ - Contact: [ukasyahrz@outlook.com](mailto:ukasyahrz@outlook.com)
6
6
 
7
7
  ---
8
8
 
@@ -10,4 +10,3 @@
10
10
 
11
11
  This project is currently maintained by the original author.
12
12
  Contributions, issues, and feature suggestions are always welcome!
13
-
package/README.md CHANGED
@@ -16,7 +16,10 @@
16
16
  - [meocord.config.ts](#meocordconfigts)
17
17
  - [ESLint](#eslint)
18
18
  - [CLI Reference](#cli-reference)
19
+ - [Command Types](#command-types)
19
20
  - [Command Parameters](#command-parameters)
21
+ - [Subcommands](#subcommands)
22
+ - [Autocomplete](#autocomplete)
20
23
  - [Guards](#guards)
21
24
  - [Custom Decorators](#custom-decorators)
22
25
  - [Testing](#testing)
@@ -28,7 +31,7 @@
28
31
 
29
32
  ## Features
30
33
 
31
- - **Decorator-based controllers** — Handle slash commands, buttons, modals, select menus, context menus, messages, and reactions with `@Command`, `@Controller`, and `@UseGuard` decorators. No routing boilerplate.
34
+ - **Decorator-based controllers** — Handle every Discord interaction type — slash commands and their subcommands, autocomplete, buttons, modals, all five select menus, context menus, activity entry points, messages, and reactions — with `@Command`, `@Autocomplete`, `@Controller`, and `@UseGuard` decorators. No routing boilerplate.
32
35
  - **Dependency injection** — Built on Inversify. Services are wired into controllers automatically; no manual instantiation or service locators.
33
36
  - **Guard system** — Pre-execution hooks for auth, rate limiting, metrics, and anything else. Apply per-method or per-class with `@UseGuard`. Guards receive the full interaction context.
34
37
  - **Full CLI** — `meocord create`, `build`, `start`, `generate`. Scaffolds controllers, services, and guards; handles Webpack builds for both development and production.
@@ -220,13 +223,13 @@ export default [
220
223
  npx meocord --help
221
224
  ```
222
225
 
223
- | Command | Alias | Description |
224
- |------------|-------|--------------------------------------|
225
- | `create` | — | Scaffold a new MeoCord application |
226
- | `build` | — | Compile the application via Webpack |
227
- | `start` | — | Start the application |
226
+ | Command | Alias | Description |
227
+ | ---------- | ----- | -------------------------------------- |
228
+ | `create` | — | Scaffold a new MeoCord application |
229
+ | `build` | — | Compile the application via Webpack |
230
+ | `start` | — | Start the application |
228
231
  | `generate` | `g` | Scaffold controllers, services, guards |
229
- | `show` | — | Display framework info |
232
+ | `show` | — | Display framework info |
230
233
 
231
234
  **Common flags:**
232
235
 
@@ -239,6 +242,93 @@ npx meocord g co slash "profile" # generate a slash controller
239
242
  npx meocord g --help # list all generator sub-commands
240
243
  ```
241
244
 
245
+ ### Generating a controller
246
+
247
+ ```shell
248
+ npx meocord g co <type> <name>
249
+ ```
250
+
251
+ `<type>` is one of:
252
+
253
+ `button` · `modal-submit` · `select-menu` · `user-select-menu` · `role-select-menu` · `mentionable-select-menu` · `channel-select-menu` · `reaction` · `message` · `slash` · `autocomplete` · `context-menu` · `primary-entry-point`
254
+
255
+ Each one lands in its own directory, named after the type:
256
+
257
+ ```
258
+ src/controllers/<type>/
259
+ ├── <name>.<type>.controller.ts
260
+ ├── <name>.<type>.controller.spec.ts
261
+ └── builders/sample.builder.ts # slash, context-menu and primary-entry-point only
262
+ ```
263
+
264
+ A builder is generated only for the three types Discord registers by name. Everything else is addressed by `customId` or, for autocomplete, by the command path it completes — there is nothing to register.
265
+
266
+ `<name>` may contain `/` to nest: `npx meocord g co button "admin/ban"` writes into `src/controllers/button/admin/`.
267
+
268
+ Directory layout is organisational only. Controllers are wired up by the `controllers` array on `@MeoCord()`, not by where they sit on disk.
269
+
270
+ ---
271
+
272
+ ## Command Types
273
+
274
+ `@Command` binds a method to one kind of interaction, and the interaction class the handler receives follows from that. Every type Discord sends is covered.
275
+
276
+ | `CommandType` | Handler receives | Routed by |
277
+ | ------------------------- | --------------------------------------------------------------------------- | ---------- |
278
+ | `SLASH` | `ChatInputCommandInteraction` | name |
279
+ | `CONTEXT_MENU` | `UserContextMenuCommandInteraction \| MessageContextMenuCommandInteraction` | name |
280
+ | `PRIMARY_ENTRY_POINT` | `PrimaryEntryPointCommandInteraction` | name |
281
+ | `BUTTON` | `ButtonInteraction` | `customId` |
282
+ | `SELECT_MENU` | `StringSelectMenuInteraction` | `customId` |
283
+ | `USER_SELECT_MENU` | `UserSelectMenuInteraction` | `customId` |
284
+ | `ROLE_SELECT_MENU` | `RoleSelectMenuInteraction` | `customId` |
285
+ | `MENTIONABLE_SELECT_MENU` | `MentionableSelectMenuInteraction` | `customId` |
286
+ | `CHANNEL_SELECT_MENU` | `ChannelSelectMenuInteraction` | `customId` |
287
+ | `MODAL_SUBMIT` | `ModalSubmitInteraction` | `customId` |
288
+
289
+ Autocomplete has its own decorator — see [Autocomplete](#autocomplete). It has no `CommandType` member, because it registers nothing and is answered with `respond()` rather than a reply. `@MessageHandler` and `@ReactionHandler` are outside `CommandType` for the same reason: `CommandType` is the set of things `@Command` can bind to, not the set of things MeoCord handles.
290
+
291
+ The kebab-case `ControllerType` used by the CLI is a wider list — it names every kind of controller that can be scaffolded, including the three that are not commands.
292
+
293
+ The four entity select menus are separate types because Discord sends them as separate component types carrying different resolved data. Declaring `SELECT_MENU` for a user select menu is a type error, not a silent mismatch:
294
+
295
+ ```typescript
296
+ @Command('assign/{taskId}', CommandType.USER_SELECT_MENU)
297
+ async assign(interaction: UserSelectMenuInteraction, { taskId }) {
298
+ await interaction.reply(`Assigned to ${interaction.users.map(user => user.username).join(', ')}`)
299
+ }
300
+ ```
301
+
302
+ ### Slash command options
303
+
304
+ A slash handler's second argument holds the options the command was invoked with, keyed by name. Entity options arrive resolved — a `User`, `Role`, `GuildChannel` or `Attachment`, not the snowflake:
305
+
306
+ ```typescript
307
+ @Command('kick', KickCommandBuilder)
308
+ async kick(interaction: ChatInputCommandInteraction, { target, reason }) {
309
+ // target is a User, reason is a string
310
+ await interaction.reply(`Kicked ${target.username}: ${reason}`)
311
+ }
312
+ ```
313
+
314
+ ### Entry point commands
315
+
316
+ Activity entry points have no builder class in `@discordjs/builders`, so their builder returns the REST body directly. `handler: AppHandler` is what makes Discord send the interaction to the bot at all:
317
+
318
+ ```typescript
319
+ @CommandBuilder(CommandType.PRIMARY_ENTRY_POINT)
320
+ export class LaunchCommandBuilder {
321
+ build() {
322
+ return {
323
+ type: ApplicationCommandType.PrimaryEntryPoint as const,
324
+ name: 'launch',
325
+ description: 'Launch the activity',
326
+ handler: EntryPointCommandHandlerType.AppHandler,
327
+ }
328
+ }
329
+ }
330
+ ```
331
+
242
332
  ---
243
333
 
244
334
  ## Command Parameters
@@ -303,6 +393,76 @@ Where two patterns trade a literal for a parameter in opposite positions — `a/
303
393
 
304
394
  An unroutable interaction replies "Command not found!" to the user and logs a warning naming the `customId` or command that failed to match. If a control appears dead, that log line is the first place to look.
305
395
 
396
+ Autocomplete cannot be replied to, so an unclaimed option is answered with an empty list instead and the warning names the command and option.
397
+
398
+ A handler that throws is logged and gets the same treatment — except when it had already replied or deferred, in which case MeoCord leaves the response alone rather than sending a second one Discord would reject.
399
+
400
+ ### Failures never take the bot down
401
+
402
+ discord.js calls event listeners without awaiting them, so anything that rejects out of one is an unhandled rejection — which terminates the process by default. MeoCord wraps every listener it registers, so one bad interaction, one unresolvable controller, or one reaction on a deleted message costs that event and nothing else. The error is logged against the event that produced it, so a genuine misconfiguration still shows up on the first interaction rather than staying hidden.
403
+
404
+ Where the failure happened before the handler ran, the user is still told: a command interaction gets the error reply, an autocomplete gets its window closed. A reaction whose message can no longer be fetched — deleted, or in a channel the bot lost access to — is skipped quietly, since that is an ordinary outcome rather than a fault.
405
+
406
+ ---
407
+
408
+ ## Subcommands
409
+
410
+ Discord sends `/settings notify email` as a single interaction named `settings`, so a command with subcommands would otherwise have one handler for all of them. Name the full path — parts separated by a space, the way Discord displays them — to give each subcommand its own method:
411
+
412
+ ```typescript
413
+ @Controller()
414
+ export class SettingsController {
415
+ // The builder is declared once, on the command itself.
416
+ @Command('settings', SettingsCommandBuilder)
417
+ async settings(interaction: ChatInputCommandInteraction) {
418
+ await interaction.reply('Pick a subcommand.')
419
+ }
420
+
421
+ @Command('settings notify email', CommandType.SLASH)
422
+ async notifyEmail(interaction: ChatInputCommandInteraction, { enabled }) {
423
+ await interaction.reply(`Email notifications ${enabled ? 'on' : 'off'}`)
424
+ }
425
+ }
426
+ ```
427
+
428
+ Subcommand handlers take the plain `CommandType.SLASH` and no builder: the subcommand is already described by the parent's builder, and registering a second command for it would be rejected by Discord. Options are flattened, so `notifyEmail` receives `{ enabled }` rather than the wrapping subcommand.
429
+
430
+ The full path is always tried before the bare command name, whatever order the controllers were registered in, and a subcommand nobody claimed falls back to the command's own handler. A group is never dropped on the way down — `settings notify email` does not fall back to `settings email`, because another group could declare its own `email`.
431
+
432
+ ---
433
+
434
+ ## Autocomplete
435
+
436
+ Autocomplete is a separate interaction from the command it belongs to: Discord sends it while the user is still typing, it is answered with `respond()` rather than a reply, and the window closes after three seconds. `@Autocomplete` binds a handler to it.
437
+
438
+ ```typescript
439
+ @Controller()
440
+ export class SearchController {
441
+ constructor(private catalog: CatalogService) {}
442
+
443
+ @Autocomplete('search', 'query')
444
+ async completeQuery(interaction: AutocompleteInteraction) {
445
+ const { value } = interaction.options.getFocused(true)
446
+ const matches = this.catalog.find(value).slice(0, 25)
447
+
448
+ await interaction.respond(matches.map(name => ({ name, value: name })))
449
+ }
450
+ }
451
+ ```
452
+
453
+ The option must be declared with `.setAutocomplete(true)` on the command builder — that is what makes Discord send the interaction.
454
+
455
+ Omit the option name to handle every option of a command and branch on `getFocused(true)` yourself. An option-specific handler always wins over a command-wide one, so the two can coexist. The first argument is the command path, so subcommands work the same way as they do for `@Command`:
456
+
457
+ ```typescript
458
+ @Autocomplete('settings notify email', 'address')
459
+ async completeAddress(interaction: AutocompleteInteraction, { region }) { /* … */ }
460
+ ```
461
+
462
+ The second argument holds the options already filled in, which is what lets one option's suggestions depend on another's value.
463
+
464
+ If no handler claims an option, MeoCord answers with an empty list and logs which command and option are missing one — a visibly empty menu rather than a client stuck loading.
465
+
306
466
  ---
307
467
 
308
468
  ## Guards
@@ -359,9 +519,7 @@ import { UseGuard } from 'meocord/decorator'
359
519
  import { DefaultGuard, RateLimiterGuard } from '@src/guards/index.js'
360
520
 
361
521
  export const Protected = (limit = 5) =>
362
- applyDecorators(
363
- UseGuard(DefaultGuard, { provide: RateLimiterGuard, params: { limit } }),
364
- )
522
+ applyDecorators(UseGuard(DefaultGuard, { provide: RateLimiterGuard, params: { limit } }))
365
523
  ```
366
524
 
367
525
  ```typescript
@@ -441,7 +599,11 @@ Creates a smart mock instance of any discord.js class. The full prototype chain
441
599
 
442
600
  **Type guards run real logic** — `isButton()`, `isRepliable()`, `isChatInputCommand()`, etc. are backed by the actual discord.js prototype methods. The right fields (`type`, `componentType`, `commandType`) are set based on the class you pass in, so no manual `.mockReturnValue(true)` setup is needed. All type guard methods are still mock functions and can be overridden per test.
443
601
 
444
- **Reply state machine** — for repliable interactions, `replied` and `deferred` start as `false`. Calling `reply()` or `deferReply()` twice throws, just like a real interaction. `followUp()`, `editReply()`, and `deleteReply()` throw if called before any reply. The ephemeral flag is tracked on `interaction.ephemeral`. All reply methods are still mock functions so call assertions work normally.
602
+ **Reply state machine** — for repliable interactions, `replied` and `deferred` start as `false`. Calling `reply()` or `deferReply()` twice throws, just like a real interaction. `followUp()`, `editReply()`, and `deleteReply()` throw if called before any reply. The ephemeral flag is tracked on `interaction.ephemeral`, read from `flags` only — the deprecated `ephemeral: true` reply option is not honoured. All reply methods are still mock functions so call assertions work normally.
603
+
604
+ Autocomplete interactions are not repliable but get the equivalent for their own single-shot response: `responded` starts as `false`, `respond()` sets it, and a second call throws.
605
+
606
+ Guards discord.js has deprecated are deliberately left unwired — `isSelectMenu()` returns `undefined` rather than reproducing behaviour the library is removing. Use `isStringSelectMenu()`.
445
607
 
446
608
  > **Framework-agnostic** — the mocks returned here are plain mock functions that stamp `_isMockFunction` and expose `.mock.calls`, the exact contract both `jest` and `vitest` check. Use them with either framework's `expect(...).toHaveBeenCalledWith(...)` / `toHaveBeenCalledTimes(...)` — no jest or vitest import is required to produce them. For typed stubs in your own code, import `MockedFunction`, `createMockFn`, and `DeepMocked` from `meocord/testing`.
447
609
 
@@ -453,17 +615,17 @@ const interaction = createMockInteraction(ChatInputCommandInteraction)
453
615
 
454
616
  // instanceof works at every level
455
617
  expect(interaction).toBeInstanceOf(ChatInputCommandInteraction) // true
456
- expect(interaction).toBeInstanceOf(BaseInteraction) // true
618
+ expect(interaction).toBeInstanceOf(BaseInteraction) // true
457
619
 
458
620
  // type guards work — no manual setup needed
459
621
  interaction.isChatInputCommand() // → true
460
- interaction.isRepliable() // → true
461
- interaction.isButton() // → false
622
+ interaction.isRepliable() // → true
623
+ interaction.isButton() // → false
462
624
 
463
625
  // reply state machine
464
- interaction.replied // → false
626
+ interaction.replied // → false
465
627
  await interaction.reply({ content: 'hi' })
466
- interaction.replied // → true
628
+ interaction.replied // → true
467
629
  await interaction.reply({ content: 'again' }) // → throws (already replied)
468
630
 
469
631
  // still a mock fn — call assertions work normally
@@ -519,15 +681,38 @@ interaction.options = createChatInputOptions({
519
681
  duration: 7,
520
682
  })
521
683
 
522
- interaction.options.getSubcommandGroup() // → 'admin'
523
- interaction.options.getSubcommand(true) // → 'ban'
524
- interaction.options.getUser('user') // → { id: '123456789' }
525
- interaction.options.getString('reason') // → 'spam'
526
- interaction.options.getNumber('duration') // → 7
527
- interaction.options.getString('duration') // → null (wrong type)
528
- interaction.options.getNumber('x', true) // → throws (absent + required)
684
+ interaction.options.getSubcommandGroup() // → 'admin'
685
+ interaction.options.getSubcommand(true) // → 'ban'
686
+ interaction.options.getUser('user') // → { id: '123456789' }
687
+ interaction.options.getString('reason') // → 'spam'
688
+ interaction.options.getNumber('duration') // → 7
689
+ interaction.options.getString('duration') // → null (wrong type)
690
+ interaction.options.getNumber('x', true) // → throws (absent + required)
691
+ ```
692
+
693
+ `data` is materialised too, nested under the subcommand path exactly as Discord sends it. That is what the framework reads to build a handler's second argument, so a params assertion sees the same record production would:
694
+
695
+ ```typescript
696
+ interaction.options.data
697
+ // → [{ name: 'admin', type: SubcommandGroup, options: [{ name: 'ban', type: Subcommand, options: [...] }] }]
529
698
  ```
530
699
 
700
+ Entity options are set on both `value` (the snowflake) and their own resolved field, so a handler that reads only one of the two is caught rather than silently passing. Pass a `createMockInteraction(User, …)`, `Role`, channel or `Attachment` mock and it lands on `user`/`role`/`channel`/`attachment`.
701
+
702
+ For autocomplete, `focused` names the option being typed:
703
+
704
+ ```typescript
705
+ const interaction = createMockInteraction(AutocompleteInteraction)
706
+ interaction.options = createChatInputOptions({ focused: 'query', query: 'ad' })
707
+
708
+ interaction.options.getFocused(true) // → { name: 'query', value: 'ad', focused: true, … }
709
+ interaction.options.getFocused() // → 'ad'
710
+ ```
711
+
712
+ Omit it and `getFocused` throws, the same as the real resolver does when no option is focused.
713
+
714
+ `subcommandGroup`, `subcommand` and `focused` are reserved keys — an option of your own cannot use those names.
715
+
531
716
  All methods are mock functions — override any per test with `.mockReturnValue()`.
532
717
 
533
718
  ### `createMockUser` / `createMockClient` / `createMockGuild` / `createMockChannel`
@@ -535,18 +720,12 @@ All methods are mock functions — override any per test with `.mockReturnValue(
535
720
  Convenience wrappers for common discord.js classes. All methods are auto-stubbed as mock functions. Nested managers (`client.users`, `guild.members`, etc.) are independent nested stubs.
536
721
 
537
722
  ```typescript
538
- import {
539
- createMockFn,
540
- createMockUser,
541
- createMockClient,
542
- createMockGuild,
543
- createMockChannel,
544
- } from 'meocord/testing'
723
+ import { createMockFn, createMockUser, createMockClient, createMockGuild, createMockChannel } from 'meocord/testing'
545
724
  import { TextChannel } from 'discord.js'
546
725
 
547
- const user = createMockUser()
548
- const client = createMockClient()
549
- const guild = createMockGuild()
726
+ const user = createMockUser()
727
+ const client = createMockClient()
728
+ const guild = createMockGuild()
550
729
  const channel = createMockChannel(TextChannel)
551
730
 
552
731
  // override nested manager methods per test (createMockFn works with vitest and jest matchers)
@@ -564,15 +743,15 @@ import { createMockMessage } from 'meocord/testing'
564
743
 
565
744
  const msg = createMockMessage()
566
745
 
567
- msg.deleted // → false
746
+ msg.deleted // → false
568
747
  await msg.delete()
569
- msg.deleted // → true
570
- await msg.delete() // → throws (already deleted)
748
+ msg.deleted // → true
749
+ await msg.delete() // → throws (already deleted)
571
750
  await msg.edit({ content: 'x' }) // → throws (already deleted)
572
751
 
573
752
  // edit() and reply() resolve to a new Message mock
574
753
  const edited = await createMockMessage().edit({ content: 'updated' })
575
- edited.delete // → a mock fn
754
+ edited.delete // → a mock fn
576
755
 
577
756
  // still a mock fn — assertions work
578
757
  expect(msg.delete).toHaveBeenCalledTimes(1)
@@ -610,8 +789,10 @@ const module = MeoCordTestingModule.create({
610
789
  controllers: [GreetingSlashController],
611
790
  providers: [{ provide: GreetingService, useValue: mockGreetingService }],
612
791
  })
613
- .overrideGuard(MetricsGuard).useValue({ canActivate: () => true })
614
- .overrideGuard(RateLimiterGuard).useValue({ canActivate: () => true })
792
+ .overrideGuard(MetricsGuard)
793
+ .useValue({ canActivate: () => true })
794
+ .overrideGuard(RateLimiterGuard)
795
+ .useValue({ canActivate: () => true })
615
796
  .compile()
616
797
  ```
617
798
 
@@ -626,7 +807,8 @@ const module = MeoCordTestingModule.create({
626
807
  controllers: [GreetingSlashController],
627
808
  providers: [{ provide: GreetingService, useValue: realGreetingService }],
628
809
  })
629
- .overrideProvider(GreetingService).useValue({ buildGreeting: createMockFn() })
810
+ .overrideProvider(GreetingService)
811
+ .useValue({ buildGreeting: createMockFn() })
630
812
  .compile()
631
813
  ```
632
814
 
@@ -656,7 +838,8 @@ describe('GreetingSlashController', () => {
656
838
  controllers: [GreetingSlashController],
657
839
  providers: [{ provide: GreetingService, useValue: greetingService }],
658
840
  })
659
- .overrideGuard(RateLimiterGuard).useValue({ canActivate: () => true })
841
+ .overrideGuard(RateLimiterGuard)
842
+ .useValue({ canActivate: () => true })
660
843
  .compile()
661
844
 
662
845
  controller = module.get(GreetingSlashController)