meocord 3.2.2 → 4.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/README.md +160 -28
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/cjs/_shared/{controller.decorator-eT0oHb8D.cjs → controller.decorator-9nIxOp9y.cjs} +1 -1
- package/dist/cjs/_shared/{metadata-key.enum-DITxfZlw.cjs → metadata-key.enum-aABGdodb.cjs} +0 -4
- package/dist/cjs/_shared/{theme-EmG1ZEwV.cjs → theme-BXdSoifk.cjs} +4 -15
- package/dist/cjs/common/index.cjs +1 -5
- package/dist/cjs/core/index.cjs +67 -5
- package/dist/cjs/decorator/index.cjs +2 -2
- package/dist/cjs/enum/index.cjs +1 -1
- package/dist/cjs/testing/index.cjs +2 -6
- package/dist/esm/bin/app-template/meocord.config.ts.template +8 -13
- package/dist/esm/bin/app-template/package.json.template +12 -12
- package/dist/esm/bin/app-template/vitest.config.ts.template +6 -2
- package/dist/esm/bin/builder-template/builder/context-menu.builder.template +2 -2
- package/dist/esm/bin/builder-template/builder/primary-entry-point.builder.template +2 -2
- package/dist/esm/bin/builder-template/builder/slash.builder.template +2 -2
- package/dist/esm/bin/builder-template/controller/autocomplete.controller.template +4 -3
- package/dist/esm/bin/builder-template/controller/context-menu.controller.template +2 -2
- package/dist/esm/bin/builder-template/controller/primary-entry-point.controller.template +2 -2
- package/dist/esm/bin/builder-template/controller/slash.controller.template +2 -2
- package/dist/esm/bin/helper/controller-generator.helper.js +43 -26
- package/dist/esm/bin/helper/guard-generator.helper.js +8 -2
- package/dist/esm/bin/helper/service-generator.helper.js +8 -2
- package/dist/esm/bin/meocord.js +160 -140
- package/dist/esm/build/native-addons.js +235 -0
- package/dist/esm/build/rsbuild-config.js +169 -0
- package/dist/esm/common/decorator.js +0 -4
- package/dist/esm/common/theme.js +1 -5
- package/dist/esm/core/meocord-factory.js +4 -0
- package/dist/esm/enum/controller.enum.js +1 -5
- package/dist/esm/enum/metadata-key.enum.js +0 -4
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/mock-fn.js +1 -5
- package/dist/esm/util/generator-cli.util.js +54 -10
- package/dist/esm/util/json.util.js +0 -4
- package/dist/esm/util/meocord-config-loader.util.js +14 -2
- package/dist/esm/util/platform.util.js +66 -0
- package/dist/esm/util/runtime.util.js +20 -7
- package/dist/esm/util/tsconfig.util.js +1 -1
- package/dist/esm/util/wait.util.js +1 -5
- package/dist/types/common/index.d.ts +0 -16
- package/dist/types/core/index.d.ts +0 -12
- package/dist/types/decorator/index.d.ts +1 -31
- package/dist/types/enum/index.d.ts +0 -5
- package/dist/types/interface/index.d.ts +46 -57
- package/dist/types/testing/index.d.ts +4 -19
- package/meocord.eslint.cjs +0 -6
- package/meocord.eslint.d.ts +0 -6
- package/meocord.eslint.mjs +0 -6
- package/package.json +19 -23
- package/webpack.config.js +0 -147
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# meocord
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#22](https://github.com/l7aromeo/meocord/pull/22) [`4deb96f`](https://github.com/l7aromeo/meocord/commit/4deb96fd73f86a29c46b9bb080c13e6267246cb1) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Build with [Rsbuild](https://rsbuild.rs) instead of webpack. Production builds are several times
|
|
8
|
+
faster, and webpack and its four loader and plugin packages are no longer installed with MeoCord.
|
|
9
|
+
The output is unchanged: `dist/main.js`, assets under `dist/assets/` with the same names, and the
|
|
10
|
+
same source maps.
|
|
11
|
+
|
|
12
|
+
**Breaking:** the `webpack` hook in `meocord.config.ts` is replaced by `rsbuild`, which receives
|
|
13
|
+
Rsbuild's configuration. A config that still declares `webpack` stops the build with a message
|
|
14
|
+
saying so. `MeoCordWebpackConfig` is removed; import `RsbuildConfig` from `meocord/interface`
|
|
15
|
+
instead. The [migration guide](https://github.com/l7aromeo/meocord/blob/main/docs/MIGRATING.md#2-replace-the-webpack-hook-with-rsbuild) shows where each
|
|
16
|
+
webpack setting goes.
|
|
17
|
+
|
|
18
|
+
- [#19](https://github.com/l7aromeo/meocord/pull/19) [`204c7be`](https://github.com/l7aromeo/meocord/commit/204c7bec74886c931732cb771d9178b47fbec5e8) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Require dotenv 18. The `dotenv` peer dependency moves from `^17.4.2` to `^18.0.3`, so install
|
|
19
|
+
`dotenv@18` alongside MeoCord 4. No application code changes — `import 'dotenv/config'` behaves
|
|
20
|
+
the same. See the [migration guide](https://github.com/l7aromeo/meocord/blob/main/docs/MIGRATING.md#1-upgrade-dotenv-to-18).
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- [#22](https://github.com/l7aromeo/meocord/pull/22) [`4deb96f`](https://github.com/l7aromeo/meocord/commit/4deb96fd73f86a29c46b9bb080c13e6267246cb1) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Add `bundleDependencies`, which puts everything a bot needs inside `dist`, so it deploys without
|
|
25
|
+
`node_modules` or an install step. Plain JavaScript dependencies are bundled into `main.js`. Native
|
|
26
|
+
addons such as `sharp` are found while building and copied, with their platform binary, into
|
|
27
|
+
`dist/node_modules` — nothing has to be listed.
|
|
28
|
+
|
|
29
|
+
A build carrying native addons records its platform in `dist/meocord.platform.json`, and a bot
|
|
30
|
+
started on another platform stops before going online with a message naming both. Build on the
|
|
31
|
+
platform you deploy to. See
|
|
32
|
+
[Self-contained builds](https://github.com/l7aromeo/meocord#self-contained-builds).
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- [#22](https://github.com/l7aromeo/meocord/pull/22) [`4deb96f`](https://github.com/l7aromeo/meocord/commit/4deb96fd73f86a29c46b9bb080c13e6267246cb1) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Read `meocord.config.ts` on every build. `meocord build` read the compiled `dist/meocord.config.mjs`
|
|
37
|
+
left by the previous build, so a config edit took effect one build late, and the watcher's reload
|
|
38
|
+
on a config change reloaded nothing.
|
|
39
|
+
|
|
40
|
+
- [#22](https://github.com/l7aromeo/meocord/pull/22) [`4deb96f`](https://github.com/l7aromeo/meocord/commit/4deb96fd73f86a29c46b9bb080c13e6267246cb1) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Start the bot with `bun --no-install` from `meocord start`. Without it, bun downloads any package it
|
|
41
|
+
cannot find while the bot runs, which a bot deployed without `node_modules` would do in
|
|
42
|
+
production. If you start `dist/main.js` with bun yourself, pass the flag too.
|
|
43
|
+
|
|
44
|
+
- [#25](https://github.com/l7aromeo/meocord/pull/25) [`2179f85`](https://github.com/l7aromeo/meocord/commit/2179f85c2912d45e3fefbf996ca267c19a1a773c) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Stop `meocord generate` overwriting files. Slash, context-menu and primary entry point controllers
|
|
45
|
+
all wrote one shared `builders/sample.builder.ts`, so generating a second controller replaced a
|
|
46
|
+
builder you had already edited. Each controller now gets its own `builders/<name>.builder.ts`
|
|
47
|
+
exporting `<Name>CommandBuilder`, and registers a command named after it — `admin/ban` registers
|
|
48
|
+
`admin-ban` — rather than every one registering `sample-slash`. Generating a controller, service or
|
|
49
|
+
guard refuses if any file it would write already exists.
|
|
50
|
+
|
|
51
|
+
- [#24](https://github.com/l7aromeo/meocord/pull/24) [`5e2a54b`](https://github.com/l7aromeo/meocord/commit/5e2a54b67a1f9f51c4c3a3e8cf51247de2ac2528) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Type `deleted` on `createMockMessage()`. The mock tracks and documents it, but it was missing from
|
|
52
|
+
the type, so `message.deleted` did not compile.
|
|
53
|
+
|
|
54
|
+
- [#24](https://github.com/l7aromeo/meocord/pull/24) [`5e2a54b`](https://github.com/l7aromeo/meocord/commit/5e2a54b67a1f9f51c4c3a3e8cf51247de2ac2528) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Accept slash command builders that add options. `@CommandBuilder(CommandType.SLASH)` rejected
|
|
55
|
+
`new SlashCommandBuilder().addStringOption(...)`, whose type narrows to
|
|
56
|
+
`SlashCommandOptionsOnlyBuilder`, so the most common builder — one command with an option — did
|
|
57
|
+
not compile.
|
|
58
|
+
|
|
59
|
+
- [#21](https://github.com/l7aromeo/meocord/pull/21) [`bebf116`](https://github.com/l7aromeo/meocord/commit/bebf1168466595546017a317b62acd3707ff2d1c) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Generate applications with current test tooling: vitest and `@vitest/coverage-istanbul` 5,
|
|
60
|
+
`unplugin-swc` 2, and current eslint, prettier and typescript-eslint. A new application's
|
|
61
|
+
`test:coverage` no longer fails on its decorated entry files.
|
|
62
|
+
|
|
3
63
|
## 3.2.2
|
|
4
64
|
|
|
5
65
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
**MeoCord** is a decorator-based Discord bot framework built on top of discord.js. It brings a NestJS-style architecture — controllers, services, guards, and dependency injection — to bot development, with a full CLI, TypeScript-first design, and testing utilities included out of the box.
|
|
9
9
|
|
|
10
|
+
> **Upgrading from 3.x?** Follow the [migration guide](https://github.com/l7aromeo/meocord/blob/main/docs/MIGRATING.md).
|
|
11
|
+
|
|
10
12
|
---
|
|
11
13
|
|
|
12
14
|
## Table of Contents
|
|
@@ -40,10 +42,10 @@
|
|
|
40
42
|
- **Decorator-based controllers** — Handle every Discord interaction type — slash commands and their subcommands, autocomplete, buttons, modals, all five select menus, context menus, activity entry points, messages, and reactions — with `@Command`, `@Autocomplete`, `@Controller`, and `@UseGuard` decorators. No routing boilerplate.
|
|
41
43
|
- **Dependency injection** — Built on Inversify. Services are wired into controllers automatically; no manual instantiation or service locators.
|
|
42
44
|
- **Guard system** — Pre-execution hooks for auth, rate limiting, metrics, and anything else. Apply per-method or per-class with `@UseGuard`. Guards receive the full interaction context.
|
|
43
|
-
- **Full CLI** — `meocord create`, `build`, `start`, `generate`. Scaffolds controllers, services, and guards;
|
|
45
|
+
- **Full CLI** — `meocord create`, `build`, `start`, `generate`. Scaffolds controllers, services, and guards; builds with Rsbuild for both development and production.
|
|
44
46
|
- **Testing utilities** — `MeoCordTestingModule`, `createMockInteraction`, `createMockMessage`, `createMockUser`, `createMockClient`, `createMockGuild`, `createMockChannel`, `createChatInputOptions`, and `overrideGuard` let you test controllers against real guard logic without a Discord connection. Type guards and reply state machines work out of the box.
|
|
45
47
|
- **TypeScript-first** — Strict types throughout. Decorator metadata, `DeepMocked<T>` for test mocks, and typed config interfaces included.
|
|
46
|
-
- **Extensible build** —
|
|
48
|
+
- **Extensible build** — An Rsbuild config hook in `meocord.config.ts` to adjust the build without ejecting, and an option to bundle dependencies so production runs without `node_modules`.
|
|
47
49
|
|
|
48
50
|
---
|
|
49
51
|
|
|
@@ -52,8 +54,9 @@
|
|
|
52
54
|
### Prerequisites
|
|
53
55
|
|
|
54
56
|
- **Runtime**: Node.js 22 or newer, or Bun 1.x+
|
|
55
|
-
- **TypeScript**: 5.0+
|
|
57
|
+
- **TypeScript**: 5.0+ with `skipLibCheck` enabled, as generated apps have it; 5.8+ with it off
|
|
56
58
|
- **Package manager**: npm, yarn, pnpm, or bun
|
|
59
|
+
- **Peer dependencies**: `discord.js` 14 and `dotenv` 18 — `meocord create` installs both
|
|
57
60
|
|
|
58
61
|
MeoCord ships dual ESM/CJS builds. New projects generated by the CLI are preconfigured for ESM.
|
|
59
62
|
|
|
@@ -157,6 +160,9 @@ export class App {}
|
|
|
157
160
|
|
|
158
161
|
## Project Structure
|
|
159
162
|
|
|
163
|
+
<details>
|
|
164
|
+
<summary><b>The generated file tree</b></summary>
|
|
165
|
+
|
|
160
166
|
```
|
|
161
167
|
.
|
|
162
168
|
├── README.md
|
|
@@ -205,13 +211,15 @@ export class App {}
|
|
|
205
211
|
└── sample.service.spec.ts
|
|
206
212
|
```
|
|
207
213
|
|
|
214
|
+
</details>
|
|
215
|
+
|
|
208
216
|
---
|
|
209
217
|
|
|
210
218
|
## Configuration
|
|
211
219
|
|
|
212
220
|
### `meocord.config.ts`
|
|
213
221
|
|
|
214
|
-
The top-level config file. At minimum it needs `discordToken`. The `
|
|
222
|
+
The top-level config file. At minimum it needs `discordToken`. The `rsbuild` hook lets you adjust the build without ejecting.
|
|
215
223
|
|
|
216
224
|
```typescript
|
|
217
225
|
import { type MeoCordConfig } from 'meocord/interface'
|
|
@@ -219,15 +227,50 @@ import { type MeoCordConfig } from 'meocord/interface'
|
|
|
219
227
|
export default {
|
|
220
228
|
appName: 'MyBot',
|
|
221
229
|
discordToken: process.env.TOKEN!,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
})
|
|
230
|
+
rsbuild: config => {
|
|
231
|
+
// Import .md and .html files as their text.
|
|
232
|
+
config.tools ??= {}
|
|
233
|
+
config.tools.rspack = (_rspackConfig, { addRules }) => {
|
|
234
|
+
addRules([{ test: /\.(md|html)$/i, type: 'asset/source' }])
|
|
235
|
+
}
|
|
226
236
|
return config
|
|
227
237
|
},
|
|
228
238
|
} satisfies MeoCordConfig
|
|
229
239
|
```
|
|
230
240
|
|
|
241
|
+
MeoCord builds with [Rsbuild](https://rsbuild.rs). The hook receives its configuration and returns it, modified. A few things it handles for you, so you do not need rules for them:
|
|
242
|
+
|
|
243
|
+
- **Images, fonts, svg and media** are emitted to `dist/assets/`, and importing one gives you its absolute path on disk — ready for `fs`, canvas, or a Discord attachment. Nothing is ever inlined as a data URI, whatever its size.
|
|
244
|
+
- **Custom asset paths** — `output.filename.image` (and `svg`, `font`, `media`) accept a function, for when two files share a name in different folders:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import path from 'node:path'
|
|
248
|
+
|
|
249
|
+
// ...
|
|
250
|
+
rsbuild: config => {
|
|
251
|
+
config.output ??= {}
|
|
252
|
+
config.output.filename = {
|
|
253
|
+
...config.output.filename,
|
|
254
|
+
// Keep the folder a file came from, so image/star.webp and image/hsr/star.webp do not collide.
|
|
255
|
+
// The result is relative to dist/assets/, and uses / on every platform.
|
|
256
|
+
image: ({ filename }) => path.relative('src/assets', filename ?? '').split(path.sep).join('/'),
|
|
257
|
+
}
|
|
258
|
+
return config
|
|
259
|
+
},
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
- **Raw bundler rules** go through `tools.rspack`, which takes a webpack-shaped configuration.
|
|
263
|
+
|
|
264
|
+
| Option | Default | Description |
|
|
265
|
+
| -------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
|
|
266
|
+
| `discordToken` | — | The bot token. Read it from the environment rather than writing it here. |
|
|
267
|
+
| `appName` | — | Shown in log lines. |
|
|
268
|
+
| `rsbuild` | — | `(config) => config` — adjust the Rsbuild configuration. |
|
|
269
|
+
| `bundleDependencies` | `false` | Put everything the bot needs inside `dist`, native addons included, so it runs without `node_modules`. |
|
|
270
|
+
| `externals` | `[]` | Modules to keep out of the bundle. Native addons are found without being listed. |
|
|
271
|
+
|
|
272
|
+
See [Self-contained builds](#self-contained-builds) for when to turn on `bundleDependencies`.
|
|
273
|
+
|
|
231
274
|
### ESLint
|
|
232
275
|
|
|
233
276
|
MeoCord exports a base ESLint config from `meocord/eslint`. Extend it as needed:
|
|
@@ -263,7 +306,7 @@ npx meocord --help
|
|
|
263
306
|
| Command | Alias | Description |
|
|
264
307
|
| ---------- | ----- | -------------------------------------- |
|
|
265
308
|
| `create` | — | Scaffold a new MeoCord application |
|
|
266
|
-
| `build` | — | Compile the application via
|
|
309
|
+
| `build` | — | Compile the application via Rsbuild |
|
|
267
310
|
| `start` | — | Start the application |
|
|
268
311
|
| `generate` | `g` | Scaffold controllers, services, guards |
|
|
269
312
|
| `show` | — | Display framework info |
|
|
@@ -312,11 +355,15 @@ Each one lands in its own directory, named after the type:
|
|
|
312
355
|
src/controllers/<type>/
|
|
313
356
|
├── <name>.<type>.controller.ts
|
|
314
357
|
├── <name>.<type>.controller.spec.ts
|
|
315
|
-
└── builders
|
|
358
|
+
└── builders/<name>.builder.ts # slash, context-menu and primary-entry-point only
|
|
316
359
|
```
|
|
317
360
|
|
|
318
361
|
A builder is generated only for the three types Discord registers by name. Everything else is addressed by `customId` or, for autocomplete, by the command path it completes — there is nothing to register.
|
|
319
362
|
|
|
363
|
+
Each controller gets its own builder, `<Name>CommandBuilder`, and registers a command named after it: `npx meocord g co slash Greeting` registers `/greeting`. A nested name uses its whole path, so `admin/ban` registers `/admin-ban` — Discord command names are global to the application, while folders only keep files apart. An autocomplete controller completes the slash command of the same name.
|
|
364
|
+
|
|
365
|
+
Generating never overwrites. If any file it would write already exists, it refuses, names the files, and writes nothing.
|
|
366
|
+
|
|
320
367
|
`<name>` may contain `/` to nest: `npx meocord g co button "admin/ban"` writes into `src/controllers/button/admin/`.
|
|
321
368
|
|
|
322
369
|
Directory layout is organisational only. Controllers are wired up by the `controllers` array on `@MeoCord()`, not by where they sit on disk.
|
|
@@ -410,7 +457,8 @@ preceded and followed by "/" or by the ends of the pattern. Write "a/{uuid}" rat
|
|
|
410
457
|
than "a-{uuid}".
|
|
411
458
|
```
|
|
412
459
|
|
|
413
|
-
|
|
460
|
+
<details>
|
|
461
|
+
<summary><b>Why the rule exists</b></summary>
|
|
414
462
|
|
|
415
463
|
A parameter matches anything up to the next `/`, so an identifier you do not control is captured whole — a hyphen inside a uuid is data, not structure:
|
|
416
464
|
|
|
@@ -430,7 +478,10 @@ Segment counts then keep neighbours apart on their own:
|
|
|
430
478
|
|
|
431
479
|
Each id matches exactly one of them.
|
|
432
480
|
|
|
433
|
-
|
|
481
|
+
</details>
|
|
482
|
+
|
|
483
|
+
<details>
|
|
484
|
+
<summary><b>Overlapping patterns</b></summary>
|
|
434
485
|
|
|
435
486
|
Two patterns with the same segment count can still both match. The one spelling out more literal text wins, so declaration order and file layout never decide it:
|
|
436
487
|
|
|
@@ -443,7 +494,10 @@ Ties between equally literal patterns go to the one with fewer parameters. The r
|
|
|
443
494
|
|
|
444
495
|
Where two patterns trade a literal for a parameter in opposite positions — `a/{x}/c` and `a/b/{y}` both take `a/b/c` — neither is more literal, and MeoCord logs a warning at startup naming the pair.
|
|
445
496
|
|
|
446
|
-
|
|
497
|
+
</details>
|
|
498
|
+
|
|
499
|
+
<details>
|
|
500
|
+
<summary><b>When nothing matches</b></summary>
|
|
447
501
|
|
|
448
502
|
An unroutable interaction replies "Command not found!" to the user and logs a warning naming the `customId` or command that failed to match. If a control appears dead, that log line is the first place to look.
|
|
449
503
|
|
|
@@ -451,12 +505,17 @@ Autocomplete cannot be replied to, so an unclaimed option is answered with an em
|
|
|
451
505
|
|
|
452
506
|
A handler that throws is logged and gets the same treatment — except when it had already replied or deferred, in which case MeoCord leaves the response alone rather than sending a second one Discord would reject.
|
|
453
507
|
|
|
454
|
-
|
|
508
|
+
</details>
|
|
509
|
+
|
|
510
|
+
<details>
|
|
511
|
+
<summary><b>Failures never take the bot down</b></summary>
|
|
455
512
|
|
|
456
513
|
discord.js calls event listeners without awaiting them, so anything that rejects out of one is an unhandled rejection — which terminates the process by default. MeoCord wraps every listener it registers, so one bad interaction, one unresolvable controller, or one reaction on a deleted message costs that event and nothing else. The error is logged against the event that produced it, so a genuine misconfiguration still shows up on the first interaction rather than staying hidden.
|
|
457
514
|
|
|
458
515
|
Where the failure happened before the handler ran, the user is still told: a command interaction gets the error reply, an autocomplete gets its window closed. A reaction whose message can no longer be fetched — deleted, or in a channel the bot lost access to — is skipped quietly, since that is an ordinary outcome rather than a fault.
|
|
459
516
|
|
|
517
|
+
</details>
|
|
518
|
+
|
|
460
519
|
---
|
|
461
520
|
|
|
462
521
|
## Subcommands
|
|
@@ -647,7 +706,8 @@ const module = MeoCordTestingModule.create({
|
|
|
647
706
|
const controller = module.get(GreetingSlashController)
|
|
648
707
|
```
|
|
649
708
|
|
|
650
|
-
|
|
709
|
+
<details>
|
|
710
|
+
<summary><b><code>createMockInteraction</code></b></summary>
|
|
651
711
|
|
|
652
712
|
Creates a smart mock instance of any discord.js class. The full prototype chain is preserved so `instanceof` checks pass at every level.
|
|
653
713
|
|
|
@@ -718,7 +778,10 @@ const contextMenu = createMockInteraction(UserContextMenuCommandInteraction, {
|
|
|
718
778
|
|
|
719
779
|
The override record is typed as `MockProps<T>`, exported from `meocord/testing`. Every key is optional, and a misspelled property name is a compile error.
|
|
720
780
|
|
|
721
|
-
|
|
781
|
+
</details>
|
|
782
|
+
|
|
783
|
+
<details>
|
|
784
|
+
<summary><b><code>createChatInputOptions</code></b></summary>
|
|
722
785
|
|
|
723
786
|
Builds a typed options resolver from a plain record. Type routing mirrors the real `CommandInteractionOptionResolver`: wrong-type access returns `null`, `required=true` throws if the option is absent.
|
|
724
787
|
|
|
@@ -769,7 +832,10 @@ Omit it and `getFocused` throws, the same as the real resolver does when no opti
|
|
|
769
832
|
|
|
770
833
|
All methods are mock functions — override any per test with `.mockReturnValue()`.
|
|
771
834
|
|
|
772
|
-
|
|
835
|
+
</details>
|
|
836
|
+
|
|
837
|
+
<details>
|
|
838
|
+
<summary><b><code>createMockUser</code> / <code>createMockClient</code> / <code>createMockGuild</code> / <code>createMockChannel</code></b></summary>
|
|
773
839
|
|
|
774
840
|
Convenience wrappers for common discord.js classes. All methods are auto-stubbed as mock functions. Nested managers (`client.users`, `guild.members`, etc.) are independent nested stubs.
|
|
775
841
|
|
|
@@ -788,7 +854,10 @@ await (client.users as any).fetch('user-123')
|
|
|
788
854
|
expect((client.users as any).fetch).toHaveBeenCalledWith('user-123')
|
|
789
855
|
```
|
|
790
856
|
|
|
791
|
-
|
|
857
|
+
</details>
|
|
858
|
+
|
|
859
|
+
<details>
|
|
860
|
+
<summary><b><code>createMockMessage</code></b></summary>
|
|
792
861
|
|
|
793
862
|
Creates a smart mock `Message`. Tracks a `deleted` boolean — `delete()`, `edit()`, `reply()`, `react()`, `pin()`, and `unpin()` throw if the message has already been deleted. `edit()` and `reply()` resolve to a new mock `Message` instance. All methods are mock functions.
|
|
794
863
|
|
|
@@ -811,7 +880,10 @@ edited.delete // → a mock fn
|
|
|
811
880
|
expect(msg.delete).toHaveBeenCalledTimes(1)
|
|
812
881
|
```
|
|
813
882
|
|
|
814
|
-
|
|
883
|
+
</details>
|
|
884
|
+
|
|
885
|
+
<details>
|
|
886
|
+
<summary><b><code>createMock</code></b></summary>
|
|
815
887
|
|
|
816
888
|
Mocks any type without a runtime class — use it for the services a controller depends on. `createMockInteraction` needs a class to build a prototype chain from, which is what makes `instanceof` and the real type guards work; a service double needs none of that, and an injected dependency may be an interface that does not exist at runtime at all.
|
|
817
889
|
|
|
@@ -834,7 +906,10 @@ expect(greetingService.buildGreeting).toHaveBeenCalledWith('Alice')
|
|
|
834
906
|
|
|
835
907
|
Nested access works without declaring the shape first — `cache.store.flush()` is a mock fn on a mock fn. Properties passed as `createMock<T>({ ... })` are used exactly as given rather than wrapped, so call assertions do not apply to those.
|
|
836
908
|
|
|
837
|
-
|
|
909
|
+
</details>
|
|
910
|
+
|
|
911
|
+
<details>
|
|
912
|
+
<summary><b><code>overrideGuard</code></b></summary>
|
|
838
913
|
|
|
839
914
|
Replaces a guard class in the DI container with a stub. No guard dependencies need to be provided.
|
|
840
915
|
|
|
@@ -845,14 +920,17 @@ const module = MeoCordTestingModule.create({
|
|
|
845
920
|
})
|
|
846
921
|
.overrideGuard(MetricsGuard)
|
|
847
922
|
.useValue({ canActivate: () => true })
|
|
848
|
-
.overrideGuard(
|
|
923
|
+
.overrideGuard(RateLimitGuard)
|
|
849
924
|
.useValue({ canActivate: () => true })
|
|
850
925
|
.compile()
|
|
851
926
|
```
|
|
852
927
|
|
|
853
928
|
`canActivate: () => true` allows the method to run. `() => false` blocks it. Multiple guards chain fluently.
|
|
854
929
|
|
|
855
|
-
|
|
930
|
+
</details>
|
|
931
|
+
|
|
932
|
+
<details>
|
|
933
|
+
<summary><b><code>overrideProvider</code></b></summary>
|
|
856
934
|
|
|
857
935
|
Replaces a provider already registered on the module. The value is typed as `Partial<T>`, so a double only has to cover the methods the test exercises — a class with a private member could never be satisfied by a full object literal anyway. A misspelled method name is still a compile error.
|
|
858
936
|
|
|
@@ -866,7 +944,10 @@ const module = MeoCordTestingModule.create({
|
|
|
866
944
|
.compile()
|
|
867
945
|
```
|
|
868
946
|
|
|
869
|
-
|
|
947
|
+
</details>
|
|
948
|
+
|
|
949
|
+
<details>
|
|
950
|
+
<summary><b>Full example</b></summary>
|
|
870
951
|
|
|
871
952
|
```typescript
|
|
872
953
|
import {
|
|
@@ -892,7 +973,7 @@ describe('GreetingSlashController', () => {
|
|
|
892
973
|
controllers: [GreetingSlashController],
|
|
893
974
|
providers: [{ provide: GreetingService, useValue: greetingService }],
|
|
894
975
|
})
|
|
895
|
-
.overrideGuard(
|
|
976
|
+
.overrideGuard(RateLimitGuard)
|
|
896
977
|
.useValue({ canActivate: () => true })
|
|
897
978
|
.compile()
|
|
898
979
|
|
|
@@ -913,6 +994,8 @@ describe('GreetingSlashController', () => {
|
|
|
913
994
|
})
|
|
914
995
|
```
|
|
915
996
|
|
|
997
|
+
</details>
|
|
998
|
+
|
|
916
999
|
---
|
|
917
1000
|
|
|
918
1001
|
## Deployment
|
|
@@ -948,6 +1031,47 @@ Start in production:
|
|
|
948
1031
|
npx meocord start --prod
|
|
949
1032
|
```
|
|
950
1033
|
|
|
1034
|
+
### Self-contained builds
|
|
1035
|
+
|
|
1036
|
+
By default `dist/main.js` imports its dependencies at runtime, which is why the server needs `node_modules`. Set `bundleDependencies` and the build puts everything the bot needs inside `dist` instead:
|
|
1037
|
+
|
|
1038
|
+
```typescript
|
|
1039
|
+
import { type MeoCordConfig } from 'meocord/interface'
|
|
1040
|
+
|
|
1041
|
+
export default {
|
|
1042
|
+
discordToken: process.env.TOKEN!,
|
|
1043
|
+
bundleDependencies: true,
|
|
1044
|
+
} satisfies MeoCordConfig
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
Deploying is then copying `dist/` — no `node_modules` beside it, no install step:
|
|
1048
|
+
|
|
1049
|
+
```
|
|
1050
|
+
dist/
|
|
1051
|
+
├── main.js
|
|
1052
|
+
├── assets/
|
|
1053
|
+
├── node_modules/ (native addons only, if you use any)
|
|
1054
|
+
├── package.json
|
|
1055
|
+
└── meocord.platform.json (if there are native addons)
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
Plain JavaScript dependencies are bundled into `main.js`. **Native addons** — packages that ship a compiled `.node` binary, like `sharp`, canvas bindings or database drivers — cannot be inlined into JavaScript, so MeoCord finds them itself while building, keeps them out of the bundle, and copies each one, with its platform binary and what it needs at runtime, into `dist/node_modules`. There is nothing to list: the build tells you which it packed.
|
|
1059
|
+
|
|
1060
|
+
```
|
|
1061
|
+
Native addons packed into dist: meo-canvas, sharp
|
|
1062
|
+
dist/node_modules holds 7 packages; nothing else to install.
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
**Build on the platform you deploy to.** A compiled binary only loads on the operating system, CPU and C library it was built for — a build made on a Mac carries macOS binaries, and a Debian (glibc) binary does not load on Alpine (musl). For a container, run `meocord build` inside the image. The build records its platform in `meocord.platform.json`, and a bot started somewhere else stops before going online with a message naming both, instead of failing on the first command that renders an image.
|
|
1066
|
+
|
|
1067
|
+
Use `externals` for anything you want kept out of the bundle for another reason; those are copied into `dist/node_modules` too. discord.js's optional accelerators — `zlib-sync`, `bufferutil`, `utf-8-validate` — are never bundled, are packed if you installed them, and are simply skipped by discord.js if you did not.
|
|
1068
|
+
|
|
1069
|
+
**On bun, keep it from installing at runtime.** With no `node_modules` in reach, bun downloads any package the moment something imports it. `meocord start` passes `--no-install` for you. If you launch the bundle yourself, pass it too:
|
|
1070
|
+
|
|
1071
|
+
```dockerfile
|
|
1072
|
+
CMD ["bun", "--no-install", "dist/main.js"]
|
|
1073
|
+
```
|
|
1074
|
+
|
|
951
1075
|
### Which runtime the bot runs on
|
|
952
1076
|
|
|
953
1077
|
`start` runs the bot on **the runtime you launched it with**. There is nothing to configure and no config key to set — if you typed `bun`, you get a bun process:
|
|
@@ -963,7 +1087,8 @@ That matters for more than tidiness. Pinning `node` would oblige a bun-only imag
|
|
|
963
1087
|
|
|
964
1088
|
Development works the same way. The watcher runs the bundle through the same command production does, so a runtime that works in `--dev` cannot quietly differ from the one that ships.
|
|
965
1089
|
|
|
966
|
-
|
|
1090
|
+
<details>
|
|
1091
|
+
<summary><b>Running the CLI itself on bun</b></summary>
|
|
967
1092
|
|
|
968
1093
|
The resolution above decides what the _bot_ runs on. The CLI process is decided earlier,
|
|
969
1094
|
by the interpreter line `#!/usr/bin/env node`, which nothing in the package can influence
|
|
@@ -996,7 +1121,10 @@ bun = true
|
|
|
996
1121
|
|
|
997
1122
|
Then plain `bun run start` runs the CLI and the bot on bun, and node need not exist.
|
|
998
1123
|
|
|
999
|
-
|
|
1124
|
+
</details>
|
|
1125
|
+
|
|
1126
|
+
<details>
|
|
1127
|
+
<summary><b>Pinning a specific binary</b></summary>
|
|
1000
1128
|
|
|
1001
1129
|
To override both signals — a particular install, or a different runtime for comparison — set `MEOCORD_RUNTIME`:
|
|
1002
1130
|
|
|
@@ -1004,6 +1132,8 @@ To override both signals — a particular install, or a different runtime for co
|
|
|
1004
1132
|
MEOCORD_RUNTIME=/usr/local/bin/bun npm run start
|
|
1005
1133
|
```
|
|
1006
1134
|
|
|
1135
|
+
</details>
|
|
1136
|
+
|
|
1007
1137
|
---
|
|
1008
1138
|
|
|
1009
1139
|
## Contributing
|
|
@@ -1016,10 +1146,12 @@ Participation is governed by the [Code of Conduct](./CODE_OF_CONDUCT.md). For vu
|
|
|
1016
1146
|
|
|
1017
1147
|
## Release Notes
|
|
1018
1148
|
|
|
1019
|
-
|
|
1149
|
+
Every release is recorded in [CHANGELOG.md](./CHANGELOG.md) and on the [GitHub Releases](https://github.com/l7aromeo/meocord/releases) page. Moving between major versions: the [migration guide](https://github.com/l7aromeo/meocord/blob/main/docs/MIGRATING.md).
|
|
1020
1150
|
|
|
1021
1151
|
---
|
|
1022
1152
|
|
|
1023
1153
|
## License
|
|
1024
1154
|
|
|
1025
|
-
|
|
1155
|
+
MeoCord is released under the [MIT License](./LICENSE), which covers the whole repository.
|
|
1156
|
+
|
|
1157
|
+
It builds on open-source packages under their own licenses, listed in [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Third-Party Notices
|
|
2
|
+
|
|
3
|
+
MeoCord is licensed under the [MIT License](./LICENSE). It depends on the open-source packages
|
|
4
|
+
below, each under its own license.
|
|
5
|
+
|
|
6
|
+
None of their code is copied into MeoCord's published package: npm installs each one alongside
|
|
7
|
+
MeoCord, with its own license file. This list is a record of what MeoCord relies on, and of the
|
|
8
|
+
terms those packages are offered under.
|
|
9
|
+
|
|
10
|
+
## Dependencies
|
|
11
|
+
|
|
12
|
+
Installed with MeoCord.
|
|
13
|
+
|
|
14
|
+
| Package | License | Source |
|
|
15
|
+
| ------------------------------------------------------------------ | ---------- | -------------------------------------------- |
|
|
16
|
+
| [@clack/prompts](https://www.npmjs.com/package/@clack/prompts) | MIT | https://github.com/bombshell-dev/clack |
|
|
17
|
+
| [@rsbuild/core](https://www.npmjs.com/package/@rsbuild/core) | MIT | https://github.com/web-infra-dev/rsbuild |
|
|
18
|
+
| [chalk](https://www.npmjs.com/package/chalk) | MIT | https://github.com/chalk/chalk |
|
|
19
|
+
| [cli-table3](https://www.npmjs.com/package/cli-table3) | MIT | https://github.com/cli-table/cli-table3 |
|
|
20
|
+
| [commander](https://www.npmjs.com/package/commander) | MIT | https://github.com/tj/commander.js |
|
|
21
|
+
| [dayjs](https://www.npmjs.com/package/dayjs) | MIT | https://github.com/iamkun/dayjs |
|
|
22
|
+
| [inversify](https://www.npmjs.com/package/inversify) | MIT | https://github.com/inversify/monorepo |
|
|
23
|
+
| [jiti](https://www.npmjs.com/package/jiti) | MIT | https://github.com/unjs/jiti |
|
|
24
|
+
| [lodash-es](https://www.npmjs.com/package/lodash-es) | MIT | https://github.com/lodash/lodash |
|
|
25
|
+
| [reflect-metadata](https://www.npmjs.com/package/reflect-metadata) | Apache-2.0 | https://github.com/rbuckton/reflect-metadata |
|
|
26
|
+
| [simple-git](https://www.npmjs.com/package/simple-git) | MIT | https://github.com/steveukx/git-js |
|
|
27
|
+
|
|
28
|
+
## Peer dependencies
|
|
29
|
+
|
|
30
|
+
Installed by the application using MeoCord.
|
|
31
|
+
|
|
32
|
+
| Package | License | Source |
|
|
33
|
+
| ------------------------------------------------------ | ------------ | --------------------------------------- |
|
|
34
|
+
| [discord.js](https://www.npmjs.com/package/discord.js) | Apache-2.0 | https://github.com/discordjs/discord.js |
|
|
35
|
+
| [dotenv](https://www.npmjs.com/package/dotenv) | BSD-2-Clause | https://github.com/motdotla/dotenv |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
Generated by `scripts/third-party-notices.ts` from the installed packages. Run
|
|
40
|
+
`bun run notices` after adding or removing a dependency.
|
package/dist/cjs/_shared/{controller.decorator-eT0oHb8D.cjs → controller.decorator-9nIxOp9y.cjs}
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require('reflect-metadata');
|
|
4
4
|
var inversify = require('inversify');
|
|
5
5
|
var enum_index = require('../enum/index.cjs');
|
|
6
|
-
var metadataKey_enum = require('./metadata-key.enum-
|
|
6
|
+
var metadataKey_enum = require('./metadata-key.enum-aABGdodb.cjs');
|
|
7
7
|
var discord_js = require('discord.js');
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* MeoCord Framework
|
|
5
|
-
* Copyright (c) 2025-present Ukasyah Rahmatullah Zada
|
|
6
|
-
* SPDX-License-Identifier: MIT
|
|
7
|
-
*/ /**
|
|
8
4
|
* Centralised metadata keys used across the framework's `Reflect` calls.
|
|
9
5
|
*
|
|
10
6
|
* Keeping them here prevents typos, documents the Inversify 8 key rename,
|
|
@@ -11,10 +11,6 @@ var chalk = require('chalk');
|
|
|
11
11
|
|
|
12
12
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
13
13
|
/**
|
|
14
|
-
* MeoCord Framework
|
|
15
|
-
* Copyright (c) 2025-present Ukasyah Rahmatullah Zada
|
|
16
|
-
* SPDX-License-Identifier: MIT
|
|
17
|
-
*/ /**
|
|
18
14
|
* Helper function to fix common JSON formatting issues in tsconfig.json, such as:
|
|
19
15
|
* - Removing single-line comments.
|
|
20
16
|
* - Removing trailing commas.
|
|
@@ -55,7 +51,7 @@ let configLoaded = false;
|
|
|
55
51
|
const compiledPath = path.resolve(process.cwd(), 'dist', 'meocord.config.mjs');
|
|
56
52
|
if (!fs.existsSync(compiledPath)) return undefined;
|
|
57
53
|
try {
|
|
58
|
-
const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-
|
|
54
|
+
const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-BXdSoifk.cjs', document.baseURI).href)), {
|
|
59
55
|
interopDefault: true
|
|
60
56
|
});
|
|
61
57
|
return jiti$1(compiledPath);
|
|
@@ -64,10 +60,7 @@ let configLoaded = false;
|
|
|
64
60
|
return undefined;
|
|
65
61
|
}
|
|
66
62
|
}
|
|
67
|
-
|
|
68
|
-
* Loads the source config from meocord.config.ts via jiti with tsconfig path alias resolution.
|
|
69
|
-
* Used in development mode where source files and tsconfig.json are available.
|
|
70
|
-
*/ function loadSourceConfig() {
|
|
63
|
+
function loadSourceConfig() {
|
|
71
64
|
const configPath = path.resolve(process.cwd(), 'meocord.config.ts');
|
|
72
65
|
if (!fs.existsSync(configPath)) return undefined;
|
|
73
66
|
try {
|
|
@@ -83,7 +76,7 @@ let configLoaded = false;
|
|
|
83
76
|
}
|
|
84
77
|
}
|
|
85
78
|
}
|
|
86
|
-
const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-
|
|
79
|
+
const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-BXdSoifk.cjs', document.baseURI).href)), {
|
|
87
80
|
interopDefault: true,
|
|
88
81
|
alias: aliases,
|
|
89
82
|
moduleCache: false
|
|
@@ -163,11 +156,7 @@ class Logger {
|
|
|
163
156
|
}
|
|
164
157
|
}
|
|
165
158
|
|
|
166
|
-
|
|
167
|
-
* MeoCord Framework
|
|
168
|
-
* Copyright (c) 2025-present Ukasyah Rahmatullah Zada
|
|
169
|
-
* SPDX-License-Identifier: MIT
|
|
170
|
-
*/ class Theme {
|
|
159
|
+
class Theme {
|
|
171
160
|
}
|
|
172
161
|
Theme.successColor = '#28A745';
|
|
173
162
|
Theme.infoColor = '#17A2B8';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var theme = require('../_shared/theme-
|
|
3
|
+
var theme = require('../_shared/theme-BXdSoifk.cjs');
|
|
4
4
|
require('node:util');
|
|
5
5
|
require('dayjs');
|
|
6
6
|
require('dayjs/plugin/utc.js');
|
|
@@ -11,10 +11,6 @@ require('jiti');
|
|
|
11
11
|
require('chalk');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* MeoCord Framework
|
|
15
|
-
* Copyright (c) 2025-present Ukasyah Rahmatullah Zada
|
|
16
|
-
* SPDX-License-Identifier: MIT
|
|
17
|
-
*/ /**
|
|
18
14
|
* Composes multiple class or method decorators into a single decorator.
|
|
19
15
|
*
|
|
20
16
|
* @example
|