meocord 4.0.0-beta.3 → 4.0.0-beta.4
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 +9 -0
- package/README.md +3 -1
- package/dist/cjs/_shared/{controller.decorator-9nIxOp9y.cjs → controller.decorator-CqgGgjj3.cjs} +30 -106
- package/dist/cjs/_shared/{metadata-key.enum-aABGdodb.cjs → metadata-key.enum-DG7cVMc4.cjs} +0 -1
- 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 +29 -57
- package/dist/cjs/decorator/index.cjs +36 -106
- package/dist/cjs/enum/index.cjs +4 -8
- package/dist/cjs/testing/index.cjs +82 -140
- 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/meocord.app.js +23 -46
- 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/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/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 +25 -53
- package/dist/types/core/index.d.ts +25 -53
- 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 +85 -87
- package/dist/types/testing/index.d.ts +85 -87
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# meocord
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.4
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#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
|
|
8
|
+
— `getCommandMap`, `getMessageHandlers`, `getReactionHandlers`, `getAutocompleteHandlers`,
|
|
9
|
+
`findAmbiguousRoutes` and `PARAM_SEPARATOR` — are internal to MeoCord now. See the
|
|
10
|
+
[migration guide](https://github.com/l7aromeo/meocord/blob/main/docs/MIGRATING.md#internal-helpers-are-no-longer-exported).
|
|
11
|
+
|
|
3
12
|
## 4.0.0-beta.3
|
|
4
13
|
|
|
5
14
|
### 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'
|
package/dist/cjs/_shared/{controller.decorator-9nIxOp9y.cjs → controller.decorator-CqgGgjj3.cjs}
RENAMED
|
@@ -3,18 +3,12 @@
|
|
|
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-
|
|
6
|
+
var metadataKey_enum = require('./metadata-key.enum-DG7cVMc4.cjs');
|
|
7
7
|
var discord_js = require('discord.js');
|
|
8
8
|
|
|
9
9
|
/**
|
|
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!".
|
|
10
|
+
* The discord.js class check for each command type, shared by `@Command`, the dispatcher and
|
|
11
|
+
* `CommandInteractionType`. A `CommandType` without an entry here fails to compile.
|
|
18
12
|
*/ const INTERACTION_MATCHERS = {
|
|
19
13
|
[enum_index.CommandType.SLASH]: (interaction)=>interaction instanceof discord_js.ChatInputCommandInteraction,
|
|
20
14
|
[enum_index.CommandType.CONTEXT_MENU]: (interaction)=>interaction instanceof discord_js.ContextMenuCommandInteraction,
|
|
@@ -32,47 +26,24 @@ var discord_js = require('discord.js');
|
|
|
32
26
|
enum_index.CommandType.CONTEXT_MENU,
|
|
33
27
|
enum_index.CommandType.PRIMARY_ENTRY_POINT
|
|
34
28
|
]);
|
|
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) {
|
|
29
|
+
/** Whether an interaction is the kind the given command type handles. */ function matchesCommandType(type, interaction) {
|
|
41
30
|
const matches = INTERACTION_MATCHERS[type];
|
|
42
31
|
return matches !== undefined && matches(interaction);
|
|
43
32
|
}
|
|
44
33
|
/**
|
|
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`.
|
|
34
|
+
* Whether a command type is routed by customId pattern: components carry an application-defined
|
|
35
|
+
* customId, while commands are matched by the name Discord registered.
|
|
51
36
|
*/ function isCustomIdRouted(type) {
|
|
52
37
|
return !NAME_ROUTED_TYPES.has(type);
|
|
53
38
|
}
|
|
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) {
|
|
39
|
+
/** Whether an interaction carries a customId, and so can be routed by pattern. */ function hasCustomId(interaction) {
|
|
59
40
|
return interaction instanceof discord_js.MessageComponentInteraction || interaction instanceof discord_js.ModalSubmitInteraction;
|
|
60
41
|
}
|
|
61
42
|
/** Separates a command from its subcommand group and subcommand in a route key. */ const COMMAND_PATH_SEPARATOR = ' ';
|
|
62
43
|
/**
|
|
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.
|
|
44
|
+
* The route keys a chat input interaction can be handled by, most specific first:
|
|
45
|
+
* `settings notify email`, then `settings`. Discord names the whole interaction `settings`, so the
|
|
46
|
+
* full path comes first; a group is never skipped, since two groups may share a subcommand name.
|
|
76
47
|
* @returns The keys to look up, most specific first.
|
|
77
48
|
*/ function resolveCommandPaths(interaction) {
|
|
78
49
|
const { commandName } = interaction;
|
|
@@ -99,24 +70,14 @@ var discord_js = require('discord.js');
|
|
|
99
70
|
discord_js.ApplicationCommandOptionType.SubcommandGroup
|
|
100
71
|
]);
|
|
101
72
|
/**
|
|
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.
|
|
73
|
+
* The value a handler receives for one option: the resolved user, role, channel or attachment
|
|
74
|
+
* discord.js attaches, rather than a bare snowflake to re-fetch.
|
|
108
75
|
*/ function resolveOptionValue(option) {
|
|
109
76
|
return option.attachment ?? option.channel ?? option.role ?? option.user ?? option.member ?? option.value;
|
|
110
77
|
}
|
|
111
78
|
/**
|
|
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.
|
|
79
|
+
* Flattens an interaction's options into the params record handlers receive, looking through
|
|
80
|
+
* subcommand and group containers, so `/settings notify email true` yields `{ email: true }`.
|
|
120
81
|
* @returns Each supplied option keyed by name, with entity options resolved.
|
|
121
82
|
*/ function resolveOptionParams(interaction) {
|
|
122
83
|
const data = interaction.options?.data;
|
|
@@ -135,14 +96,8 @@ var discord_js = require('discord.js');
|
|
|
135
96
|
return params;
|
|
136
97
|
}
|
|
137
98
|
/**
|
|
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.
|
|
99
|
+
* The name of the option the user is typing, or undefined when nothing is focused; `getFocused`
|
|
100
|
+
* throws then, and hand-built test doubles lack it entirely.
|
|
146
101
|
*/ function focusedOptionName(interaction) {
|
|
147
102
|
if (typeof interaction.options?.getFocused !== 'function') return undefined;
|
|
148
103
|
try {
|
|
@@ -254,21 +209,9 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
254
209
|
/** The character a parameter will not cross, so one pattern segment maps to one value. */ const PARAM_SEPARATOR = '/';
|
|
255
210
|
/** Escapes a literal stretch of a pattern so only placeholders stay meaningful. */ const escapeLiteral = (literal)=>literal.replace(/[/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
256
211
|
/**
|
|
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.
|
|
212
|
+
* Compiles a pattern into a regex, its parameter names and its specificity. A `{name}` matches up to
|
|
213
|
+
* the next `/`, so a uuid is captured whole and `profile/{uuid}` never overlaps `profile/{uuid}/{id}`;
|
|
214
|
+
* `-`-separated patterns can, which {@link findAmbiguousRoutes} reports at registration.
|
|
272
215
|
*/ function createRegexFromPattern(pattern) {
|
|
273
216
|
const params = [];
|
|
274
217
|
let regexPattern = '';
|
|
@@ -405,19 +348,13 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
405
348
|
return Reflect.getMetadata(COMMAND_METADATA_KEY, controller);
|
|
406
349
|
}
|
|
407
350
|
/**
|
|
408
|
-
*
|
|
351
|
+
* Registers an autocomplete handler for an option of a chat input command.
|
|
409
352
|
*
|
|
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.
|
|
353
|
+
* Enable it on the option with `setAutocomplete(true)` and answer with `interaction.respond()`.
|
|
416
354
|
*
|
|
417
|
-
* @param commandPath - The command
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
* branching on `interaction.options.getFocused(true)`.
|
|
355
|
+
* @param commandPath - The command, such as `search` or `settings notify email` for a subcommand.
|
|
356
|
+
* @param optionName - The option to complete. Omit to handle every option, branching on
|
|
357
|
+
* `interaction.options.getFocused(true)`.
|
|
421
358
|
*
|
|
422
359
|
* @example
|
|
423
360
|
* ```typescript
|
|
@@ -439,13 +376,8 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
439
376
|
};
|
|
440
377
|
}
|
|
441
378
|
/**
|
|
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.
|
|
379
|
+
* Returns a controller's autocomplete handlers, option-specific ones first.
|
|
380
|
+
* @param controller - The controller instance.
|
|
449
381
|
*/ function getAutocompleteHandlers(controller) {
|
|
450
382
|
const handlers = Reflect.getMetadata(AUTOCOMPLETE_METADATA_KEY, controller) || [];
|
|
451
383
|
return [
|
|
@@ -453,7 +385,7 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
453
385
|
].sort((a, b)=>Number(Boolean(b.optionName)) - Number(Boolean(a.optionName)));
|
|
454
386
|
}
|
|
455
387
|
/**
|
|
456
|
-
*
|
|
388
|
+
* Marks a class as a controller, to be listed in `@MeoCord({ controllers })`.
|
|
457
389
|
*
|
|
458
390
|
* @example
|
|
459
391
|
* ```typescript
|
|
@@ -463,8 +395,7 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
463
395
|
*
|
|
464
396
|
* @Command('ping', PingCommandBuilder)
|
|
465
397
|
* async ping(interaction: ChatInputCommandInteraction) {
|
|
466
|
-
*
|
|
467
|
-
* await interaction.reply(response)
|
|
398
|
+
* await interaction.reply(await this.pingService.handlePing())
|
|
468
399
|
* }
|
|
469
400
|
* }
|
|
470
401
|
* ```
|
|
@@ -476,15 +407,8 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
476
407
|
};
|
|
477
408
|
}
|
|
478
409
|
/**
|
|
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.
|
|
410
|
+
* Finds pairs of customId patterns that can both match one id, such as `a/{x}/c` and `a/b/{y}`.
|
|
411
|
+
* @returns Each ambiguous pair once, in the order the patterns were given.
|
|
488
412
|
*/ function findAmbiguousRoutes(patterns) {
|
|
489
413
|
const isParam = (segment)=>PLACEHOLDER_PATTERN.test(segment);
|
|
490
414
|
const segmentsOf = (pattern)=>pattern.split(PARAM_SEPARATOR);
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/ var MetadataKey = /*#__PURE__*/ function(MetadataKey) {
|
|
9
9
|
/**
|
|
10
10
|
* Set by Inversify 8's `injectable()` decorator.
|
|
11
|
-
* Renamed from the legacy `'inversify:injectable'` string used in older versions.
|
|
12
11
|
*/ MetadataKey["Injectable"] = "@inversifyjs/core/classIsInjectableFlagReflectKey";
|
|
13
12
|
/**
|
|
14
13
|
* Stores the Inversify `Container` instance on a controller class.
|
|
@@ -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) {
|
package/dist/cjs/core/index.cjs
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
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-
|
|
7
|
-
var controller_decorator = require('../_shared/controller.decorator-
|
|
6
|
+
var theme = require('../_shared/theme-C8w8ivsR.cjs');
|
|
7
|
+
var controller_decorator = require('../_shared/controller.decorator-CqgGgjj3.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
11
|
var fs = require('fs');
|
|
12
12
|
var path = require('path');
|
|
13
|
-
var metadataKey_enum = require('../_shared/metadata-key.enum-
|
|
13
|
+
var metadataKey_enum = require('../_shared/metadata-key.enum-DG7cVMc4.cjs');
|
|
14
14
|
require('node:util');
|
|
15
15
|
require('dayjs');
|
|
16
16
|
require('dayjs/plugin/utc.js');
|
|
@@ -66,20 +66,8 @@ const createErrorEmbed = (description)=>{
|
|
|
66
66
|
}
|
|
67
67
|
class MeoCordApp {
|
|
68
68
|
/**
|
|
69
|
-
* Runs an event handler so
|
|
70
|
-
*
|
|
71
|
-
* discord.js calls listeners without awaiting them, so a rejection escaping one has
|
|
72
|
-
* nothing left to settle it: Node reports an unhandled rejection, which terminates
|
|
73
|
-
* the process by default. Losing the whole bot because one reaction landed on a
|
|
74
|
-
* deleted message, or one controller could not be resolved, is a worse failure than
|
|
75
|
-
* the one that caused it -- every other user is served by the same process.
|
|
76
|
-
*
|
|
77
|
-
* Nothing is silenced. The error is logged against the event that produced it, so a
|
|
78
|
-
* genuine misconfiguration -- an unbound controller, a missing dependency -- shows
|
|
79
|
-
* up on the very first interaction rather than staying hidden.
|
|
80
|
-
*
|
|
81
|
-
* @param event - The gateway event being handled, named in the log.
|
|
82
|
-
* @param run - The handler to run.
|
|
69
|
+
* Runs an event handler so its failure is logged against the event instead of surfacing as an
|
|
70
|
+
* unhandled rejection, which would terminate the whole bot.
|
|
83
71
|
*/ async runListener(event, run) {
|
|
84
72
|
try {
|
|
85
73
|
await run();
|
|
@@ -107,14 +95,20 @@ class MeoCordApp {
|
|
|
107
95
|
return this.controllerInstancesCache.get(controllerClass);
|
|
108
96
|
}
|
|
109
97
|
/**
|
|
110
|
-
* Registers the Discord event handlers and logs in.
|
|
98
|
+
* Registers the Discord event handlers and logs the bot in.
|
|
99
|
+
*
|
|
100
|
+
* If the login fails, the process exit code is set to `1` before the promise rejects, so the
|
|
101
|
+
* process exits non-zero even when the caller catches the error to log it. A later `start()`
|
|
102
|
+
* that logs in clears that code again.
|
|
103
|
+
*
|
|
104
|
+
* @returns A promise that resolves once the bot is logged in.
|
|
105
|
+
* @throws The login error, such as an invalid token or Discord being unreachable.
|
|
111
106
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* logs in -- an entry point retrying -- clears the code again, if this is what set it.
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* const app = MeoCordFactory.create(App)
|
|
110
|
+
* await app.start()
|
|
111
|
+
* ```
|
|
118
112
|
*/ async start() {
|
|
119
113
|
this.logger.log('Starting bot...');
|
|
120
114
|
this.bot.on('clientReady', ()=>this.runListener('clientReady', async ()=>{
|
|
@@ -147,12 +141,8 @@ class MeoCordApp {
|
|
|
147
141
|
this.logger.log('Bot is online!');
|
|
148
142
|
}
|
|
149
143
|
async registerCommands() {
|
|
150
|
-
// Keyed by type and name
|
|
151
|
-
//
|
|
152
|
-
// and keying on the name alone would drop one of them from the payload. Within a
|
|
153
|
-
// single type the name is unique, so a command whose subcommands live in separate
|
|
154
|
-
// methods still contributes its builder once — sending it twice makes Discord
|
|
155
|
-
// reject the whole payload.
|
|
144
|
+
// Keyed by type and name: Discord treats that pair as one command, so a user and a message context
|
|
145
|
+
// menu may share a name, and a command split across methods sends its builder once.
|
|
156
146
|
const buildersByCommand = new Map();
|
|
157
147
|
for (const controllerClass of this.controllerClasses){
|
|
158
148
|
const instance = this.getInstance(controllerClass);
|
|
@@ -265,13 +255,8 @@ class MeoCordApp {
|
|
|
265
255
|
return routes;
|
|
266
256
|
}
|
|
267
257
|
/**
|
|
268
|
-
* Dispatches an interaction,
|
|
269
|
-
* the
|
|
270
|
-
*
|
|
271
|
-
* {@link executeCommand} already reports what a handler throws, but everything
|
|
272
|
-
* *before* the handler can fail too — resolving a controller through the container
|
|
273
|
-
* is the common case — and a component that fails there would otherwise look dead
|
|
274
|
-
* with nothing said to the user and nothing in the log.
|
|
258
|
+
* Dispatches an interaction, reporting a failure before the handler, such as resolving the
|
|
259
|
+
* controller, to the user and the log as well.
|
|
275
260
|
*/ async handleInteraction(interaction) {
|
|
276
261
|
try {
|
|
277
262
|
await this.dispatchInteraction(interaction);
|
|
@@ -344,12 +329,8 @@ class MeoCordApp {
|
|
|
344
329
|
return [];
|
|
345
330
|
}
|
|
346
331
|
/**
|
|
347
|
-
* Answers an autocomplete interaction from the `@Autocomplete` handler that claims it.
|
|
348
|
-
*
|
|
349
|
-
* Discord closes the window after three seconds and shows a loading state until
|
|
350
|
-
* something arrives, so an unclaimed option is answered with an empty list rather
|
|
351
|
-
* than left to time out -- a visibly empty menu is a better failure than a stuck one,
|
|
352
|
-
* and the warning says which option is missing a handler.
|
|
332
|
+
* Answers an autocomplete interaction from the `@Autocomplete` handler that claims it. An unclaimed
|
|
333
|
+
* option gets an empty list and a warning, rather than a menu stuck loading until Discord times out.
|
|
353
334
|
*/ async handleAutocomplete(interaction) {
|
|
354
335
|
const focusedName = controller_decorator.focusedOptionName(interaction);
|
|
355
336
|
for (const path of controller_decorator.resolveCommandPaths(interaction)){
|
|
@@ -402,12 +383,8 @@ class MeoCordApp {
|
|
|
402
383
|
}
|
|
403
384
|
}
|
|
404
385
|
/**
|
|
405
|
-
* Tells the user something went wrong, if the interaction can still
|
|
406
|
-
*
|
|
407
|
-
* A handler that replies and *then* throws is the common shape of a failure, and
|
|
408
|
-
* replying twice throws in turn -- out of the catch block, where nothing is left to
|
|
409
|
-
* handle it. Whatever the interaction's state, reporting an error must not be able
|
|
410
|
-
* to become a second, worse one.
|
|
386
|
+
* Tells the user something went wrong, if the interaction can still take a reply. Never throws, since
|
|
387
|
+
* a handler that already replied would otherwise turn one error into two.
|
|
411
388
|
*/ async replyWithError(interaction, message) {
|
|
412
389
|
if (!interaction.isRepliable() || interaction.replied || interaction.deferred) return;
|
|
413
390
|
try {
|
|
@@ -561,14 +538,9 @@ class MeoCordApp {
|
|
|
561
538
|
return !built.libc || !running.libc || built.libc === running.libc;
|
|
562
539
|
}
|
|
563
540
|
/**
|
|
564
|
-
* Stops a bundle from starting on a platform its native addons were not built for
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
* it -- for an image library, the first command that renders one -- with a dynamic linker error
|
|
568
|
-
* that names a file rather than the cause. Checking the manifest up front turns that into one
|
|
569
|
-
* clear message before the bot goes online.
|
|
570
|
-
*
|
|
571
|
-
* @param distDir - Directory holding the manifest. Defaults to the directory of the entry script.
|
|
541
|
+
* Stops a bundle from starting on a platform its native addons were not built for, with one clear
|
|
542
|
+
* message instead of a linker error on the first command that loads an addon.
|
|
543
|
+
* @param distDir - Directory holding the manifest. Defaults to the entry script's directory.
|
|
572
544
|
*/ function assertBuiltForThisPlatform(distDir = process.argv[1] ? path.dirname(process.argv[1]) : undefined) {
|
|
573
545
|
if (!distDir) return;
|
|
574
546
|
const manifest = path.join(distDir, PLATFORM_MANIFEST);
|