meocord 4.0.0-beta.3 → 4.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +37 -2
- package/dist/cjs/_shared/component-routes-RTZZ3t5i.cjs +55 -0
- package/dist/cjs/_shared/{controller.decorator-9nIxOp9y.cjs → controller.decorator-DnKZ29W4.cjs} +32 -108
- package/dist/cjs/_shared/{theme-Dqwlnz0m.cjs → theme-C8w8ivsR.cjs} +4 -12
- package/dist/cjs/common/index.cjs +1 -1
- package/dist/cjs/core/index.cjs +45 -101
- package/dist/cjs/decorator/index.cjs +45 -116
- package/dist/cjs/enum/index.cjs +34 -10
- package/dist/cjs/testing/index.cjs +144 -144
- package/dist/esm/bin/app-template/README.md.template +3 -1
- package/dist/esm/bin/app-template/vitest.config.ts.template +3 -5
- package/dist/esm/bin/helper/app-generator.helper.js +6 -24
- package/dist/esm/bin/helper/controller-generator.helper.js +8 -35
- package/dist/esm/bin/helper/guard-generator.helper.js +2 -7
- package/dist/esm/bin/helper/service-generator.helper.js +2 -7
- package/dist/esm/bin/meocord.js +9 -31
- package/dist/esm/build/native-addons.js +10 -33
- package/dist/esm/build/rsbuild-config.js +8 -26
- package/dist/esm/core/component-routes.js +51 -0
- package/dist/esm/core/meocord.app.js +37 -87
- package/dist/esm/decorator/app.decorator.js +10 -25
- package/dist/esm/decorator/command-builder.decorator.js +6 -9
- package/dist/esm/decorator/controller.decorator.js +14 -45
- package/dist/esm/decorator/guard.decorator.js +14 -56
- package/dist/esm/decorator/index.js +1 -1
- package/dist/esm/decorator/service.decorator.js +4 -8
- package/dist/esm/enum/controller.enum.js +3 -7
- package/dist/esm/enum/metadata-key.enum.js +0 -1
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/index.js +1 -0
- package/dist/esm/testing/meocord-testing-module.js +9 -5
- package/dist/esm/testing/mock-fn.js +14 -25
- package/dist/esm/testing/mock-interaction.js +58 -109
- package/dist/esm/testing/routing.js +59 -0
- package/dist/esm/util/common.util.js +5 -22
- package/dist/esm/util/generator-cli.util.js +13 -51
- package/dist/esm/util/interaction.util.js +15 -60
- package/dist/esm/util/json.util.js +1 -9
- package/dist/esm/util/meocord-cli.util.js +4 -31
- package/dist/esm/util/meocord-config-loader.util.js +3 -11
- package/dist/esm/util/meocord-source-config.util.js +2 -9
- package/dist/esm/util/package-manager.util.js +2 -6
- package/dist/esm/util/package-version.util.js +2 -11
- package/dist/esm/util/platform.util.js +3 -8
- package/dist/esm/util/runtime.util.js +10 -38
- package/dist/esm/util/tsconfig.util.js +4 -9
- package/dist/types/{controller.enum-DYfhYaat.d.ts → controller.enum-BL6wEQls.d.cts} +3 -7
- package/dist/types/{controller.enum-DYfhYaat.d.cts → controller.enum-BL6wEQls.d.ts} +3 -7
- package/dist/types/core/index.d.cts +27 -55
- package/dist/types/core/index.d.ts +27 -55
- package/dist/types/decorator/index.d.cts +45 -168
- package/dist/types/decorator/index.d.ts +45 -168
- package/dist/types/enum/index.d.cts +1 -2
- package/dist/types/enum/index.d.ts +1 -2
- package/dist/types/interface/index.d.cts +49 -58
- package/dist/types/interface/index.d.ts +49 -58
- package/dist/types/testing/index.d.cts +147 -89
- package/dist/types/testing/index.d.ts +147 -89
- package/package.json +1 -1
- package/dist/cjs/_shared/metadata-key.enum-aABGdodb.cjs +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# meocord
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.5
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#47](https://github.com/l7aromeo/meocord/pull/47) [`9652436`](https://github.com/l7aromeo/meocord/commit/965243660323277827a64851f28da9cda2c2fe0c) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add `resolveRoute` and `findRouteConflicts` to `meocord/testing`, for testing which handler a
|
|
8
|
+
component's customId reaches. `resolveRoute(App, { type, customId })` gives the answer dispatch gives
|
|
9
|
+
— across every controller the app registers, for that component type, most specific pattern first —
|
|
10
|
+
as the controller, the handler method and its name, and the captured params, or `undefined`. `findRouteConflicts(App)` returns the
|
|
11
|
+
pattern pairs that can match the same customId, which MeoCord otherwise only warns about at startup.
|
|
12
|
+
Both read decorator metadata only, and dispatch runs on the same matcher.
|
|
13
|
+
|
|
14
|
+
## 4.0.0-beta.4
|
|
15
|
+
|
|
16
|
+
### Major Changes
|
|
17
|
+
|
|
18
|
+
- [#44](https://github.com/l7aromeo/meocord/pull/44) [`e48472e`](https://github.com/l7aromeo/meocord/commit/e48472e9ffee2f0371a7a2a71a4063e99ce61674) Thanks [@l7aromeo](https://github.com/l7aromeo)! - **Breaking:** `meocord/decorator` exports only the decorators. The routing helpers it also exported
|
|
19
|
+
— `getCommandMap`, `getMessageHandlers`, `getReactionHandlers`, `getAutocompleteHandlers`,
|
|
20
|
+
`findAmbiguousRoutes` and `PARAM_SEPARATOR` — are internal to MeoCord now. See the
|
|
21
|
+
[migration guide](https://github.com/l7aromeo/meocord/blob/main/docs/MIGRATING.md#internal-helpers-are-no-longer-exported).
|
|
22
|
+
|
|
3
23
|
## 4.0.0-beta.3
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -273,7 +273,9 @@ See [Self-contained builds](#self-contained-builds) for when to turn on `bundleD
|
|
|
273
273
|
|
|
274
274
|
### ESLint
|
|
275
275
|
|
|
276
|
-
MeoCord exports a base ESLint config from `meocord/eslint`.
|
|
276
|
+
MeoCord exports a base ESLint config from `meocord/eslint`. It lints your TypeScript, `meocord.config.ts`
|
|
277
|
+
included, with type information from `tsconfig.json`, `tsconfig.test.json` and `tsconfig.eslint.json`; a
|
|
278
|
+
file ESLint reports as not included in any of them needs listing in one. Extend it as needed:
|
|
277
279
|
|
|
278
280
|
```javascript
|
|
279
281
|
import meocordEslint, { typescriptConfig } from 'meocord/eslint'
|
|
@@ -687,7 +689,7 @@ bun run test:typecheck # tsc -p tsconfig.test.json
|
|
|
687
689
|
bun run lint # eslint --fix + tsc
|
|
688
690
|
```
|
|
689
691
|
|
|
690
|
-
|
|
692
|
+
Generated apps come with Vitest set up — `vitest.config.ts` with SWC for decorator metadata, and `test`, `test:watch` and `test:coverage` scripts — plus a spec beside every generated component. In an older project, add Vitest (or keep Jest) with the same SWC setup; `meocord/testing` works with either.
|
|
691
693
|
|
|
692
694
|
### `MeoCordTestingModule`
|
|
693
695
|
|
|
@@ -882,6 +884,39 @@ expect(msg.delete).toHaveBeenCalledTimes(1)
|
|
|
882
884
|
|
|
883
885
|
</details>
|
|
884
886
|
|
|
887
|
+
<details>
|
|
888
|
+
<summary><b><code>resolveRoute</code> / <code>findRouteConflicts</code></b></summary>
|
|
889
|
+
|
|
890
|
+
Tests which handler a component's customId reaches — the same answer dispatch gives, across every
|
|
891
|
+
controller your app registers, most specific pattern first. They read decorator metadata only, so
|
|
892
|
+
they need no Discord client, config or container. They check routing alone: guards are not run, and
|
|
893
|
+
whether a controller's dependencies are bound is for `MeoCordTestingModule` to test.
|
|
894
|
+
|
|
895
|
+
```typescript
|
|
896
|
+
import { findRouteConflicts, resolveRoute } from 'meocord/testing'
|
|
897
|
+
import { CommandType } from 'meocord/enum'
|
|
898
|
+
import App from '@src/app'
|
|
899
|
+
import { ProfileController } from '@src/controllers/button/profile.button.controller'
|
|
900
|
+
|
|
901
|
+
it('routes the profile button to its handler', () => {
|
|
902
|
+
const route = resolveRoute(App, { type: CommandType.BUTTON, customId: 'profile/111/8000' })
|
|
903
|
+
|
|
904
|
+
// The method itself rather than its name, so renaming it in your editor updates the test too.
|
|
905
|
+
expect(route?.handler).toBe(ProfileController.prototype.showProfile)
|
|
906
|
+
expect(route?.params).toEqual({ ownerId: '111', uid: '8000' })
|
|
907
|
+
})
|
|
908
|
+
|
|
909
|
+
// Patterns that can match the same customId, as a failing test rather than a startup warning.
|
|
910
|
+
it('has no overlapping component patterns', () => {
|
|
911
|
+
expect(findRouteConflicts(App)).toEqual([])
|
|
912
|
+
})
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
`resolveRoute` returns the `controller`, the `handler` method and its name as `method`, and the
|
|
916
|
+
`params` the pattern captured — or `undefined` when no route handles the customId.
|
|
917
|
+
|
|
918
|
+
</details>
|
|
919
|
+
|
|
885
920
|
<details>
|
|
886
921
|
<summary><b><code>createMock</code></b></summary>
|
|
887
922
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var controller_decorator = require('./controller.decorator-DnKZ29W4.cjs');
|
|
4
|
+
|
|
5
|
+
/** Every customId-pattern route of the given controllers, most specific first, read from metadata alone. */ function buildComponentRoutes(controllerClasses) {
|
|
6
|
+
const routes = [];
|
|
7
|
+
for (const controllerClass of controllerClasses){
|
|
8
|
+
const commandMap = controller_decorator.getCommandMap(controllerClass.prototype);
|
|
9
|
+
if (!commandMap) continue;
|
|
10
|
+
for (const [pattern, metaArray] of Object.entries(commandMap)){
|
|
11
|
+
if (!Array.isArray(metaArray)) continue;
|
|
12
|
+
for (const meta of metaArray){
|
|
13
|
+
if (meta.regex) routes.push({
|
|
14
|
+
controllerClass,
|
|
15
|
+
meta,
|
|
16
|
+
pattern
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return routes.sort((a, b)=>(b.meta.specificity ?? 0) - (a.meta.specificity ?? 0));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The route dispatch runs for a customId: the first, in rank order, whose type `acceptsType` allows
|
|
25
|
+
* and whose pattern matches, with the captured params.
|
|
26
|
+
*/ function matchComponentRoute(routes, acceptsType, customId) {
|
|
27
|
+
for (const route of routes){
|
|
28
|
+
if (!acceptsType(route.meta.type)) continue;
|
|
29
|
+
const match = route.meta.regex.exec(customId);
|
|
30
|
+
if (match) return {
|
|
31
|
+
route,
|
|
32
|
+
params: {
|
|
33
|
+
...match.groups
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
/** Pattern pairs that can match one customId, compared only within a component type, as dispatch does. */ function findComponentRouteConflicts(routes) {
|
|
40
|
+
const byType = new Map();
|
|
41
|
+
for (const { meta, pattern } of routes)byType.set(meta.type, [
|
|
42
|
+
...byType.get(meta.type) ?? [],
|
|
43
|
+
pattern
|
|
44
|
+
]);
|
|
45
|
+
return [
|
|
46
|
+
...byType
|
|
47
|
+
].flatMap(([type, patterns])=>controller_decorator.findAmbiguousRoutes(patterns).map((pair)=>({
|
|
48
|
+
type,
|
|
49
|
+
patterns: pair
|
|
50
|
+
})));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
exports.buildComponentRoutes = buildComponentRoutes;
|
|
54
|
+
exports.findComponentRouteConflicts = findComponentRouteConflicts;
|
|
55
|
+
exports.matchComponentRoute = matchComponentRoute;
|
package/dist/cjs/_shared/{controller.decorator-9nIxOp9y.cjs → controller.decorator-DnKZ29W4.cjs}
RENAMED
|
@@ -3,18 +3,11 @@
|
|
|
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-aABGdodb.cjs');
|
|
7
6
|
var discord_js = require('discord.js');
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
|
-
* The discord.js class each command type
|
|
11
|
-
*
|
|
12
|
-
* One table rather than a chain of `isButton() || isStringSelectMenu() || ...`: the
|
|
13
|
-
* registration guard in `@Command`, the dispatcher, and the type-level
|
|
14
|
-
* `CommandInteractionType` all have to agree on what a command type accepts, and a
|
|
15
|
-
* chain repeated in three files drifts the moment a fifth select menu appears. Adding
|
|
16
|
-
* a `CommandType` member without an entry here is a compile error, not a silent
|
|
17
|
-
* fall-through to "Command not found!".
|
|
9
|
+
* The discord.js class check for each command type, shared by `@Command`, the dispatcher and
|
|
10
|
+
* `CommandInteractionType`. A `CommandType` without an entry here fails to compile.
|
|
18
11
|
*/ const INTERACTION_MATCHERS = {
|
|
19
12
|
[enum_index.CommandType.SLASH]: (interaction)=>interaction instanceof discord_js.ChatInputCommandInteraction,
|
|
20
13
|
[enum_index.CommandType.CONTEXT_MENU]: (interaction)=>interaction instanceof discord_js.ContextMenuCommandInteraction,
|
|
@@ -32,47 +25,24 @@ var discord_js = require('discord.js');
|
|
|
32
25
|
enum_index.CommandType.CONTEXT_MENU,
|
|
33
26
|
enum_index.CommandType.PRIMARY_ENTRY_POINT
|
|
34
27
|
]);
|
|
35
|
-
/**
|
|
36
|
-
* Whether an interaction is the kind the given command type handles.
|
|
37
|
-
*
|
|
38
|
-
* @param type - The command type declared on `@Command`.
|
|
39
|
-
* @param interaction - The interaction being dispatched.
|
|
40
|
-
*/ function matchesCommandType(type, interaction) {
|
|
28
|
+
/** Whether an interaction is the kind the given command type handles. */ function matchesCommandType(type, interaction) {
|
|
41
29
|
const matches = INTERACTION_MATCHERS[type];
|
|
42
30
|
return matches !== undefined && matches(interaction);
|
|
43
31
|
}
|
|
44
32
|
/**
|
|
45
|
-
* Whether
|
|
46
|
-
*
|
|
47
|
-
* Components carry an application-defined customId and so are matched by pattern;
|
|
48
|
-
* commands carry a name Discord itself registered and are matched exactly.
|
|
49
|
-
*
|
|
50
|
-
* @param type - The command type declared on `@Command`.
|
|
33
|
+
* Whether a command type is routed by customId pattern: components carry an application-defined
|
|
34
|
+
* customId, while commands are matched by the name Discord registered.
|
|
51
35
|
*/ function isCustomIdRouted(type) {
|
|
52
36
|
return !NAME_ROUTED_TYPES.has(type);
|
|
53
37
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Whether an interaction carries a customId, and so can be routed by pattern.
|
|
56
|
-
*
|
|
57
|
-
* @param interaction - The interaction being dispatched.
|
|
58
|
-
*/ function hasCustomId(interaction) {
|
|
38
|
+
/** Whether an interaction carries a customId, and so can be routed by pattern. */ function hasCustomId(interaction) {
|
|
59
39
|
return interaction instanceof discord_js.MessageComponentInteraction || interaction instanceof discord_js.ModalSubmitInteraction;
|
|
60
40
|
}
|
|
61
41
|
/** Separates a command from its subcommand group and subcommand in a route key. */ const COMMAND_PATH_SEPARATOR = ' ';
|
|
62
42
|
/**
|
|
63
|
-
* The route keys a chat input interaction can be handled by, most specific first
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* routing on `commandName` alone gives every subcommand of a command the same handler
|
|
67
|
-
* — and the framework would run whichever one was declared first. The full path is
|
|
68
|
-
* tried before the bare name so a command can either split its subcommands across
|
|
69
|
-
* methods or keep handling them in one, but never both by accident.
|
|
70
|
-
*
|
|
71
|
-
* A group is never dropped on the way down: `settings notify email` does not fall back
|
|
72
|
-
* to `settings email`, because a second group could declare its own `email` and the
|
|
73
|
-
* two would be indistinguishable.
|
|
74
|
-
*
|
|
75
|
-
* @param interaction - The chat input or autocomplete interaction being dispatched.
|
|
43
|
+
* The route keys a chat input interaction can be handled by, most specific first:
|
|
44
|
+
* `settings notify email`, then `settings`. Discord names the whole interaction `settings`, so the
|
|
45
|
+
* full path comes first; a group is never skipped, since two groups may share a subcommand name.
|
|
76
46
|
* @returns The keys to look up, most specific first.
|
|
77
47
|
*/ function resolveCommandPaths(interaction) {
|
|
78
48
|
const { commandName } = interaction;
|
|
@@ -99,24 +69,14 @@ var discord_js = require('discord.js');
|
|
|
99
69
|
discord_js.ApplicationCommandOptionType.SubcommandGroup
|
|
100
70
|
]);
|
|
101
71
|
/**
|
|
102
|
-
* The value a handler
|
|
103
|
-
*
|
|
104
|
-
* Discord sends entity options as a snowflake plus a `resolved` payload, and discord.js
|
|
105
|
-
* puts that payload on the option as `user`/`role`/`channel`/`attachment`. Passing
|
|
106
|
-
* `value` alone would hand the handler a bare id string for `@user`, forcing every
|
|
107
|
-
* handler to re-fetch what the gateway already delivered.
|
|
72
|
+
* The value a handler receives for one option: the resolved user, role, channel or attachment
|
|
73
|
+
* discord.js attaches, rather than a bare snowflake to re-fetch.
|
|
108
74
|
*/ function resolveOptionValue(option) {
|
|
109
75
|
return option.attachment ?? option.channel ?? option.role ?? option.user ?? option.member ?? option.value;
|
|
110
76
|
}
|
|
111
77
|
/**
|
|
112
|
-
* Flattens
|
|
113
|
-
*
|
|
114
|
-
* Subcommand and subcommand-group options are containers, not values — for
|
|
115
|
-
* `/settings notify email true` the top level holds only `notify`. Recursing past them
|
|
116
|
-
* means a subcommand handler sees `{ email: true }`, the same shape a flat command's
|
|
117
|
-
* handler sees.
|
|
118
|
-
*
|
|
119
|
-
* @param interaction - The chat input or autocomplete interaction being dispatched.
|
|
78
|
+
* Flattens an interaction's options into the params record handlers receive, looking through
|
|
79
|
+
* subcommand and group containers, so `/settings notify email true` yields `{ email: true }`.
|
|
120
80
|
* @returns Each supplied option keyed by name, with entity options resolved.
|
|
121
81
|
*/ function resolveOptionParams(interaction) {
|
|
122
82
|
const data = interaction.options?.data;
|
|
@@ -135,14 +95,8 @@ var discord_js = require('discord.js');
|
|
|
135
95
|
return params;
|
|
136
96
|
}
|
|
137
97
|
/**
|
|
138
|
-
* The name of the option the user is
|
|
139
|
-
*
|
|
140
|
-
* `getFocused` throws when nothing is focused rather than returning null, and it is
|
|
141
|
-
* absent altogether on a hand-built test double. Neither is worth failing a dispatch
|
|
142
|
-
* over — an autocomplete with no focused option simply matches no option-specific
|
|
143
|
-
* handler.
|
|
144
|
-
*
|
|
145
|
-
* @param interaction - The autocomplete interaction being dispatched.
|
|
98
|
+
* The name of the option the user is typing, or undefined when nothing is focused; `getFocused`
|
|
99
|
+
* throws then, and hand-built test doubles lack it entirely.
|
|
146
100
|
*/ function focusedOptionName(interaction) {
|
|
147
101
|
if (typeof interaction.options?.getFocused !== 'function') return undefined;
|
|
148
102
|
try {
|
|
@@ -254,21 +208,9 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
254
208
|
/** The character a parameter will not cross, so one pattern segment maps to one value. */ const PARAM_SEPARATOR = '/';
|
|
255
209
|
/** Escapes a literal stretch of a pattern so only placeholders stay meaningful. */ const escapeLiteral = (literal)=>literal.replace(/[/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
256
210
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* for a path segment. That is what lets a value the application does not control — a
|
|
261
|
-
* uuid, an opaque vendor id, a slug — be captured whole without the author annotating
|
|
262
|
-
* anything, since a hyphen inside it is data rather than structure.
|
|
263
|
-
*
|
|
264
|
-
* It also keeps neighbouring patterns apart: `profile/{uuid}` and `profile/{uuid}/{id}`
|
|
265
|
-
* cannot both match one id, because a parameter cannot swallow the separator between
|
|
266
|
-
* them. Patterns separated by `-` instead have no such boundary, so a pair like
|
|
267
|
-
* `profile-{uuid}` and `profile-{uuid}-{id}` is ambiguous — {@link findAmbiguousRoutes}
|
|
268
|
-
* reports those at registration.
|
|
269
|
-
*
|
|
270
|
-
* @param pattern - The pattern string to parse.
|
|
271
|
-
* @returns The regex, the parameter names, and how specific the pattern is.
|
|
211
|
+
* Compiles a pattern into a regex, its parameter names and its specificity. A `{name}` matches up to
|
|
212
|
+
* the next `/`, so a uuid is captured whole and `profile/{uuid}` never overlaps `profile/{uuid}/{id}`;
|
|
213
|
+
* `-`-separated patterns can, which {@link findAmbiguousRoutes} reports at registration.
|
|
272
214
|
*/ function createRegexFromPattern(pattern) {
|
|
273
215
|
const params = [];
|
|
274
216
|
let regexPattern = '';
|
|
@@ -368,7 +310,7 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
368
310
|
if (typeof builderOrType === 'function') {
|
|
369
311
|
const builderObj = new builderOrType();
|
|
370
312
|
builderInstance = builderObj.build(commandName);
|
|
371
|
-
commandType = Reflect.getMetadata(
|
|
313
|
+
commandType = Reflect.getMetadata(enum_index.MetadataKey.CommandType, builderOrType);
|
|
372
314
|
if (!(commandType in enum_index.CommandType)) {
|
|
373
315
|
throw new Error(`Metadata for 'commandType' is missing on builder ${builderOrType.name}`);
|
|
374
316
|
}
|
|
@@ -405,19 +347,13 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
405
347
|
return Reflect.getMetadata(COMMAND_METADATA_KEY, controller);
|
|
406
348
|
}
|
|
407
349
|
/**
|
|
408
|
-
*
|
|
350
|
+
* Registers an autocomplete handler for an option of a chat input command.
|
|
409
351
|
*
|
|
410
|
-
*
|
|
411
|
-
* sends it while the user is still typing. It is not a `@Command`: nothing is
|
|
412
|
-
* registered for it — the option's own `setAutocomplete(true)` is what turns it on —
|
|
413
|
-
* and it is answered with `interaction.respond()` rather than a reply. Leaving it
|
|
414
|
-
* unhandled is not silent to the user: the client shows a loading state until the
|
|
415
|
-
* three-second window closes.
|
|
352
|
+
* Enable it on the option with `setAutocomplete(true)` and answer with `interaction.respond()`.
|
|
416
353
|
*
|
|
417
|
-
* @param commandPath - The command
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
* branching on `interaction.options.getFocused(true)`.
|
|
354
|
+
* @param commandPath - The command, such as `search` or `settings notify email` for a subcommand.
|
|
355
|
+
* @param optionName - The option to complete. Omit to handle every option, branching on
|
|
356
|
+
* `interaction.options.getFocused(true)`.
|
|
421
357
|
*
|
|
422
358
|
* @example
|
|
423
359
|
* ```typescript
|
|
@@ -439,13 +375,8 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
439
375
|
};
|
|
440
376
|
}
|
|
441
377
|
/**
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
* Handlers naming an option come first, so a command-wide handler acts as the fallback
|
|
445
|
-
* for options no specific handler claimed rather than shadowing them by declaration order.
|
|
446
|
-
*
|
|
447
|
-
* @param controller - The controller class instance.
|
|
448
|
-
* @returns The registered autocomplete handlers, most specific first.
|
|
378
|
+
* Returns a controller's autocomplete handlers, option-specific ones first.
|
|
379
|
+
* @param controller - The controller instance.
|
|
449
380
|
*/ function getAutocompleteHandlers(controller) {
|
|
450
381
|
const handlers = Reflect.getMetadata(AUTOCOMPLETE_METADATA_KEY, controller) || [];
|
|
451
382
|
return [
|
|
@@ -453,7 +384,7 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
453
384
|
].sort((a, b)=>Number(Boolean(b.optionName)) - Number(Boolean(a.optionName)));
|
|
454
385
|
}
|
|
455
386
|
/**
|
|
456
|
-
*
|
|
387
|
+
* Marks a class as a controller, to be listed in `@MeoCord({ controllers })`.
|
|
457
388
|
*
|
|
458
389
|
* @example
|
|
459
390
|
* ```typescript
|
|
@@ -463,28 +394,20 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
463
394
|
*
|
|
464
395
|
* @Command('ping', PingCommandBuilder)
|
|
465
396
|
* async ping(interaction: ChatInputCommandInteraction) {
|
|
466
|
-
*
|
|
467
|
-
* await interaction.reply(response)
|
|
397
|
+
* await interaction.reply(await this.pingService.handlePing())
|
|
468
398
|
* }
|
|
469
399
|
* }
|
|
470
400
|
* ```
|
|
471
401
|
*/ function Controller() {
|
|
472
402
|
return function(target) {
|
|
473
|
-
if (!Reflect.hasMetadata(
|
|
403
|
+
if (!Reflect.hasMetadata(enum_index.MetadataKey.Injectable, target)) {
|
|
474
404
|
inversify.injectable()(target);
|
|
475
405
|
}
|
|
476
406
|
};
|
|
477
407
|
}
|
|
478
408
|
/**
|
|
479
|
-
* Finds
|
|
480
|
-
*
|
|
481
|
-
* Patterns of different segment counts are disjoint, because a parameter cannot cross
|
|
482
|
-
* `/`. Within the same count, two patterns overlap unless some position holds literals
|
|
483
|
-
* that differ: `a/{x}/c` and `a/b/{y}` both take `a/b/c`, and neither is more literal
|
|
484
|
-
* than the other, so ranking cannot settle it either.
|
|
485
|
-
*
|
|
486
|
-
* @param patterns - The registered patterns.
|
|
487
|
-
* @returns Each ambiguous pair, once, in the order the patterns were given.
|
|
409
|
+
* Finds pairs of customId patterns that can both match one id, such as `a/{x}/c` and `a/b/{y}`.
|
|
410
|
+
* @returns Each ambiguous pair once, in the order the patterns were given.
|
|
488
411
|
*/ function findAmbiguousRoutes(patterns) {
|
|
489
412
|
const isParam = (segment)=>PLACEHOLDER_PATTERN.test(segment);
|
|
490
413
|
const segmentsOf = (pattern)=>pattern.split(PARAM_SEPARATOR);
|
|
@@ -524,6 +447,7 @@ exports.getCommandMap = getCommandMap;
|
|
|
524
447
|
exports.getMessageHandlers = getMessageHandlers;
|
|
525
448
|
exports.getReactionHandlers = getReactionHandlers;
|
|
526
449
|
exports.hasCustomId = hasCustomId;
|
|
450
|
+
exports.isCustomIdRouted = isCustomIdRouted;
|
|
527
451
|
exports.matchesCommandType = matchesCommandType;
|
|
528
452
|
exports.resolveCommandPaths = resolveCommandPaths;
|
|
529
453
|
exports.resolveOptionParams = resolveOptionParams;
|
|
@@ -13,17 +13,9 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
|
|
|
13
13
|
let cachedConfig;
|
|
14
14
|
let configLoaded = false;
|
|
15
15
|
/**
|
|
16
|
-
* Loads the configuration a built application runs with
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* Reads only the compiled file, which is plain JavaScript and needs no transpiler, tsconfig or
|
|
20
|
-
* source. Reading `meocord.config.ts` is {@link loadMeoCordSourceConfig}'s job, and it lives in its
|
|
21
|
-
* own module because it needs jiti: this one is imported by the logger and the factory, so a
|
|
22
|
-
* bot built with `bundleDependencies` bundles whatever it imports.
|
|
23
|
-
*
|
|
24
|
-
* The result is cached after the first load.
|
|
25
|
-
*
|
|
26
|
-
* @returns The configuration, or undefined when there is no compiled config or it fails to load.
|
|
16
|
+
* Loads the configuration a built application runs with, `dist/meocord.config.mjs`, and caches it.
|
|
17
|
+
* Imports no transpiler, since the logger and the factory import this module into every bot.
|
|
18
|
+
* @returns The configuration, or undefined when the compiled config is missing or fails to load.
|
|
27
19
|
*/ function loadMeoCordConfig() {
|
|
28
20
|
if (configLoaded) return cachedConfig;
|
|
29
21
|
configLoaded = true;
|
|
@@ -39,7 +31,7 @@ let configLoaded = false;
|
|
|
39
31
|
try {
|
|
40
32
|
// Called through a variable so a bundler does not try to resolve the path while building the
|
|
41
33
|
// application; the file is only there once the build is done.
|
|
42
|
-
const load = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-
|
|
34
|
+
const load = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-C8w8ivsR.cjs', document.baseURI).href)));
|
|
43
35
|
const loaded = load(compiledPath);
|
|
44
36
|
return loaded.default ?? loaded;
|
|
45
37
|
} catch (error) {
|