commandkit 1.2.1-dev.20260411010608 → 1.2.1-dev.20260411144916

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 (148) hide show
  1. package/dist/CommandTree-DwfiCsOl.d.ts +55 -0
  2. package/dist/{CommandsRouter-DiNoF0dq.d.ts → CommandsRouter-D-b3xv38.d.ts} +102 -25
  3. package/dist/{EventsRouter-EuuSu6NH.d.ts → EventsRouter-Dyr_oJJD.d.ts} +1 -1
  4. package/dist/{MessageCommandParser-DPCbHXCS.d.ts → MessageCommandParser-UTKsRvb9.d.ts} +7 -1
  5. package/dist/analytics/analytics-engine.d.ts +1 -1
  6. package/dist/analytics/analytics-engine.js +1 -1
  7. package/dist/analytics/analytics-provider.d.ts +1 -1
  8. package/dist/analytics/utils.js +1 -1
  9. package/dist/{analytics-engine-uBNaD2CQ.d.ts → analytics-engine-BYA0ongA.d.ts} +124 -18
  10. package/dist/app/commands/AppCommandRunner.d.ts +1 -1
  11. package/dist/app/commands/AppCommandRunner.js +1 -1
  12. package/dist/app/commands/Context.d.ts +1 -1
  13. package/dist/app/commands/Context.js +1 -1
  14. package/dist/app/commands/MessageCommandParser.d.ts +1 -1
  15. package/dist/app/commands/MessageCommandParser.js +75 -70
  16. package/dist/app/commands/MessageCommandParser.js.map +1 -1
  17. package/dist/app/commands/helpers.d.ts +1 -1
  18. package/dist/app/events/EventWorkerContext.d.ts +2 -2
  19. package/dist/app/handlers/AppCommandHandler.d.ts +1 -1
  20. package/dist/app/handlers/AppCommandHandler.js +1 -1
  21. package/dist/app/handlers/AppEventsHandler.d.ts +1 -1
  22. package/dist/app/handlers/AppEventsHandler.js +1 -1
  23. package/dist/app/index.d.ts +4 -4
  24. package/dist/app/index.js +1 -1
  25. package/dist/app/interrupt/signals.d.ts +1 -1
  26. package/dist/app/middlewares/permissions.d.ts +1 -1
  27. package/dist/app/middlewares/permissions.js +1 -1
  28. package/dist/app/register/CommandRegistrar.d.ts +1 -1
  29. package/dist/app/register/CommandRegistrar.js +1 -1
  30. package/dist/app/router/CommandTree.d.ts +2 -0
  31. package/dist/app/router/CommandTree.js +1 -0
  32. package/dist/app/router/CommandsRouter.d.ts +1 -1
  33. package/dist/app/router/CommandsRouter.js +385 -23
  34. package/dist/app/router/CommandsRouter.js.map +1 -1
  35. package/dist/app/router/EventsRouter.d.ts +1 -1
  36. package/dist/app/router/index.d.ts +4 -3
  37. package/dist/checkbox-CvQGiyRe.d.ts +28 -0
  38. package/dist/cli/app-process.d.ts +2 -1
  39. package/dist/cli/app-process.js +15 -3
  40. package/dist/cli/app-process.js.map +1 -1
  41. package/dist/cli/build.d.ts +1 -1
  42. package/dist/cli/build.js +4 -2
  43. package/dist/cli/build.js.map +1 -1
  44. package/dist/cli/common.d.ts +1 -1
  45. package/dist/cli/development.d.ts +3 -3
  46. package/dist/cli/development.js +2 -2
  47. package/dist/cli/development.js.map +1 -1
  48. package/dist/cli/init.js +1 -1
  49. package/dist/cli/production.js +4 -3
  50. package/dist/cli/production.js.map +1 -1
  51. package/dist/{commandkit-DNln6BAJ.js → commandkit-DTqT800J.js} +555 -111
  52. package/dist/commandkit-DTqT800J.js.map +1 -0
  53. package/dist/commandkit.d.ts +1 -1
  54. package/dist/commandkit.js +1 -1
  55. package/dist/components/common/element.js +1 -1
  56. package/dist/components/display/common.js +1 -1
  57. package/dist/components/display/container.d.ts +1 -1
  58. package/dist/components/display/container.js +1 -1
  59. package/dist/components/display/file.d.ts +1 -1
  60. package/dist/components/display/index.d.ts +8 -8
  61. package/dist/components/display/index.js +1 -1
  62. package/dist/components/display/label.d.ts +1 -1
  63. package/dist/components/display/label.js +1 -1
  64. package/dist/components/display/media-gallery.d.ts +1 -1
  65. package/dist/components/display/media-gallery.js +1 -1
  66. package/dist/components/display/poll.d.ts +1 -1
  67. package/dist/components/display/section.d.ts +1 -1
  68. package/dist/components/display/section.js +1 -1
  69. package/dist/components/display/separator.d.ts +1 -1
  70. package/dist/components/display/text-display.d.ts +1 -1
  71. package/dist/components/index.d.ts +11 -9
  72. package/dist/components/index.js +6 -1
  73. package/dist/components/interactive/button/Button.js +1 -1
  74. package/dist/components/interactive/button/ButtonKit.js +1 -1
  75. package/dist/components/interactive/checkbox/checkbox.d.ts +2 -0
  76. package/dist/components/interactive/checkbox/checkbox.js +6 -0
  77. package/dist/components/interactive/modal/Modal.js +1 -1
  78. package/dist/components/interactive/modal/ModalKit.js +1 -1
  79. package/dist/components/interactive/radio/radio.d.ts +2 -0
  80. package/dist/components/interactive/radio/radio.js +5 -0
  81. package/dist/components/interactive/select-menu/ChannelSelectMenuKit.js +1 -1
  82. package/dist/components/interactive/select-menu/MentionableSelectMenuKit.js +1 -1
  83. package/dist/components/interactive/select-menu/RoleSelectMenuKit.js +1 -1
  84. package/dist/components/interactive/select-menu/SelectMenu.js +1 -1
  85. package/dist/components/interactive/select-menu/StringSelectMenuKit.js +1 -1
  86. package/dist/components/interactive/select-menu/UserSelectMenuKit.js +1 -1
  87. package/dist/config/config.d.ts +1 -1
  88. package/dist/config/config.js +1 -1
  89. package/dist/config/default.d.ts +1 -1
  90. package/dist/config/default.js +1 -1
  91. package/dist/config/loader.d.ts +1 -1
  92. package/dist/config/loader.js +1 -1
  93. package/dist/config/types.d.ts +2 -2
  94. package/dist/config/utils.d.ts +1 -1
  95. package/dist/{constants-DqtJY0t-.d.ts → constants-D_sgdBHy.d.ts} +1 -1
  96. package/dist/{constants-DwxpkIeH.d.ts → constants-GWSfiS4h.d.ts} +1 -1
  97. package/dist/{container-CYojOA9d.d.ts → container-B7Kmw__R.d.ts} +1 -1
  98. package/dist/context/async-context.d.ts +1 -1
  99. package/dist/context/async-context.js +1 -1
  100. package/dist/context/environment.d.ts +1 -1
  101. package/dist/context/environment.js +1 -1
  102. package/dist/events/CommandKitEventsChannel.d.ts +1 -1
  103. package/dist/{file-DLO1zEcH.d.ts → file-BP8aoN3A.d.ts} +1 -1
  104. package/dist/flags/FlagProvider.d.ts +1 -1
  105. package/dist/flags/feature-flags.d.ts +1 -1
  106. package/dist/flags/feature-flags.js +1 -1
  107. package/dist/flags/store.d.ts +1 -1
  108. package/dist/{helpers-zmC44Mcu.d.ts → helpers-C5jRrAM1.d.ts} +1 -1
  109. package/dist/index.d.ts +19 -16
  110. package/dist/index.js +6 -1
  111. package/dist/kv/constants.d.ts +1 -1
  112. package/dist/kv/serde.d.ts +1 -1
  113. package/dist/{label-DqGRqodo.d.ts → label-CqHiBMXP.d.ts} +1 -1
  114. package/dist/logger/DefaultLogger.js +1 -1
  115. package/dist/logger/Logger.js +1 -1
  116. package/dist/{media-gallery-Ca8es-eB.d.ts → media-gallery-hFfa57Kd.d.ts} +1 -1
  117. package/dist/plugins/CompilerPlugin.d.ts +1 -1
  118. package/dist/plugins/PluginCommon.d.ts +1 -1
  119. package/dist/plugins/RuntimePlugin.d.ts +1 -1
  120. package/dist/plugins/index.d.ts +1 -1
  121. package/dist/plugins/index.js +1 -1
  122. package/dist/plugins/plugin-runtime/CommandKitPluginRuntime.d.ts +1 -1
  123. package/dist/plugins/plugin-runtime/CommandKitPluginRuntime.js +1 -1
  124. package/dist/plugins/plugin-runtime/CompilerPluginRuntime.d.ts +1 -1
  125. package/dist/plugins/plugin-runtime/CompilerPluginRuntime.js +1 -1
  126. package/dist/plugins/plugin-runtime/builtin/CommonDirectiveTransformer.d.ts +1 -1
  127. package/dist/plugins/plugin-runtime/builtin/CommonDirectiveTransformer.js +1 -1
  128. package/dist/plugins/plugin-runtime/builtin/MacroPlugin.d.ts +1 -1
  129. package/dist/plugins/plugin-runtime/builtin/MacroPlugin.js +1 -1
  130. package/dist/plugins/plugin-runtime/runtime.d.ts +1 -1
  131. package/dist/plugins/types.d.ts +1 -1
  132. package/dist/{poll-DQ6DX_Tt.d.ts → poll-ElAd5Z2K.d.ts} +1 -1
  133. package/dist/radio-Dcd4YIZx.d.ts +21 -0
  134. package/dist/{section-Q8nwx_-q.d.ts → section-BqDLXlp3.d.ts} +1 -1
  135. package/dist/{separator-B8P-kdIK.d.ts → separator-DSaCa1Tc.d.ts} +1 -1
  136. package/dist/{signals-CBXwugBW.d.ts → signals-DVOXPkQK.d.ts} +1 -1
  137. package/dist/{text-display-CL9C2yMc.d.ts → text-display-D-0dD7bR.d.ts} +1 -1
  138. package/dist/types.d.ts +1 -1
  139. package/dist/utils/constants.d.ts +1 -1
  140. package/dist/utils/dev-hooks.d.ts +1 -1
  141. package/dist/utils/dev-hooks.js +1 -1
  142. package/dist/utils/utilities.js +1 -1
  143. package/dist/version.js +1 -1
  144. package/package.json +3 -3
  145. package/dist/commandkit-DNln6BAJ.js.map +0 -1
  146. /package/dist/{index-BIsCUWAs.d.ts → index-CQiOqvUC.d.ts} +0 -0
  147. /package/dist/{index-CwHYpK-f.d.ts → index-CTLjHn5u.d.ts} +0 -0
  148. /package/dist/{index-DHrsNvX1.d.ts → index-LKrIp3Oo.d.ts} +0 -0
@@ -631,20 +631,30 @@ function findAppDirectory() {
631
631
  */
632
632
  function debounce(fn, ms) {
633
633
  let timer = null;
634
- let resolve = null;
634
+ let sharedPromise = null;
635
+ let sharedResolve = null;
636
+ let sharedReject = null;
635
637
  return ((...args) => {
636
- if (timer) {
637
- clearTimeout(timer);
638
- if (resolve) resolve(null);
639
- }
640
- return new Promise((res) => {
641
- resolve = res;
642
- timer = setTimeout(() => {
643
- res(fn(...args));
644
- timer = null;
645
- resolve = null;
646
- }, ms);
638
+ if (timer) clearTimeout(timer);
639
+ if (!sharedPromise) sharedPromise = new Promise((res, rej) => {
640
+ sharedResolve = res;
641
+ sharedReject = rej;
647
642
  });
643
+ timer = setTimeout(async () => {
644
+ const currentResolve = sharedResolve;
645
+ const currentReject = sharedReject;
646
+ timer = null;
647
+ sharedPromise = null;
648
+ sharedResolve = null;
649
+ sharedReject = null;
650
+ try {
651
+ const result = await fn(...args);
652
+ if (currentResolve) currentResolve(result);
653
+ } catch (err) {
654
+ if (currentReject) currentReject(err);
655
+ }
656
+ }, ms);
657
+ return sharedPromise;
648
658
  });
649
659
  }
650
660
  /**
@@ -1166,7 +1176,8 @@ const defaultConfig = {
1166
1176
  development: true,
1167
1177
  production: false
1168
1178
  },
1169
- jsxDefaultOptionalComponents: true
1179
+ jsxDefaultOptionalComponents: true,
1180
+ experimental: { devServerRuntime: null }
1170
1181
  };
1171
1182
 
1172
1183
  //#endregion
@@ -1222,7 +1233,11 @@ function defineConfig(config = {}) {
1222
1233
  ...config.antiCrashScript
1223
1234
  },
1224
1235
  disablePermissionsMiddleware: config.disablePermissionsMiddleware ?? defaultConfig.disablePermissionsMiddleware,
1225
- jsxDefaultOptionalComponents: config.jsxDefaultOptionalComponents ?? defaultConfig.jsxDefaultOptionalComponents
1236
+ jsxDefaultOptionalComponents: config.jsxDefaultOptionalComponents ?? defaultConfig.jsxDefaultOptionalComponents,
1237
+ experimental: {
1238
+ ...defaultConfig.experimental,
1239
+ ...config.experimental
1240
+ }
1226
1241
  };
1227
1242
  return defined;
1228
1243
  }
@@ -1579,9 +1594,12 @@ var Context = class Context {
1579
1594
  * Gets the name of the current command.
1580
1595
  */
1581
1596
  get commandName() {
1597
+ const routeKey = this.command.data.command.__routeKey;
1598
+ if (typeof routeKey === "string" && routeKey.length) return routeKey;
1599
+ if (this.command.data.command.name) return this.command.data.command.name;
1582
1600
  if (this.isInteraction()) return this.interaction.commandName;
1583
- const maybeAlias = this.config.messageCommandParser.getCommand();
1584
- return this.commandkit.commandHandler.resolveMessageCommandName(maybeAlias);
1601
+ const parser = this.config.messageCommandParser;
1602
+ return this.commandkit.commandHandler.resolveMessageCommandName(parser.getFullCommand());
1585
1603
  }
1586
1604
  /**
1587
1605
  * Gets the invoked command name (could be an alias for message commands)
@@ -1675,7 +1693,7 @@ var Context = class Context {
1675
1693
  */
1676
1694
  getCommandIdentifier() {
1677
1695
  if (this.isInteraction()) return this.interaction.commandName;
1678
- else return this.message.content.split(" ")[0].slice(1);
1696
+ else return this.commandName;
1679
1697
  }
1680
1698
  /**
1681
1699
  * Returns the locale of the guild where this command was triggered.
@@ -1787,7 +1805,7 @@ var AppCommandRunner = class {
1787
1805
  const env = new CommandKitEnvironment(commandkit);
1788
1806
  env.setType(CommandKitEnvironmentType.CommandHandler);
1789
1807
  env.variables.set("commandHandlerType", "app");
1790
- env.variables.set("currentCommandName", prepared.command.command.name);
1808
+ env.variables.set("currentCommandName", prepared.command.data.command.__routeKey ?? prepared.command.command.name);
1791
1809
  env.variables.set("execHandlerKind", executionMode);
1792
1810
  env.variables.set("customHandler", (options === null || options === void 0 ? void 0 : options.handler) ?? null);
1793
1811
  try {
@@ -1827,15 +1845,15 @@ var AppCommandRunner = class {
1827
1845
  if (fn) try {
1828
1846
  const _executeCommand = makeContextAwareFunction(env, async () => {
1829
1847
  env.registerDeferredFunction(async (env) => {
1830
- var _prepared$command2;
1848
+ var _prepared$command3, _prepared$command4;
1831
1849
  env.markEnd();
1832
1850
  const error = env.getExecutionError();
1833
1851
  const marker = env.getMarker();
1834
1852
  const time = `${env.getExecutionTime().toFixed(2)}ms`;
1835
1853
  if (error) {
1836
- var _prepared$command;
1854
+ var _prepared$command, _prepared$command2;
1837
1855
  Logger.error`[${marker} - ${time}] Error executing command: ${error}`;
1838
- const commandName = ((_prepared$command = prepared.command) === null || _prepared$command === void 0 || (_prepared$command = _prepared$command.data) === null || _prepared$command === void 0 || (_prepared$command = _prepared$command.command) === null || _prepared$command === void 0 ? void 0 : _prepared$command.name) ?? prepared.command.command.name;
1856
+ const commandName = ((_prepared$command = prepared.command) === null || _prepared$command === void 0 || (_prepared$command = _prepared$command.data) === null || _prepared$command === void 0 || (_prepared$command = _prepared$command.command) === null || _prepared$command === void 0 ? void 0 : _prepared$command.__routeKey) ?? ((_prepared$command2 = prepared.command) === null || _prepared$command2 === void 0 || (_prepared$command2 = _prepared$command2.data) === null || _prepared$command2 === void 0 || (_prepared$command2 = _prepared$command2.command) === null || _prepared$command2 === void 0 ? void 0 : _prepared$command2.name) ?? prepared.command.command.name;
1839
1857
  await analytics.track({
1840
1858
  name: require_analytics_constants.AnalyticsEvents.COMMAND_EXECUTION,
1841
1859
  id: commandName,
@@ -1849,7 +1867,7 @@ var AppCommandRunner = class {
1849
1867
  return;
1850
1868
  }
1851
1869
  Logger.info(`[${marker} - ${time}] Command executed successfully`);
1852
- const commandName = ((_prepared$command2 = prepared.command) === null || _prepared$command2 === void 0 || (_prepared$command2 = _prepared$command2.data) === null || _prepared$command2 === void 0 || (_prepared$command2 = _prepared$command2.command) === null || _prepared$command2 === void 0 ? void 0 : _prepared$command2.name) ?? prepared.command.command.name;
1870
+ const commandName = ((_prepared$command3 = prepared.command) === null || _prepared$command3 === void 0 || (_prepared$command3 = _prepared$command3.data) === null || _prepared$command3 === void 0 || (_prepared$command3 = _prepared$command3.command) === null || _prepared$command3 === void 0 ? void 0 : _prepared$command3.__routeKey) ?? ((_prepared$command4 = prepared.command) === null || _prepared$command4 === void 0 || (_prepared$command4 = _prepared$command4.data) === null || _prepared$command4 === void 0 || (_prepared$command4 = _prepared$command4.command) === null || _prepared$command4 === void 0 ? void 0 : _prepared$command4.name) ?? prepared.command.command.name;
1853
1871
  await analytics.track({
1854
1872
  name: require_analytics_constants.AnalyticsEvents.COMMAND_EXECUTION,
1855
1873
  id: commandName,
@@ -1864,7 +1882,7 @@ var AppCommandRunner = class {
1864
1882
  return fn(middlewareCtx.clone());
1865
1883
  }, this.#finalizer.bind(this));
1866
1884
  const executeCommand = runCommand != null ? runCommand(_executeCommand) : _executeCommand;
1867
- env.markStart(prepared.command.data.command.name);
1885
+ env.markStart(prepared.command.data.command.__routeKey ?? prepared.command.data.command.name);
1868
1886
  if (!await commandkit.plugins.execute(async (ctx, plugin) => {
1869
1887
  return plugin.executeCommand(ctx, env, source, prepared, executeCommand);
1870
1888
  })) result = await executeCommand();
@@ -2024,11 +2042,17 @@ var CommandRegistrar = class {
2024
2042
  * Gets the commands data, consuming pre-generated context menu entries when available.
2025
2043
  */
2026
2044
  getCommandsData() {
2045
+ return [...this.getFlatCommandsData(), ...this.getHierarchicalCommandsData()];
2046
+ }
2047
+ /**
2048
+ * Gets flat command data, consuming pre-generated context menu entries when available.
2049
+ */
2050
+ getFlatCommandsData() {
2027
2051
  const commands = this.commandkit.commandHandler.getCommandsArray();
2028
2052
  const commandIds = new Set(commands.map((command) => command.command.id));
2029
2053
  return commands.flatMap((cmd) => {
2030
2054
  const isPreGeneratedCtx = cmd.command.id.endsWith("::user-ctx") || cmd.command.id.endsWith("::message-ctx");
2031
- const json = "toJSON" in cmd.data.command ? cmd.data.command.toJSON() : cmd.data.command;
2055
+ const json = this.sanitizeCommandData("toJSON" in cmd.data.command ? cmd.data.command.toJSON() : cmd.data.command);
2032
2056
  const __metadata = cmd.metadata ?? cmd.data.metadata;
2033
2057
  const isContextMenuType = json.type === discord_js.ApplicationCommandType.User || json.type === discord_js.ApplicationCommandType.Message;
2034
2058
  const applyId = (id) => {
@@ -2079,6 +2103,119 @@ var CommandRegistrar = class {
2079
2103
  });
2080
2104
  }
2081
2105
  /**
2106
+ * Gets hierarchical chat-input command payloads compiled from cached tree nodes.
2107
+ */
2108
+ getHierarchicalCommandsData() {
2109
+ const router = this.commandkit.commandsRouter;
2110
+ if (!router) return [];
2111
+ const { treeNodes } = router.getData();
2112
+ const hierarchicalNodes = new Map(this.commandkit.commandHandler.getHierarchicalNodesArray().map((node) => [node.command.id, node]));
2113
+ const rootNodes = Array.from(treeNodes.values()).filter((node) => {
2114
+ return node.source !== "flat" && node.kind === "command" && node.route.length === 1;
2115
+ });
2116
+ const commands = [];
2117
+ for (const rootNode of rootNodes) {
2118
+ const payload = this.buildHierarchicalRootPayload(rootNode.id, treeNodes, hierarchicalNodes);
2119
+ if (payload) commands.push(payload);
2120
+ }
2121
+ return commands;
2122
+ }
2123
+ /**
2124
+ * Removes internal runtime-only fields before Discord registration data is emitted.
2125
+ */
2126
+ sanitizeCommandData(command) {
2127
+ const { __routeKey, ...json } = command;
2128
+ return json;
2129
+ }
2130
+ /**
2131
+ * Builds a top-level Discord payload for a hierarchical command root.
2132
+ */
2133
+ buildHierarchicalRootPayload(rootNodeId, treeNodes, hierarchicalNodes) {
2134
+ const rootNode = treeNodes.get(rootNodeId);
2135
+ const rootLoaded = hierarchicalNodes.get(rootNodeId);
2136
+ if (!rootNode || !rootLoaded) return null;
2137
+ const rootJson = this.sanitizeCommandData("toJSON" in rootLoaded.data.command ? rootLoaded.data.command.toJSON() : rootLoaded.data.command);
2138
+ if (rootNode.executable) {
2139
+ if (!rootLoaded.data.chatInput) return null;
2140
+ return {
2141
+ ...rootJson,
2142
+ type: discord_js.ApplicationCommandType.ChatInput,
2143
+ description: rootJson.description ?? "No command description set.",
2144
+ __metadata: rootLoaded.metadata ?? rootLoaded.data.metadata,
2145
+ __applyId: (id) => {
2146
+ rootLoaded.discordId = id;
2147
+ }
2148
+ };
2149
+ }
2150
+ const options = rootNode.childIds.map((childId) => this.buildHierarchicalOption(childId, treeNodes, hierarchicalNodes)).filter(Boolean);
2151
+ if (!options.length) return null;
2152
+ const scopeKeys = new Set(this.collectHierarchicalGuildScopes(rootNode.childIds, treeNodes, hierarchicalNodes));
2153
+ if (scopeKeys.size > 1) {
2154
+ Logger.error(`Failed to register hierarchical command "${rootJson.name ?? rootNode.token}": all chat-input leaves under the same root must use the same guild scope in v1.`);
2155
+ return null;
2156
+ }
2157
+ const scopeKey = scopeKeys.values().next().value;
2158
+ const scopeGuilds = scopeKey ? scopeKey.split(",").filter(Boolean) : [];
2159
+ const metadata = {
2160
+ ...rootLoaded.metadata ?? rootLoaded.data.metadata,
2161
+ guilds: scopeGuilds.length ? scopeGuilds : void 0
2162
+ };
2163
+ return {
2164
+ ...rootJson,
2165
+ type: discord_js.ApplicationCommandType.ChatInput,
2166
+ description: rootJson.description ?? "No command description set.",
2167
+ options,
2168
+ __metadata: metadata,
2169
+ __applyId: (id) => {
2170
+ rootLoaded.discordId = id;
2171
+ }
2172
+ };
2173
+ }
2174
+ /**
2175
+ * Builds a nested subcommand or subcommand-group option from a hierarchical node.
2176
+ */
2177
+ buildHierarchicalOption(nodeId, treeNodes, hierarchicalNodes) {
2178
+ const node = treeNodes.get(nodeId);
2179
+ const loadedNode = hierarchicalNodes.get(nodeId);
2180
+ if (!node || !loadedNode) return null;
2181
+ const json = this.sanitizeCommandData("toJSON" in loadedNode.data.command ? loadedNode.data.command.toJSON() : loadedNode.data.command);
2182
+ if (node.kind === "group") {
2183
+ const options = node.childIds.map((childId) => this.buildHierarchicalOption(childId, treeNodes, hierarchicalNodes)).filter(Boolean);
2184
+ if (!options.length) return null;
2185
+ return {
2186
+ ...json,
2187
+ type: discord_js.ApplicationCommandOptionType.SubcommandGroup,
2188
+ description: json.description ?? "No command description set.",
2189
+ options
2190
+ };
2191
+ }
2192
+ if (!node.executable || !loadedNode.data.chatInput) return null;
2193
+ return {
2194
+ ...json,
2195
+ type: discord_js.ApplicationCommandOptionType.Subcommand,
2196
+ description: json.description ?? "No command description set."
2197
+ };
2198
+ }
2199
+ /**
2200
+ * Collects normalized guild scopes for all chat-input leaves within a hierarchical subtree.
2201
+ */
2202
+ collectHierarchicalGuildScopes(nodeIds, treeNodes, hierarchicalNodes) {
2203
+ const scopes = [];
2204
+ for (const nodeId of nodeIds) {
2205
+ var _loadedNode$metadata;
2206
+ const node = treeNodes.get(nodeId);
2207
+ const loadedNode = hierarchicalNodes.get(nodeId);
2208
+ if (!node || !loadedNode) continue;
2209
+ if (node.kind === "group") {
2210
+ scopes.push(...this.collectHierarchicalGuildScopes(node.childIds, treeNodes, hierarchicalNodes));
2211
+ continue;
2212
+ }
2213
+ if (!node.executable || !loadedNode.data.chatInput) continue;
2214
+ scopes.push((((_loadedNode$metadata = loadedNode.metadata) === null || _loadedNode$metadata === void 0 ? void 0 : _loadedNode$metadata.guilds) ?? []).filter(Boolean).slice().sort().join(","));
2215
+ }
2216
+ return scopes;
2217
+ }
2218
+ /**
2082
2219
  * Registers loaded commands.
2083
2220
  */
2084
2221
  async register() {
@@ -2249,6 +2386,20 @@ var AppCommandHandler = class {
2249
2386
  */
2250
2387
  loadedCommands = new discord_js.Collection();
2251
2388
  /**
2389
+ * Executable runtime commands indexed by canonical route key.
2390
+ * This includes flat commands and hierarchical executable leaves.
2391
+ * @private
2392
+ * @internal
2393
+ */
2394
+ runtimeRouteIndex = new discord_js.Collection();
2395
+ /**
2396
+ * Loaded hierarchical command nodes keyed by tree node id.
2397
+ * Container nodes are cached here for registration compilation.
2398
+ * @private
2399
+ * @internal
2400
+ */
2401
+ hierarchicalNodes = new discord_js.Collection();
2402
+ /**
2252
2403
  * @private
2253
2404
  * @internal
2254
2405
  */
@@ -2291,31 +2442,54 @@ var AppCommandHandler = class {
2291
2442
  * Prints a formatted banner showing all loaded commands organized by category.
2292
2443
  */
2293
2444
  printBanner() {
2445
+ var _this$commandkit$comm;
2294
2446
  const uncategorized = crypto.randomUUID();
2295
- const categorizedCommands = this.getCommandsArray().reduce((acc, cmd) => {
2296
- const category = cmd.command.category || uncategorized;
2297
- acc[category] = acc[category] || [];
2298
- acc[category].push(cmd);
2299
- return acc;
2300
- }, {});
2301
- console.log(require_utils_colors.default.green(`Loaded ${require_utils_colors.default.magenta(this.loadedCommands.size.toString())} commands:`));
2302
- const categories = Object.keys(categorizedCommands).sort();
2447
+ const flatCommands = this.getCommandsArray();
2448
+ const treeNodes = Array.from(((_this$commandkit$comm = this.commandkit.commandsRouter) === null || _this$commandkit$comm === void 0 ? void 0 : _this$commandkit$comm.getData().treeNodes.values()) ?? []);
2449
+ const hierarchicalRoots = treeNodes.filter((n) => n.kind === "command" && n.source !== "root");
2450
+ const totalCount = flatCommands.length + hierarchicalRoots.length;
2451
+ console.log(require_utils_colors.default.green(`Loaded ${require_utils_colors.default.magenta(totalCount.toString())} commands:`));
2452
+ const printHierarchicalNode = (nodeId, prefix, indent) => {
2453
+ const node = treeNodes.find((n) => n.id === nodeId);
2454
+ if (!node || node.kind === "root") return;
2455
+ const loadedNode = this.hierarchicalNodes.get(nodeId);
2456
+ const hasMw = loadedNode && loadedNode.command.middlewares.length > 0 ? require_utils_colors.default.magenta(" (λ)") : "";
2457
+ const kindLabel = node.kind === "group" ? require_utils_colors.default.cyan(` [group]`) : node.kind === "command" ? "" : "";
2458
+ console.log(`${require_utils_colors.default.green(prefix)} ${require_utils_colors.default.yellow(node.token)}${kindLabel}${hasMw}`);
2459
+ const children = node.childIds;
2460
+ children.forEach((childId, idx) => {
2461
+ const isLastChild = idx === children.length - 1;
2462
+ printHierarchicalNode(childId, indent + (isLastChild ? "└─" : "├─"), indent + (isLastChild ? " " : "│ "));
2463
+ });
2464
+ };
2465
+ const categoryBuckets = {};
2466
+ const ensureBucket = (cat) => {
2467
+ categoryBuckets[cat] ??= {
2468
+ flat: [],
2469
+ hierarchical: []
2470
+ };
2471
+ };
2472
+ for (const cmd of flatCommands) {
2473
+ const cat = cmd.command.category || uncategorized;
2474
+ ensureBucket(cat);
2475
+ categoryBuckets[cat].flat.push(cmd);
2476
+ }
2477
+ for (const root of hierarchicalRoots) {
2478
+ const cat = root.category || uncategorized;
2479
+ ensureBucket(cat);
2480
+ categoryBuckets[cat].hierarchical.push(root);
2481
+ }
2482
+ const categories = Object.keys(categoryBuckets).sort();
2303
2483
  const categoryTree = {};
2304
2484
  categories.forEach((category) => {
2305
- if (category === uncategorized) return;
2306
- if (category.includes(":")) {
2307
- const parts = category.split(":");
2308
- let bestParent = null;
2309
- for (let i = parts.length - 1; i > 0; i--) {
2310
- const potentialParent = parts.slice(0, i).join(":");
2311
- if (categories.includes(potentialParent)) {
2312
- bestParent = potentialParent;
2313
- break;
2314
- }
2315
- }
2316
- if (bestParent) {
2317
- categoryTree[bestParent] = categoryTree[bestParent] || [];
2318
- categoryTree[bestParent].push(category);
2485
+ if (category === uncategorized || !category.includes(":")) return;
2486
+ const parts = category.split(":");
2487
+ for (let i = parts.length - 1; i > 0; i--) {
2488
+ const potentialParent = parts.slice(0, i).join(":");
2489
+ if (categories.includes(potentialParent)) {
2490
+ categoryTree[potentialParent] ??= [];
2491
+ categoryTree[potentialParent].push(category);
2492
+ break;
2319
2493
  }
2320
2494
  }
2321
2495
  });
@@ -2323,20 +2497,37 @@ var AppCommandHandler = class {
2323
2497
  const printCategory = (category, indent = "", isLast = false, parentPrefix = "") => {
2324
2498
  if (processedCategories.has(category)) return;
2325
2499
  processedCategories.add(category);
2326
- const commands = categorizedCommands[category];
2500
+ const bucket = categoryBuckets[category];
2327
2501
  const hasChildren = categoryTree[category] && categoryTree[category].length > 0;
2502
+ const allEntries = [...bucket.flat, ...bucket.hierarchical];
2328
2503
  const thisPrefix = isLast ? "└─" : "├─";
2329
2504
  const childIndent = parentPrefix + (isLast ? " " : "│ ");
2330
2505
  if (category !== uncategorized) {
2331
2506
  const displayName = category.includes(":") ? category.split(":").pop() : category;
2332
2507
  console.log(require_utils_colors.default.cyan(`${indent}${thisPrefix} ${require_utils_colors.default.bold(displayName)}`));
2333
2508
  }
2334
- commands.forEach((cmd, cmdIndex) => {
2335
- const cmdPrefix = cmdIndex === commands.length - 1 && !hasChildren ? "└─" : "├─";
2336
- const cmdIndent = category !== uncategorized ? childIndent : indent;
2509
+ const cmdIndent = category !== uncategorized ? childIndent : indent;
2510
+ const totalEntries = allEntries.length;
2511
+ let entryIndex = 0;
2512
+ bucket.flat.forEach((cmd) => {
2513
+ const cmdPrefix = entryIndex === totalEntries - 1 && !hasChildren ? "└─" : "├─";
2337
2514
  const name = cmd.data.command.name;
2338
2515
  const middlewareIcon = cmd.command.middlewares.length > 0 ? require_utils_colors.default.magenta(" (λ)") : "";
2339
2516
  console.log(`${require_utils_colors.default.green(`${cmdIndent}${cmdPrefix}`)} ${require_utils_colors.default.yellow(name)}${middlewareIcon}`);
2517
+ entryIndex++;
2518
+ });
2519
+ bucket.hierarchical.forEach((root) => {
2520
+ const isLastEntry = entryIndex === totalEntries - 1 && !hasChildren;
2521
+ const rootPrefix = cmdIndent + (isLastEntry ? "└─" : "├─");
2522
+ const rootChildIndent = cmdIndent + (isLastEntry ? " " : "│ ");
2523
+ const loadedNode = this.hierarchicalNodes.get(root.id);
2524
+ const hasMw = loadedNode && loadedNode.command.middlewares.length > 0 ? require_utils_colors.default.magenta(" (λ)") : "";
2525
+ console.log(`${require_utils_colors.default.green(rootPrefix)} ${require_utils_colors.default.yellow(root.token)}${hasMw}`);
2526
+ root.childIds.forEach((childId, idx) => {
2527
+ const isLastChild = idx === root.childIds.length - 1;
2528
+ printHierarchicalNode(childId, rootChildIndent + (isLastChild ? "└─" : "├─"), rootChildIndent + (isLastChild ? " " : "│ "));
2529
+ });
2530
+ entryIndex++;
2340
2531
  });
2341
2532
  if (hasChildren) {
2342
2533
  const children = categoryTree[category].sort();
@@ -2369,6 +2560,20 @@ var AppCommandHandler = class {
2369
2560
  return Array.from(this.loadedCommands.values());
2370
2561
  }
2371
2562
  /**
2563
+ * Gets all executable runtime routes, including hierarchical leaves.
2564
+ * @returns Array of route-indexed commands
2565
+ */
2566
+ getRuntimeCommandsArray() {
2567
+ return Array.from(this.runtimeRouteIndex.values());
2568
+ }
2569
+ /**
2570
+ * Gets loaded hierarchical command nodes, including non-executable containers.
2571
+ * @returns Array of hierarchical node definitions
2572
+ */
2573
+ getHierarchicalNodesArray() {
2574
+ return Array.from(this.hierarchicalNodes.values());
2575
+ }
2576
+ /**
2372
2577
  * Registers event handlers for Discord interactions and messages.
2373
2578
  */
2374
2579
  registerCommandHandler() {
@@ -2394,6 +2599,35 @@ var AppCommandHandler = class {
2394
2599
  this.commandkit.client.on(discord_js.Events.MessageCreate, this.onMessageCreate);
2395
2600
  }
2396
2601
  /**
2602
+ * @private
2603
+ * @internal
2604
+ */
2605
+ normalizeRouteKey(input) {
2606
+ return input.trim().replace(/[.:]/g, " ").split(/\s+/).filter(Boolean).join(".");
2607
+ }
2608
+ /**
2609
+ * @private
2610
+ * @internal
2611
+ */
2612
+ buildInteractionRouteKey(source) {
2613
+ if (!source.isCommand() && !source.isAutocomplete()) return "";
2614
+ const segments = [source.commandName];
2615
+ if (source.isChatInputCommand() || source.isAutocomplete()) {
2616
+ const group = source.options.getSubcommandGroup(false);
2617
+ const subcommand = source.options.getSubcommand(false);
2618
+ if (group) segments.push(group);
2619
+ if (subcommand) segments.push(subcommand);
2620
+ }
2621
+ return segments.filter(Boolean).join(".");
2622
+ }
2623
+ /**
2624
+ * @private
2625
+ * @internal
2626
+ */
2627
+ buildMessageRouteKey(parser) {
2628
+ return parser.getFullRoute().join(".");
2629
+ }
2630
+ /**
2397
2631
  * Prepares a command for execution by resolving the command and its middleware.
2398
2632
  * @param source - The interaction or message that triggered the command
2399
2633
  * @param cmdName - Optional command name override
@@ -2403,13 +2637,19 @@ var AppCommandHandler = class {
2403
2637
  var _loadedCommand$metada3, _loadedCommand$metada4;
2404
2638
  if (getConfig().disablePrefixCommands && source instanceof discord_js.Message) return null;
2405
2639
  let parser;
2406
- if (!cmdName) if (source instanceof discord_js.Message) {
2640
+ let routeKey;
2641
+ let usedCommandOverride = false;
2642
+ if (cmdName) {
2643
+ routeKey = this.normalizeRouteKey(cmdName);
2644
+ usedCommandOverride = true;
2645
+ }
2646
+ if (!routeKey) if (source instanceof discord_js.Message) {
2407
2647
  if (source.author.bot) return null;
2408
2648
  const prefix = await this.commandkit.appConfig.getMessageCommandPrefix(source);
2409
2649
  if (!prefix || (typeof prefix === "string" || Array.isArray(prefix)) && !prefix.length) return null;
2410
2650
  parser = new require_app_commands_MessageCommandParser.MessageCommandParser(source, prefix instanceof RegExp ? prefix : Array.isArray(prefix) ? prefix : [prefix], (command) => {
2411
2651
  var _loadedCommand$metada, _loadedCommand$metada2, _json$options;
2412
- const loadedCommand = this.findCommandByName(command);
2652
+ const loadedCommand = this.findCommandByRoute(command);
2413
2653
  if (!loadedCommand) {
2414
2654
  if (require_utils_constants.COMMANDKIT_IS_DEV && this.commandkit.config.showUnknownPrefixCommandsWarning) Logger.error`Prefix command "${command}" was not found.\nNote: This warning is only shown in development mode as an alert to help you find the command. If you wish to remove this warning, set \`showUnknownPrefixCommandsWarning\` to \`false\` in your commandkit config.`;
2415
2655
  return null;
@@ -2421,7 +2661,7 @@ var AppCommandHandler = class {
2421
2661
  }, {})) ?? {};
2422
2662
  });
2423
2663
  try {
2424
- cmdName = parser.getFullCommand().split(" ")[0];
2664
+ routeKey = this.buildMessageRouteKey(parser);
2425
2665
  } catch (e) {
2426
2666
  if (require_utils_error_codes.isErrorType(e, require_utils_error_codes.CommandKitErrorCodes.InvalidCommandPrefix)) return null;
2427
2667
  Logger.error`${e}`;
@@ -2429,10 +2669,10 @@ var AppCommandHandler = class {
2429
2669
  }
2430
2670
  } else {
2431
2671
  if (!(source.isChatInputCommand() || source.isAutocomplete() || source.isContextMenuCommand())) return null;
2432
- cmdName = source.commandName;
2672
+ routeKey = this.buildInteractionRouteKey(source);
2433
2673
  }
2434
2674
  const hint = require_app_commands_helpers.isInteractionSource(source) ? source.isUserContextMenuCommand() ? "user" : source.isMessageContextMenuCommand() ? "message" : void 0 : void 0;
2435
- const loadedCommand = this.findCommandByName(cmdName, hint);
2675
+ const loadedCommand = hint ? this.findCommandByName(routeKey, hint) : this.findCommandByRoute(routeKey, usedCommandOverride);
2436
2676
  if (!loadedCommand) return null;
2437
2677
  if ((source instanceof discord_js.CommandInteraction || source instanceof discord_js.AutocompleteInteraction) && source.guildId && ((_loadedCommand$metada3 = loadedCommand.metadata) === null || _loadedCommand$metada3 === void 0 || (_loadedCommand$metada3 = _loadedCommand$metada3.guilds) === null || _loadedCommand$metada3 === void 0 ? void 0 : _loadedCommand$metada3.length) && !((_loadedCommand$metada4 = loadedCommand.metadata) === null || _loadedCommand$metada4 === void 0 ? void 0 : _loadedCommand$metada4.guilds.includes(source.guildId))) return null;
2438
2678
  if (source instanceof discord_js.Message) {
@@ -2483,21 +2723,46 @@ var AppCommandHandler = class {
2483
2723
  }
2484
2724
  return null;
2485
2725
  }
2486
- resolveMessageCommandName(name) {
2487
- for (const [, loadedCommand] of this.loadedCommands) {
2726
+ /**
2727
+ * Finds a command by its canonical route key.
2728
+ * @param route - The command route or command name
2729
+ * @param allowFlatAliasFallback - Whether to check flat aliases if the route key was not found
2730
+ * @returns The loaded command or null if not found
2731
+ */
2732
+ findCommandByRoute(route, allowFlatAliasFallback = true) {
2733
+ const normalizedRoute = this.normalizeRouteKey(route);
2734
+ const directMatch = this.runtimeRouteIndex.get(normalizedRoute);
2735
+ if (directMatch) return directMatch;
2736
+ if (!allowFlatAliasFallback || normalizedRoute.includes(".")) return null;
2737
+ for (const loadedCommand of this.runtimeRouteIndex.values()) {
2488
2738
  var _loadedCommand$data$m3;
2489
- if (loadedCommand.data.command.name === name) return loadedCommand.data.command.name;
2490
2739
  const aliases = (_loadedCommand$data$m3 = loadedCommand.data.metadata) === null || _loadedCommand$data$m3 === void 0 ? void 0 : _loadedCommand$data$m3.aliases;
2491
- if (aliases && Array.isArray(aliases) && aliases.includes(name)) return loadedCommand.data.command.name;
2740
+ if (aliases && Array.isArray(aliases) && aliases.includes(normalizedRoute)) return loadedCommand;
2492
2741
  }
2742
+ return null;
2743
+ }
2744
+ /**
2745
+ * @private
2746
+ * @internal
2747
+ */
2748
+ getRouteKeyFor(command) {
2749
+ return command.data.command.__routeKey ?? this.normalizeRouteKey(command.data.command.name);
2750
+ }
2751
+ resolveMessageCommandName(name) {
2752
+ const loadedCommand = this.findCommandByRoute(name);
2753
+ if (loadedCommand) return this.getRouteKeyFor(loadedCommand);
2493
2754
  return name;
2494
2755
  }
2495
2756
  /**
2496
2757
  * Reloads all commands and middleware from scratch.
2497
2758
  */
2498
2759
  async reloadCommands() {
2760
+ var _this$commandkit$comm2;
2761
+ await ((_this$commandkit$comm2 = this.commandkit.commandsRouter) === null || _this$commandkit$comm2 === void 0 ? void 0 : _this$commandkit$comm2.scan());
2499
2762
  this.loadedCommands.clear();
2500
2763
  this.loadedMiddlewares.clear();
2764
+ this.runtimeRouteIndex.clear();
2765
+ this.hierarchicalNodes.clear();
2501
2766
  this.externalCommandData.clear();
2502
2767
  this.externalMiddlewareData.clear();
2503
2768
  await this.loadCommands();
@@ -2534,7 +2799,10 @@ var AppCommandHandler = class {
2534
2799
  * @param data - Array of loaded commands to register
2535
2800
  */
2536
2801
  async registerExternalLoadedCommands(data) {
2537
- for (const command of data) this.loadedCommands.set(command.command.id, command);
2802
+ for (const command of data) {
2803
+ this.loadedCommands.set(command.command.id, command);
2804
+ this.registerRuntimeRoute(command);
2805
+ }
2538
2806
  }
2539
2807
  /**
2540
2808
  * Loads all commands and middleware from the router.
@@ -2545,15 +2813,20 @@ var AppCommandHandler = class {
2545
2813
  });
2546
2814
  const commandsRouter = this.commandkit.commandsRouter;
2547
2815
  if (!commandsRouter) throw new Error("Commands router has not yet initialized");
2548
- const { commands, middlewares } = commandsRouter.getData();
2816
+ const { commands, middlewares, treeNodes, compiledRoutes } = commandsRouter.getData();
2549
2817
  const combinedCommands = this.externalCommandData.size ? commands.concat(this.externalCommandData) : commands;
2550
2818
  const combinedMiddlewares = this.externalMiddlewareData.size ? middlewares.concat(this.externalMiddlewareData) : middlewares;
2551
2819
  for (const [id, middleware] of combinedMiddlewares) await this.loadMiddleware(id, middleware);
2552
2820
  for (const [id, command] of combinedCommands) await this.loadCommand(id, command);
2821
+ const hierarchicalNodes = Array.from(treeNodes.values()).filter((node) => node.source !== "flat" && !!node.definitionPath).sort((left, right) => left.route.length - right.route.length);
2822
+ for (const node of hierarchicalNodes) {
2823
+ const routeKey = node.route.join(".");
2824
+ await this.loadHierarchicalNode(node, compiledRoutes.get(routeKey) ?? void 0);
2825
+ }
2553
2826
  if (require_utils_constants.COMMANDKIT_IS_DEV) {
2554
- const commandNames = Array.from(this.loadedCommands.values()).map((v) => v.data.command.name);
2555
- const aliases = Array.from(this.loadedCommands.values()).flatMap((v) => v.metadata.aliases || []);
2556
- await require_utils_types_package.rewriteCommandDeclaration(`type CommandTypeData = ${[...commandNames, ...aliases].map((name) => JSON.stringify(name)).join(" | ")}`);
2827
+ const commandNames = Array.from(this.runtimeRouteIndex.keys());
2828
+ const aliases = Array.from(this.runtimeRouteIndex.values()).flatMap((v) => v.metadata.aliases || []);
2829
+ await require_utils_types_package.rewriteCommandDeclaration(`type CommandTypeData = ${Array.from(new Set([...commandNames, ...aliases])).map((name) => JSON.stringify(name)).join(" | ")}`);
2557
2830
  }
2558
2831
  await this.commandkit.plugins.execute((ctx, plugin) => {
2559
2832
  return plugin.onAfterCommandsLoad(ctx);
@@ -2584,10 +2857,84 @@ var AppCommandHandler = class {
2584
2857
  * @private
2585
2858
  * @internal
2586
2859
  */
2860
+ shouldIndexAsRuntimeRoute(command) {
2861
+ return !!(command.data.chatInput || command.data.message || command.data.autocomplete);
2862
+ }
2863
+ /**
2864
+ * @private
2865
+ * @internal
2866
+ */
2867
+ registerRuntimeRoute(command, routeKey) {
2868
+ if (!this.shouldIndexAsRuntimeRoute(command)) return;
2869
+ const key = this.normalizeRouteKey(routeKey ?? command.data.command.name);
2870
+ if (!key) return;
2871
+ const commandData = command.data.command;
2872
+ commandData.__routeKey ??= key;
2873
+ this.runtimeRouteIndex.set(key, command);
2874
+ }
2875
+ /**
2876
+ * @private
2877
+ * @internal
2878
+ */
2879
+ async processCommandFile(fileUrl, identifier, fallbackName, isHierarchical) {
2880
+ const commandFileData = await import(`${require_utils_resolve_file_url.toFileURL(fileUrl)}?t=${Date.now()}`);
2881
+ if (!commandFileData.command) throw new Error(`Invalid export for ${isHierarchical ? "hierarchical node" : "command"} ${identifier}: no command definition found`);
2882
+ const metadataFunc = commandFileData.generateMetadata;
2883
+ const metadataObj = commandFileData.metadata;
2884
+ if (metadataFunc && metadataObj) throw new Error("A command may only export either `generateMetadata` or `metadata`, not both");
2885
+ const metadata = (metadataFunc ? await metadataFunc() : metadataObj) ?? {
2886
+ aliases: [],
2887
+ guilds: [],
2888
+ userPermissions: [],
2889
+ botPermissions: []
2890
+ };
2891
+ let commandName = commandFileData.command.name;
2892
+ if (isHierarchical) {
2893
+ if (typeof commandName === "string" && commandName !== fallbackName) Logger.warn(`Hierarchical node \`${identifier}\` overrides its command name with \`${commandName}\`. The filesystem token \`${fallbackName}\` will be used instead.`);
2894
+ commandName = fallbackName;
2895
+ } else commandName = commandName || fallbackName;
2896
+ let commandDescription = commandFileData.command.description;
2897
+ if (!commandDescription && commandFileData.chatInput) commandDescription = "No command description set.";
2898
+ const updatedCommandData = {
2899
+ ...commandFileData.command,
2900
+ name: commandName,
2901
+ description: commandDescription
2902
+ };
2903
+ let handlerCount = 0;
2904
+ for (const [key, propValidator] of Object.entries(commandDataSchema)) {
2905
+ const exportedProp = commandFileData[key];
2906
+ if (exportedProp) {
2907
+ if (!await propValidator(exportedProp)) throw new Error(`Invalid export for ${isHierarchical ? "hierarchical node" : "command"} ${identifier}: ${key} does not match expected value`);
2908
+ if (!KNOWN_NON_HANDLER_KEYS.includes(key)) handlerCount++;
2909
+ }
2910
+ }
2911
+ let lastUpdated = updatedCommandData;
2912
+ await this.commandkit.plugins.execute(async (ctx, plugin) => {
2913
+ const res = await plugin.prepareCommand(ctx, lastUpdated);
2914
+ if (res) lastUpdated = res;
2915
+ });
2916
+ const commandJson = "toJSON" in lastUpdated && typeof lastUpdated.toJSON === "function" ? lastUpdated.toJSON() : lastUpdated;
2917
+ if ("guilds" in commandJson || "aliases" in commandJson) Logger.warn(`Command \`${identifier}\` uses deprecated metadata properties. Please update to use the new \`metadata\` object or \`generateMetadata\` function.`);
2918
+ const resolvedMetadata = {
2919
+ guilds: commandJson.guilds,
2920
+ aliases: commandJson.aliases,
2921
+ ...metadata
2922
+ };
2923
+ return {
2924
+ commandFileData,
2925
+ handlerCount,
2926
+ commandJson,
2927
+ resolvedMetadata
2928
+ };
2929
+ }
2930
+ /**
2931
+ * @private
2932
+ * @internal
2933
+ */
2587
2934
  async loadCommand(id, command) {
2588
2935
  try {
2589
2936
  if (command.path === null) {
2590
- this.loadedCommands.set(id, {
2937
+ const loadedCommand = {
2591
2938
  discordId: null,
2592
2939
  command,
2593
2940
  metadata: {
@@ -2597,50 +2944,14 @@ var AppCommandHandler = class {
2597
2944
  botPermissions: []
2598
2945
  },
2599
2946
  data: { command: { name: command.name } }
2600
- });
2947
+ };
2948
+ this.loadedCommands.set(id, loadedCommand);
2949
+ this.registerRuntimeRoute(loadedCommand);
2601
2950
  return;
2602
2951
  }
2603
- const commandFileData = await import(`${require_utils_resolve_file_url.toFileURL(command.path)}?t=${Date.now()}`);
2604
- if (!commandFileData.command) throw new Error(`Invalid export for command ${command.name}: no command definition found`);
2605
- const metadataFunc = commandFileData.generateMetadata;
2606
- const metadataObj = commandFileData.metadata;
2607
- if (metadataFunc && metadataObj) throw new Error("A command may only export either `generateMetadata` or `metadata`, not both");
2608
- const metadata = (metadataFunc ? await metadataFunc() : metadataObj) ?? {
2609
- aliases: [],
2610
- guilds: [],
2611
- userPermissions: [],
2612
- botPermissions: []
2613
- };
2614
- const commandName = commandFileData.command.name || command.name;
2615
- let commandDescription = commandFileData.command.description;
2616
- if (!commandDescription && commandFileData.chatInput) commandDescription = "No command description set.";
2617
- const updatedCommandData = {
2618
- ...commandFileData.command,
2619
- name: commandName,
2620
- description: commandDescription
2621
- };
2622
- let handlerCount = 0;
2623
- for (const [key, propValidator] of Object.entries(commandDataSchema)) {
2624
- const exportedProp = commandFileData[key];
2625
- if (exportedProp) {
2626
- if (!await propValidator(exportedProp)) throw new Error(`Invalid export for command ${command.name}: ${key} does not match expected value`);
2627
- if (!KNOWN_NON_HANDLER_KEYS.includes(key)) handlerCount++;
2628
- }
2629
- }
2952
+ const { commandFileData, handlerCount, commandJson, resolvedMetadata } = await this.processCommandFile(command.path, command.name, command.name, false);
2630
2953
  if (handlerCount === 0) throw new Error(`Invalid export for command ${command.name}: at least one handler function must be provided`);
2631
- let lastUpdated = updatedCommandData;
2632
- await this.commandkit.plugins.execute(async (ctx, plugin) => {
2633
- const res = await plugin.prepareCommand(ctx, lastUpdated);
2634
- if (res) lastUpdated = res;
2635
- });
2636
- const commandJson = "toJSON" in lastUpdated && typeof lastUpdated.toJSON === "function" ? lastUpdated.toJSON() : lastUpdated;
2637
- if ("guilds" in commandJson || "aliases" in commandJson) Logger.warn(`Command \`${command.name}\` uses deprecated metadata properties. Please update to use the new \`metadata\` object or \`generateMetadata\` function.`);
2638
- const resolvedMetadata = {
2639
- guilds: commandJson.guilds,
2640
- aliases: commandJson.aliases,
2641
- ...metadata
2642
- };
2643
- this.loadedCommands.set(id, {
2954
+ const loadedCommand = {
2644
2955
  discordId: null,
2645
2956
  command,
2646
2957
  metadata: resolvedMetadata,
@@ -2649,20 +2960,68 @@ var AppCommandHandler = class {
2649
2960
  metadata: resolvedMetadata,
2650
2961
  command: commandJson
2651
2962
  }
2652
- });
2963
+ };
2964
+ this.loadedCommands.set(id, loadedCommand);
2965
+ this.registerRuntimeRoute(loadedCommand);
2653
2966
  this.generateContextMenuCommands(id, command, commandFileData, commandJson, resolvedMetadata);
2654
2967
  } catch (error) {
2655
2968
  Logger.error`Failed to load command ${command.name} (${id}): ${error}`;
2656
2969
  }
2657
2970
  }
2658
2971
  /**
2972
+ * Loads a hierarchical command node into the hierarchical cache.
2973
+ * Executable leaves are also added to the runtime route index.
2974
+ * @private
2975
+ * @internal
2976
+ */
2977
+ async loadHierarchicalNode(node, compiledRoute) {
2978
+ if (!node.definitionPath) return;
2979
+ const routeKey = node.route.join(".");
2980
+ const command = {
2981
+ id: node.id,
2982
+ name: routeKey,
2983
+ path: node.definitionPath,
2984
+ relativePath: (compiledRoute === null || compiledRoute === void 0 ? void 0 : compiledRoute.relativePath) ?? node.relativePath,
2985
+ parentPath: (0, node_path.dirname)(node.definitionPath),
2986
+ middlewares: compiledRoute ? [...compiledRoute.middlewares] : [],
2987
+ category: node.category
2988
+ };
2989
+ try {
2990
+ const { commandFileData, handlerCount, commandJson, resolvedMetadata } = await this.processCommandFile(command.path, routeKey, node.token, true);
2991
+ const isRootHierarchyLeaf = node.kind === "command";
2992
+ const hasContextMenuHandlers = !!(commandFileData.userContextMenu || commandFileData.messageContextMenu);
2993
+ const hasExecutableSlashHandlers = !!(commandFileData.chatInput || commandFileData.message || commandFileData.autocomplete);
2994
+ if (!isRootHierarchyLeaf && hasContextMenuHandlers) throw new Error(`Invalid export for hierarchical node ${routeKey}: context menu handlers are only supported for top-level root commands.`);
2995
+ if (node.executable && handlerCount === 0) throw new Error(`Invalid export for hierarchical node ${routeKey}: executable leaves must provide at least one handler function`);
2996
+ if (!node.executable && hasExecutableSlashHandlers) throw new Error(`Invalid export for hierarchical node ${routeKey}: non-leaf hierarchical nodes cannot export executable slash/prefix handlers`);
2997
+ const loadedCommand = {
2998
+ discordId: null,
2999
+ command,
3000
+ metadata: resolvedMetadata,
3001
+ data: {
3002
+ ...commandFileData,
3003
+ metadata: resolvedMetadata,
3004
+ command: {
3005
+ ...commandJson,
3006
+ __routeKey: routeKey
3007
+ }
3008
+ }
3009
+ };
3010
+ this.hierarchicalNodes.set(node.id, loadedCommand);
3011
+ if (node.executable) this.registerRuntimeRoute(loadedCommand, routeKey);
3012
+ if (isRootHierarchyLeaf && hasContextMenuHandlers) this.generateContextMenuCommands(node.id, command, commandFileData, commandJson, resolvedMetadata);
3013
+ } catch (error) {
3014
+ Logger.error`Failed to load hierarchical node ${routeKey} (${node.id}): ${error}`;
3015
+ }
3016
+ }
3017
+ /**
2659
3018
  * Gets the metadata for a command.
2660
3019
  * @param command - The command name to get metadata for
2661
3020
  * @param hint - The hint for the command type (user or message)
2662
3021
  * @returns The command metadata or null if not found
2663
3022
  */
2664
3023
  getMetadataFor(command, hint) {
2665
- const loadedCommand = this.findCommandByName(command, hint);
3024
+ const loadedCommand = hint ? this.findCommandByName(command, hint) : this.findCommandByRoute(command);
2666
3025
  if (!loadedCommand) return null;
2667
3026
  return loadedCommand.metadata ??= {
2668
3027
  aliases: [],
@@ -3707,6 +4066,61 @@ function ChannelSelectMenu(props) {
3707
4066
  return builder;
3708
4067
  }
3709
4068
 
4069
+ //#endregion
4070
+ //#region src/components/interactive/checkbox/checkbox.ts
4071
+ function Checkbox(props) {
4072
+ const checkbox = new discord_js.CheckboxBuilder();
4073
+ applyId(props, checkbox);
4074
+ if (props.customId != null) checkbox.setCustomId(props.customId);
4075
+ if (props.default != null) checkbox.setDefault(props.default);
4076
+ return checkbox;
4077
+ }
4078
+ function CheckboxGroupOption(props) {
4079
+ const option = new discord_js.CheckboxGroupOptionBuilder();
4080
+ option.setLabel(props.label);
4081
+ option.setValue(props.value);
4082
+ if (props.description != null) option.setDescription(props.description);
4083
+ if (props.default != null) option.setDefault(props.default);
4084
+ return option;
4085
+ }
4086
+ function CheckboxGroup(props) {
4087
+ const checkboxGroup = new discord_js.CheckboxGroupBuilder();
4088
+ applyId(props, checkboxGroup);
4089
+ if (props.customId != null) checkboxGroup.setCustomId(props.customId);
4090
+ if (props.required != null) checkboxGroup.setRequired(props.required);
4091
+ if (props.minValues != null) checkboxGroup.setMinValues(props.minValues);
4092
+ if (props.maxValues != null) checkboxGroup.setMaxValues(props.maxValues);
4093
+ if (props.children != null) {
4094
+ const options = (Array.isArray(props.children) ? props.children : [props.children]).filter((option) => option != null);
4095
+ if (options.length === 0) throw new Error("CheckboxGroup requires at least 1 option. Use <CheckboxGroupOption label=... value=... /> as a child.");
4096
+ checkboxGroup.setOptions(options);
4097
+ }
4098
+ return checkboxGroup;
4099
+ }
4100
+
4101
+ //#endregion
4102
+ //#region src/components/interactive/radio/radio.ts
4103
+ function RadioGroup(props) {
4104
+ const radioGroup = new discord_js.RadioGroupBuilder();
4105
+ applyId(props, radioGroup);
4106
+ radioGroup.setCustomId(props.customId);
4107
+ if (props.children != null) {
4108
+ const options = (Array.isArray(props.children) ? props.children : [props.children]).filter((option) => option != null);
4109
+ if (options.length === 0) throw new Error("RadioGroup requires at least 1 option. Use <RadioGroupOption label=... value=... /> as a child.");
4110
+ radioGroup.setOptions(options);
4111
+ }
4112
+ if (props.required != null) radioGroup.setRequired(props.required);
4113
+ return radioGroup;
4114
+ }
4115
+ function RadioGroupOption(props) {
4116
+ const option = new discord_js.RadioGroupOptionBuilder();
4117
+ option.setLabel(props.label);
4118
+ option.setValue(props.value);
4119
+ if (props.description != null) option.setDescription(props.description);
4120
+ if (props.default != null) option.setDefault(props.default);
4121
+ return option;
4122
+ }
4123
+
3710
4124
  //#endregion
3711
4125
  //#region src/components/display/container.ts
3712
4126
  /**
@@ -4163,11 +4577,11 @@ function registerDevHooks(commandkit) {
4163
4577
  if (prevented) return;
4164
4578
  switch (event) {
4165
4579
  case require_utils_constants.HMREventType.ReloadCommands:
4166
- commandkit.commandHandler.reloadCommands();
4580
+ await commandkit.commandHandler.reloadCommands();
4167
4581
  handled = true;
4168
4582
  break;
4169
4583
  case require_utils_constants.HMREventType.ReloadEvents:
4170
- commandkit.eventHandler.reloadEvents();
4584
+ await commandkit.eventHandler.reloadEvents();
4171
4585
  handled = true;
4172
4586
  break;
4173
4587
  case require_utils_constants.HMREventType.Unknown:
@@ -4670,6 +5084,24 @@ Object.defineProperty(exports, 'ChannelSelectMenuKit', {
4670
5084
  return ChannelSelectMenuKit;
4671
5085
  }
4672
5086
  });
5087
+ Object.defineProperty(exports, 'Checkbox', {
5088
+ enumerable: true,
5089
+ get: function () {
5090
+ return Checkbox;
5091
+ }
5092
+ });
5093
+ Object.defineProperty(exports, 'CheckboxGroup', {
5094
+ enumerable: true,
5095
+ get: function () {
5096
+ return CheckboxGroup;
5097
+ }
5098
+ });
5099
+ Object.defineProperty(exports, 'CheckboxGroupOption', {
5100
+ enumerable: true,
5101
+ get: function () {
5102
+ return CheckboxGroupOption;
5103
+ }
5104
+ });
4673
5105
  Object.defineProperty(exports, 'CommandExecutionMode', {
4674
5106
  enumerable: true,
4675
5107
  get: function () {
@@ -4832,6 +5264,18 @@ Object.defineProperty(exports, 'ParagraphInput', {
4832
5264
  return ParagraphInput;
4833
5265
  }
4834
5266
  });
5267
+ Object.defineProperty(exports, 'RadioGroup', {
5268
+ enumerable: true,
5269
+ get: function () {
5270
+ return RadioGroup;
5271
+ }
5272
+ });
5273
+ Object.defineProperty(exports, 'RadioGroupOption', {
5274
+ enumerable: true,
5275
+ get: function () {
5276
+ return RadioGroupOption;
5277
+ }
5278
+ });
4835
5279
  Object.defineProperty(exports, 'RoleSelectMenu', {
4836
5280
  enumerable: true,
4837
5281
  get: function () {
@@ -5144,4 +5588,4 @@ Object.defineProperty(exports, 'useStore', {
5144
5588
  return useStore;
5145
5589
  }
5146
5590
  });
5147
- //# sourceMappingURL=commandkit-DNln6BAJ.js.map
5591
+ //# sourceMappingURL=commandkit-DTqT800J.js.map