meocord 3.2.0 → 3.2.2

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/CHANGELOG.md +9 -0
  2. package/LICENSE +1 -1
  3. package/README.md +7 -24
  4. package/dist/cjs/_shared/{controller.decorator-MUHA_A3z.cjs → controller.decorator-eT0oHb8D.cjs} +1 -1
  5. package/dist/cjs/_shared/{metadata-key.enum-BzzvGUId.cjs → metadata-key.enum-DITxfZlw.cjs} +1 -1
  6. package/dist/cjs/_shared/{theme-Bz-D4RbT.cjs → theme-EmG1ZEwV.cjs} +4 -4
  7. package/dist/cjs/common/index.cjs +2 -2
  8. package/dist/cjs/core/index.cjs +44 -21
  9. package/dist/cjs/decorator/index.cjs +2 -2
  10. package/dist/cjs/enum/index.cjs +1 -1
  11. package/dist/cjs/testing/index.cjs +2 -2
  12. package/dist/esm/bin/meocord.js +6 -2
  13. package/dist/esm/common/decorator.js +1 -1
  14. package/dist/esm/common/theme.js +1 -1
  15. package/dist/esm/core/meocord.app.js +42 -19
  16. package/dist/esm/enum/controller.enum.js +1 -1
  17. package/dist/esm/enum/metadata-key.enum.js +1 -1
  18. package/dist/esm/package.json.js +1 -1
  19. package/dist/esm/testing/mock-fn.js +1 -1
  20. package/dist/esm/util/json.util.js +1 -1
  21. package/dist/esm/util/meocord-cli.util.js +1 -1
  22. package/dist/esm/util/runtime.util.js +1 -1
  23. package/dist/esm/util/wait.util.js +1 -1
  24. package/dist/types/common/index.d.ts +3 -3
  25. package/dist/types/core/index.d.ts +2 -2
  26. package/dist/types/decorator/index.d.ts +5 -5
  27. package/dist/types/enum/index.d.ts +1 -1
  28. package/dist/types/interface/index.d.ts +2 -2
  29. package/dist/types/testing/index.d.ts +3 -3
  30. package/meocord.eslint.cjs +1 -1
  31. package/meocord.eslint.d.ts +1 -1
  32. package/meocord.eslint.mjs +1 -1
  33. package/package.json +19 -17
  34. package/webpack.config.js +1 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # meocord
2
+
3
+ ## 3.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#13](https://github.com/l7aromeo/meocord/pull/13) [`f8a6b15`](https://github.com/l7aromeo/meocord/commit/f8a6b156e8d22abf88443ba77c605d6d47991ab4) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Update the shipped dependencies — `@clack/prompts` 1.8.0, `@swc/core` 1.16.2, and `webpack`
8
+ 5.110.3 — and state the copyright as `2025-present`, including in the notice the CLI prints
9
+ under `--license` and in its help banner.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # MeoCord Framework
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/meocord.svg)](https://www.npmjs.com/package/meocord)
4
+ [![CI](https://github.com/l7aromeo/meocord/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/l7aromeo/meocord/actions/workflows/release.yml)
5
+ [![node](https://img.shields.io/node/v/meocord)](https://www.npmjs.com/package/meocord)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
7
+
3
8
  **MeoCord** is a decorator-based Discord bot framework built on top of discord.js. It brings a NestJS-style architecture — controllers, services, guards, and dependency injection — to bot development, with a full CLI, TypeScript-first design, and testing utilities included out of the box.
4
9
 
5
10
  ---
@@ -1003,31 +1008,9 @@ MEOCORD_RUNTIME=/usr/local/bin/bun npm run start
1003
1008
 
1004
1009
  ## Contributing
1005
1010
 
1006
- 1. Fork the repository
1007
- 2. Create a feature branch: `git checkout -b feat/your-feature`
1008
- 3. Commit with conventional commits: `git commit -m "feat: add X"`
1009
- 4. Run `bun run lint` and `bun run test` before pushing
1010
- 5. If you touched anything under `src/bin/`, also run `bun run build && bun run verify:generated`
1011
- 6. Push and open a pull request against `main`
1012
-
1013
- Include a description of what changed and why, and add tests for any new behaviour.
1014
-
1015
- `verify:generated` generates one controller of every type through the built CLI — flat and nested, in separate throwaway projects — and typechecks the result against the published package. Rendering a template says nothing about whether the code it produces compiles, and two bugs lived behind exactly that gap. It runs in CI as part of the Build job, so you do not have to remember it; running it locally is just faster than waiting.
1016
-
1017
- ### Commit messages and releases
1018
-
1019
- Commit messages drive versioning through [semantic-release](https://semantic-release.gitbook.io/). Only these publish:
1020
-
1021
- | Prefix | Release |
1022
- | ------------------------------ | ------- |
1023
- | `feat:` | minor |
1024
- | `fix:` | patch |
1025
- | `perf:` | patch |
1026
- | `BREAKING CHANGE:` in the body | major |
1027
-
1028
- Everything else — `docs:`, `test:`, `ci:`, `chore:`, `refactor:`, `style:` — lands on `main` without publishing and ships with whatever releasable commit comes next.
1011
+ Issues, questions, and pull requests are welcome. [CONTRIBUTING.md](./CONTRIBUTING.md) covers getting set up, what each check exists to catch, and how releases work — a change that reaches the published package carries a [changeset](https://github.com/changesets/changesets), and merging the release pull request is what publishes it.
1029
1012
 
1030
- Pick the prefix by what reaches the installed package, not by which file you edited. JSDoc is compiled into the published `.d.ts` and is what a user reads in their editor, so correcting a wrong `@example` is a `fix:` even though you only touched a comment. A README-only change is `docs:`.
1013
+ Participation is governed by the [Code of Conduct](./CODE_OF_CONDUCT.md). For vulnerabilities, follow [SECURITY.md](./SECURITY.md) rather than opening an issue.
1031
1014
 
1032
1015
  ---
1033
1016
 
@@ -3,7 +3,7 @@
3
3
  require('reflect-metadata');
4
4
  var inversify = require('inversify');
5
5
  var enum_index = require('../enum/index.cjs');
6
- var metadataKey_enum = require('./metadata-key.enum-BzzvGUId.cjs');
6
+ var metadataKey_enum = require('./metadata-key.enum-DITxfZlw.cjs');
7
7
  var discord_js = require('discord.js');
8
8
 
9
9
  /**
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * MeoCord Framework
5
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
5
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
6
6
  * SPDX-License-Identifier: MIT
7
7
  */ /**
8
8
  * Centralised metadata keys used across the framework's `Reflect` calls.
@@ -12,7 +12,7 @@ var chalk = require('chalk');
12
12
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
13
13
  /**
14
14
  * MeoCord Framework
15
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
15
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
16
16
  * SPDX-License-Identifier: MIT
17
17
  */ /**
18
18
  * Helper function to fix common JSON formatting issues in tsconfig.json, such as:
@@ -55,7 +55,7 @@ let configLoaded = false;
55
55
  const compiledPath = path.resolve(process.cwd(), 'dist', 'meocord.config.mjs');
56
56
  if (!fs.existsSync(compiledPath)) return undefined;
57
57
  try {
58
- const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-Bz-D4RbT.cjs', document.baseURI).href)), {
58
+ const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-EmG1ZEwV.cjs', document.baseURI).href)), {
59
59
  interopDefault: true
60
60
  });
61
61
  return jiti$1(compiledPath);
@@ -83,7 +83,7 @@ let configLoaded = false;
83
83
  }
84
84
  }
85
85
  }
86
- const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-Bz-D4RbT.cjs', document.baseURI).href)), {
86
+ const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-EmG1ZEwV.cjs', document.baseURI).href)), {
87
87
  interopDefault: true,
88
88
  alias: aliases,
89
89
  moduleCache: false
@@ -165,7 +165,7 @@ class Logger {
165
165
 
166
166
  /**
167
167
  * MeoCord Framework
168
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
168
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
169
169
  * SPDX-License-Identifier: MIT
170
170
  */ class Theme {
171
171
  }
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var theme = require('../_shared/theme-Bz-D4RbT.cjs');
3
+ var theme = require('../_shared/theme-EmG1ZEwV.cjs');
4
4
  require('node:util');
5
5
  require('dayjs');
6
6
  require('dayjs/plugin/utc.js');
@@ -12,7 +12,7 @@ require('chalk');
12
12
 
13
13
  /**
14
14
  * MeoCord Framework
15
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
15
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
16
16
  * SPDX-License-Identifier: MIT
17
17
  */ /**
18
18
  * Composes multiple class or method decorators into a single decorator.
@@ -3,12 +3,12 @@
3
3
  require('reflect-metadata');
4
4
  var inversify = require('inversify');
5
5
  var discord_js = require('discord.js');
6
- var theme = require('../_shared/theme-Bz-D4RbT.cjs');
7
- var controller_decorator = require('../_shared/controller.decorator-MUHA_A3z.cjs');
6
+ var theme = require('../_shared/theme-EmG1ZEwV.cjs');
7
+ var controller_decorator = require('../_shared/controller.decorator-eT0oHb8D.cjs');
8
8
  var lodashEs = require('lodash-es');
9
9
  var enum_index = require('../enum/index.cjs');
10
10
  var Table = require('cli-table3');
11
- var metadataKey_enum = require('../_shared/metadata-key.enum-BzzvGUId.cjs');
11
+ var metadataKey_enum = require('../_shared/metadata-key.enum-DITxfZlw.cjs');
12
12
  require('node:util');
13
13
  require('dayjs');
14
14
  require('dayjs/plugin/utc.js');
@@ -26,23 +26,44 @@ const createErrorEmbed = (description)=>{
26
26
  return embed;
27
27
  };
28
28
 
29
+ /** What Discord assumes a command body is when it carries no type of its own. */ const DEFAULT_APPLICATION_COMMAND_TYPE = discord_js.ApplicationCommandType.ChatInput;
29
30
  /**
30
- * The name a builder registers under, which is what Discord deduplicates on.
31
+ * The body a builder registers, which is what Discord sees.
31
32
  *
32
- * Read from the built payload rather than from the `@Command` argument: a builder is
33
- * free to name the command something other than the string it was handed, and it is
34
- * the payload Discord sees.
35
- */ function commandNameOf(builder) {
33
+ * Read from the built payload rather than from the `@Command` arguments: a builder is
34
+ * free to describe the command differently from the strings it was handed.
35
+ */ function payloadOf(builder) {
36
36
  try {
37
- const json = typeof builder.toJSON === 'function' ? builder.toJSON() : builder;
38
- return typeof json?.name === 'string' ? json.name : undefined;
37
+ return typeof builder.toJSON === 'function' ? builder.toJSON() : builder;
39
38
  } catch {
40
39
  // A builder missing a required field throws from toJSON. Surfacing that is the
41
40
  // registration call's job, where it is reported against the command Discord
42
41
  // rejected -- deduplication should not be what turns it into a startup crash.
43
- return undefined;
42
+ return {};
44
43
  }
45
44
  }
45
+ /** The name a builder registers under. */ function commandNameOf(builder) {
46
+ const { name } = payloadOf(builder);
47
+ return typeof name === 'string' ? name : undefined;
48
+ }
49
+ /**
50
+ * The application command type a builder registers as.
51
+ *
52
+ * A slash builder may leave the field out, so an absent type is read as the chat input
53
+ * one Discord would infer — which keeps untyped slash builders colliding with each
54
+ * other rather than each claiming a key of its own.
55
+ */ function commandTypeOf(builder) {
56
+ const { type } = payloadOf(builder);
57
+ return typeof type === 'number' ? type : DEFAULT_APPLICATION_COMMAND_TYPE;
58
+ }
59
+ /**
60
+ * The identity Discord gives a command.
61
+ *
62
+ * The numeric type leads, so the two halves can never be read apart wrongly: everything
63
+ * before the first separator is the type, everything after it is the name.
64
+ */ function registrationKey(builder, fallbackName) {
65
+ return `${commandTypeOf(builder)}:${commandNameOf(builder) ?? fallbackName}`;
66
+ }
46
67
  class MeoCordApp {
47
68
  /**
48
69
  * Runs an event handler so a failure inside it cannot take the process down.
@@ -109,11 +130,13 @@ class MeoCordApp {
109
130
  }
110
131
  }
111
132
  async registerCommands() {
112
- // Keyed by registered name: a command whose subcommands live in separate methods
113
- // declares the same name more than once, and sending its builder twice makes
114
- // Discord reject the whole payload. The first builder wins, and a second one that
115
- // is not the same object is reported rather than silently dropped.
116
- const buildersByName = new Map();
133
+ // Keyed by type and name together, because that pair is what Discord treats as one
134
+ // command: a user context menu and a message context menu are free to share a name,
135
+ // and keying on the name alone would drop one of them from the payload. Within a
136
+ // single type the name is unique, so a command whose subcommands live in separate
137
+ // methods still contributes its builder once — sending it twice makes Discord
138
+ // reject the whole payload.
139
+ const buildersByCommand = new Map();
117
140
  for (const controllerClass of this.controllerClasses){
118
141
  const instance = this.getInstance(controllerClass);
119
142
  const commandMap = controller_decorator.getCommandMap(instance);
@@ -122,18 +145,18 @@ class MeoCordApp {
122
145
  if (!Array.isArray(commandMetadataArray)) continue;
123
146
  for (const { builder, type } of commandMetadataArray){
124
147
  if (!(type in enum_index.CommandType) || !builder) continue;
125
- const registeredName = commandNameOf(builder) ?? commandName;
126
- const existing = buildersByName.get(registeredName);
148
+ const key = registrationKey(builder, commandName);
149
+ const existing = buildersByCommand.get(key);
127
150
  if (existing === undefined) {
128
- buildersByName.set(registeredName, builder);
151
+ buildersByCommand.set(key, builder);
129
152
  } else if (existing !== builder) {
130
- this.logger.warn(`Command "${registeredName}" is built more than once; only the first builder is registered. ` + `Declare the builder on one @Command and give the others the plain CommandType.`);
153
+ this.logger.warn(`Command "${commandNameOf(builder) ?? commandName}" is built more than once for the same ` + `application command type; only the first builder is registered. Two builders of one type ` + `cannot both own a name, so declare the builder on a single @Command and give the others ` + `the plain CommandType.`);
131
154
  }
132
155
  }
133
156
  }
134
157
  }
135
158
  const builders = [
136
- ...buildersByName.values()
159
+ ...buildersByCommand.values()
137
160
  ];
138
161
  try {
139
162
  if (this.bot.application) {
@@ -2,8 +2,8 @@
2
2
 
3
3
  require('reflect-metadata');
4
4
  var inversify = require('inversify');
5
- var metadataKey_enum = require('../_shared/metadata-key.enum-BzzvGUId.cjs');
6
- var controller_decorator = require('../_shared/controller.decorator-MUHA_A3z.cjs');
5
+ var metadataKey_enum = require('../_shared/metadata-key.enum-DITxfZlw.cjs');
6
+ var controller_decorator = require('../_shared/controller.decorator-eT0oHb8D.cjs');
7
7
  var discord_js = require('discord.js');
8
8
  require('../enum/index.cjs');
9
9
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var metadataKey_enum = require('../_shared/metadata-key.enum-BzzvGUId.cjs');
3
+ var metadataKey_enum = require('../_shared/metadata-key.enum-DITxfZlw.cjs');
4
4
 
5
5
  /**
6
6
  * The kinds of interaction a `@Command` method can be bound to.
@@ -2,7 +2,7 @@
2
2
 
3
3
  require('reflect-metadata');
4
4
  var inversify = require('inversify');
5
- var metadataKey_enum = require('../_shared/metadata-key.enum-BzzvGUId.cjs');
5
+ var metadataKey_enum = require('../_shared/metadata-key.enum-DITxfZlw.cjs');
6
6
  var discord_js = require('discord.js');
7
7
 
8
8
  function isValueProvider(p) {
@@ -122,7 +122,7 @@ function isValueProvider(p) {
122
122
 
123
123
  /**
124
124
  * MeoCord Framework
125
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
125
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
126
126
  * SPDX-License-Identifier: MIT
127
127
  */ // ---------------------------------------------------------------------------
128
128
  // Framework-agnostic mock function
@@ -82,7 +82,7 @@ SOFTWARE.
82
82
  console.log(`
83
83
  MIT License
84
84
 
85
- Copyright (c) 2025 Ukasyah Rahmatullah Zada
85
+ Copyright (c) 2025-present Ukasyah Rahmatullah Zada
86
86
 
87
87
  Permission is hereby granted, free of charge, to any person obtaining a copy
88
88
  of this software and associated documentation files (the "Software"), to deal
@@ -164,7 +164,11 @@ copies or substantial portions of the Software.
164
164
  })),
165
165
  initialValue: defaultPM
166
166
  });
167
- if (p.isCancel(selected)) {
167
+ // `select` types its cancel sentinel as plain `symbol`, while `isCancel` narrows only
168
+ // that sentinel's own `unique symbol` — so the guard leaves `symbol` in its false
169
+ // branch and `pm` stops being a PackageManager. The prompt returns a symbol in no
170
+ // other case, so the cancel is detected on that instead.
171
+ if (typeof selected === 'symbol') {
168
172
  p.cancel('Operation cancelled.');
169
173
  process.exit(0);
170
174
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ /**
6
6
  * Composes multiple class or method decorators into a single decorator.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ class Theme {
6
6
  }
@@ -1,4 +1,4 @@
1
- import { SlashCommandBuilder, MessageFlagsBitField } from 'discord.js';
1
+ import { ApplicationCommandType, SlashCommandBuilder, MessageFlagsBitField } from 'discord.js';
2
2
  import { Logger } from '../common/logger.js';
3
3
  import '../common/theme.js';
4
4
  import { getCommandMap, findAmbiguousRoutes, PARAM_SEPARATOR, getAutocompleteHandlers, getMessageHandlers, getReactionHandlers } from '../decorator/controller.decorator.js';
@@ -8,23 +8,44 @@ import { describeInteraction, hasCustomId, matchesCommandType, resolveCommandPat
8
8
  import { ReactionHandlerAction, CommandType } from '../enum/controller.enum.js';
9
9
  import CliTable3 from 'cli-table3';
10
10
 
11
+ /** What Discord assumes a command body is when it carries no type of its own. */ const DEFAULT_APPLICATION_COMMAND_TYPE = ApplicationCommandType.ChatInput;
11
12
  /**
12
- * The name a builder registers under, which is what Discord deduplicates on.
13
+ * The body a builder registers, which is what Discord sees.
13
14
  *
14
- * Read from the built payload rather than from the `@Command` argument: a builder is
15
- * free to name the command something other than the string it was handed, and it is
16
- * the payload Discord sees.
17
- */ function commandNameOf(builder) {
15
+ * Read from the built payload rather than from the `@Command` arguments: a builder is
16
+ * free to describe the command differently from the strings it was handed.
17
+ */ function payloadOf(builder) {
18
18
  try {
19
- const json = typeof builder.toJSON === 'function' ? builder.toJSON() : builder;
20
- return typeof json?.name === 'string' ? json.name : undefined;
19
+ return typeof builder.toJSON === 'function' ? builder.toJSON() : builder;
21
20
  } catch {
22
21
  // A builder missing a required field throws from toJSON. Surfacing that is the
23
22
  // registration call's job, where it is reported against the command Discord
24
23
  // rejected -- deduplication should not be what turns it into a startup crash.
25
- return undefined;
24
+ return {};
26
25
  }
27
26
  }
27
+ /** The name a builder registers under. */ function commandNameOf(builder) {
28
+ const { name } = payloadOf(builder);
29
+ return typeof name === 'string' ? name : undefined;
30
+ }
31
+ /**
32
+ * The application command type a builder registers as.
33
+ *
34
+ * A slash builder may leave the field out, so an absent type is read as the chat input
35
+ * one Discord would infer — which keeps untyped slash builders colliding with each
36
+ * other rather than each claiming a key of its own.
37
+ */ function commandTypeOf(builder) {
38
+ const { type } = payloadOf(builder);
39
+ return typeof type === 'number' ? type : DEFAULT_APPLICATION_COMMAND_TYPE;
40
+ }
41
+ /**
42
+ * The identity Discord gives a command.
43
+ *
44
+ * The numeric type leads, so the two halves can never be read apart wrongly: everything
45
+ * before the first separator is the type, everything after it is the name.
46
+ */ function registrationKey(builder, fallbackName) {
47
+ return `${commandTypeOf(builder)}:${commandNameOf(builder) ?? fallbackName}`;
48
+ }
28
49
  class MeoCordApp {
29
50
  /**
30
51
  * Runs an event handler so a failure inside it cannot take the process down.
@@ -91,11 +112,13 @@ class MeoCordApp {
91
112
  }
92
113
  }
93
114
  async registerCommands() {
94
- // Keyed by registered name: a command whose subcommands live in separate methods
95
- // declares the same name more than once, and sending its builder twice makes
96
- // Discord reject the whole payload. The first builder wins, and a second one that
97
- // is not the same object is reported rather than silently dropped.
98
- const buildersByName = new Map();
115
+ // Keyed by type and name together, because that pair is what Discord treats as one
116
+ // command: a user context menu and a message context menu are free to share a name,
117
+ // and keying on the name alone would drop one of them from the payload. Within a
118
+ // single type the name is unique, so a command whose subcommands live in separate
119
+ // methods still contributes its builder once — sending it twice makes Discord
120
+ // reject the whole payload.
121
+ const buildersByCommand = new Map();
99
122
  for (const controllerClass of this.controllerClasses){
100
123
  const instance = this.getInstance(controllerClass);
101
124
  const commandMap = getCommandMap(instance);
@@ -104,18 +127,18 @@ class MeoCordApp {
104
127
  if (!Array.isArray(commandMetadataArray)) continue;
105
128
  for (const { builder, type } of commandMetadataArray){
106
129
  if (!(type in CommandType) || !builder) continue;
107
- const registeredName = commandNameOf(builder) ?? commandName;
108
- const existing = buildersByName.get(registeredName);
130
+ const key = registrationKey(builder, commandName);
131
+ const existing = buildersByCommand.get(key);
109
132
  if (existing === undefined) {
110
- buildersByName.set(registeredName, builder);
133
+ buildersByCommand.set(key, builder);
111
134
  } else if (existing !== builder) {
112
- this.logger.warn(`Command "${registeredName}" is built more than once; only the first builder is registered. ` + `Declare the builder on one @Command and give the others the plain CommandType.`);
135
+ this.logger.warn(`Command "${commandNameOf(builder) ?? commandName}" is built more than once for the same ` + `application command type; only the first builder is registered. Two builders of one type ` + `cannot both own a name, so declare the builder on a single @Command and give the others ` + `the plain CommandType.`);
113
136
  }
114
137
  }
115
138
  }
116
139
  }
117
140
  const builders = [
118
- ...buildersByName.values()
141
+ ...buildersByCommand.values()
119
142
  ];
120
143
  try {
121
144
  if (this.bot.application) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ var ControllerType = /*#__PURE__*/ function(ControllerType) {
6
6
  ControllerType["BUTTON"] = "button";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ /**
6
6
  * Centralised metadata keys used across the framework's `Reflect` calls.
@@ -1,4 +1,4 @@
1
- var version = "0.0.0-development";
1
+ var version = "3.2.2";
2
2
  var packageJson = {
3
3
  version: version};
4
4
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ // ---------------------------------------------------------------------------
6
6
  // Framework-agnostic mock function
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ /**
6
6
  * Helper function to fix common JSON formatting issues in tsconfig.json, such as:
@@ -23,7 +23,7 @@ function configureCommandHelp(command) {
23
23
  * @param options - The options available for the command.
24
24
  * @returns The formatted help text.
25
25
  */ function formatHelp(cmd, helper, options) {
26
- let helpText = `MeoCord Copyright (c) 2025 Ukasyah Rahmatullah Zada — MIT License\n\n`;
26
+ let helpText = `MeoCord Copyright (c) 2025-present Ukasyah Rahmatullah Zada — MIT License\n\n`;
27
27
  helpText += `${helper.commandUsage(cmd)}\n\n`;
28
28
  helpText += `${helper.commandDescription(cmd)}\n\n`;
29
29
  if (cmd.registeredArguments.length > 0) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ /**
6
6
  * Environment variable that pins the binary the application is spawned with.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */ function wait(ms) {
6
6
  return new Promise((resolve)=>setTimeout(resolve, ms));
@@ -2,7 +2,7 @@ import { ColorResolvable } from 'discord.js';
2
2
 
3
3
  /**
4
4
  * MeoCord Framework
5
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
5
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
6
6
  * SPDX-License-Identifier: MIT
7
7
  */
8
8
  declare class Logger {
@@ -21,7 +21,7 @@ declare class Logger {
21
21
 
22
22
  /**
23
23
  * MeoCord Framework
24
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
24
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
25
25
  * SPDX-License-Identifier: MIT
26
26
  */
27
27
 
@@ -34,7 +34,7 @@ declare class Theme {
34
34
 
35
35
  /**
36
36
  * MeoCord Framework
37
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
37
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
38
38
  * SPDX-License-Identifier: MIT
39
39
  */
40
40
  /**
@@ -3,7 +3,7 @@ import { Client, ActivityOptions } from 'discord.js';
3
3
 
4
4
  /**
5
5
  * MeoCord Framework
6
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
6
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
7
7
  * SPDX-License-Identifier: MIT
8
8
  */
9
9
 
@@ -124,7 +124,7 @@ declare class MeoCordApp {
124
124
 
125
125
  /**
126
126
  * MeoCord Framework
127
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
127
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
128
128
  * SPDX-License-Identifier: MIT
129
129
  */
130
130
 
@@ -6,7 +6,7 @@ import 'webpack';
6
6
 
7
7
  /**
8
8
  * MeoCord Framework
9
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
9
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
10
10
  * SPDX-License-Identifier: MIT
11
11
  */
12
12
 
@@ -31,7 +31,7 @@ declare function Service<T>(): (target: new (...args: any[]) => T) => void;
31
31
 
32
32
  /**
33
33
  * MeoCord Framework
34
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
34
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
35
35
  * SPDX-License-Identifier: MIT
36
36
  */
37
37
 
@@ -57,7 +57,7 @@ declare function CommandBuilder<T extends BuildableCommandType>(commandType: T):
57
57
 
58
58
  /**
59
59
  * MeoCord Framework
60
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
60
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
61
61
  * SPDX-License-Identifier: MIT
62
62
  */
63
63
 
@@ -231,7 +231,7 @@ declare function findAmbiguousRoutes(patterns: string[]): [string, string][];
231
231
 
232
232
  /**
233
233
  * MeoCord Framework
234
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
234
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
235
235
  * SPDX-License-Identifier: MIT
236
236
  */
237
237
 
@@ -319,7 +319,7 @@ declare function UseGuard(...guards: ((new (...args: any[]) => GuardInterface) |
319
319
 
320
320
  /**
321
321
  * MeoCord Framework
322
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
322
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
323
323
  * SPDX-License-Identifier: MIT
324
324
  */
325
325
 
@@ -2,7 +2,7 @@ export { C as CommandType, R as ReactionHandlerAction } from '../controller.enum
2
2
 
3
3
  /**
4
4
  * MeoCord Framework
5
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
5
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
6
6
  * SPDX-License-Identifier: MIT
7
7
  */
8
8
  /**
@@ -4,7 +4,7 @@ import { CommandType, ReactionHandlerAction } from '../enum/index.js';
4
4
 
5
5
  /**
6
6
  * MeoCord Framework
7
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
7
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
8
8
  * SPDX-License-Identifier: MIT
9
9
  */
10
10
 
@@ -90,7 +90,7 @@ type CommandInteractionType<CBC extends BuildableCommandType, T extends CommandB
90
90
 
91
91
  /**
92
92
  * MeoCord Framework
93
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
93
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
94
94
  * SPDX-License-Identifier: MIT
95
95
  */
96
96
 
@@ -6,7 +6,7 @@ import '../controller.enum-DYfhYaat.js';
6
6
 
7
7
  /**
8
8
  * MeoCord Framework
9
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
9
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
10
10
  * SPDX-License-Identifier: MIT
11
11
  */
12
12
 
@@ -78,7 +78,7 @@ declare class MeoCordTestingModule {
78
78
 
79
79
  /**
80
80
  * MeoCord Framework
81
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
81
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
82
82
  * SPDX-License-Identifier: MIT
83
83
  */
84
84
  interface MockResult<T = unknown> {
@@ -142,7 +142,7 @@ declare function createMockFn<T extends (...args: any[]) => any = (...args: any[
142
142
 
143
143
  /**
144
144
  * MeoCord Framework
145
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
145
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
146
146
  * SPDX-License-Identifier: MIT
147
147
  */
148
148
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "meocord",
3
3
  "description": "Decorator-based Discord bot framework built on discord.js. Brings NestJS-style controllers, dependency injection, guards, and testing utilities to bot development — with a full CLI and TypeScript-first design.",
4
- "version": "3.2.0",
4
+ "version": "3.2.2",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22"
@@ -16,7 +16,10 @@
16
16
  "verify:generated": "bun scripts/verify-generated.ts",
17
17
  "test:watch": "vitest",
18
18
  "test:coverage": "vitest run --coverage",
19
- "test:typecheck": "tsc --noEmit --project tsconfig.test.json"
19
+ "test:typecheck": "tsc --noEmit --project tsconfig.test.json",
20
+ "changeset": "changeset",
21
+ "release:version": "changeset version",
22
+ "release:publish": "changeset publish"
20
23
  },
21
24
  "bin": "./dist/esm/bin/meocord.js",
22
25
  "repository": {
@@ -74,11 +77,12 @@
74
77
  "package.json",
75
78
  "LICENSE",
76
79
  "AUTHOR.md",
80
+ "CHANGELOG.md",
77
81
  "README.md"
78
82
  ],
79
83
  "dependencies": {
80
- "@clack/prompts": "^1.7.0",
81
- "@swc/core": "1.16.1",
84
+ "@clack/prompts": "^1.8.0",
85
+ "@swc/core": "1.16.2",
82
86
  "chalk": "^6.0.0",
83
87
  "cli-table3": "^0.6.5",
84
88
  "commander": "^15.0.0",
@@ -91,40 +95,38 @@
91
95
  "swc-loader": "^0.2.7",
92
96
  "terser-webpack-plugin": "^5.6.1",
93
97
  "tsconfig-paths-webpack-plugin": "^4.2.0",
94
- "webpack": "^5.109.2",
98
+ "webpack": "^5.110.3",
95
99
  "webpack-node-externals": "^3.0.0"
96
100
  },
97
101
  "devDependencies": {
102
+ "@changesets/changelog-github": "^1.0.1",
103
+ "@changesets/cli": "^3.0.2",
98
104
  "@eslint/js": "^10.0.1",
99
105
  "@rollup/plugin-alias": "^6.0.0",
100
106
  "@rollup/plugin-json": "^6.1.0",
101
107
  "@rollup/plugin-node-resolve": "^16.0.3",
102
108
  "@rollup/plugin-swc": "^0.4.1",
103
- "@semantic-release/commit-analyzer": "^13.0.1",
104
- "@semantic-release/github": "^12.0.9",
105
- "@semantic-release/release-notes-generator": "^14.1.1",
106
109
  "@types/lodash-es": "^4.17.12",
107
110
  "@types/webpack-node-externals": "^3.0.4",
108
- "@typescript-eslint/parser": "^8.67.0",
111
+ "@typescript-eslint/parser": "^8.70.0",
109
112
  "discord.js": "^14.27.0",
110
- "eslint": "^10.9.0",
113
+ "eslint": "^10.10.0",
111
114
  "eslint-config-prettier": "^10.1.8",
112
115
  "eslint-plugin-headers": "^1.3.4",
113
116
  "eslint-plugin-import-x": "^4.17.1",
114
117
  "eslint-plugin-prettier": "^5.5.6",
115
118
  "eslint-plugin-unused-imports": "^4.4.1",
116
- "globals": "^17.11.0",
119
+ "globals": "^17.12.0",
117
120
  "husky": "^9.1.7",
118
121
  "prettier": "^3.9.6",
119
- "rollup": "^4.62.5",
122
+ "rollup": "^4.63.1",
120
123
  "rollup-plugin-copy": "^3.5.0",
121
124
  "rollup-plugin-dts": "^6.5.1",
122
- "semantic-release": "^25.0.9",
123
125
  "typescript": "^6.0.3",
124
- "typescript-eslint": "^8.67.0",
125
- "@vitest/coverage-istanbul": "^4.1.11",
126
- "unplugin-swc": "^1.5.11",
127
- "vitest": "^4.1.11"
126
+ "typescript-eslint": "^8.70.0",
127
+ "@vitest/coverage-istanbul": "^5.0.0",
128
+ "unplugin-swc": "^1.6.0",
129
+ "vitest": "^5.0.0"
128
130
  },
129
131
  "peerDependencies": {
130
132
  "discord.js": "^14.26.4",
package/webpack.config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MeoCord Framework
3
- * Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ * Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6