meocord 3.2.1 → 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.
Files changed (54) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +1 -1
  3. package/README.md +167 -52
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/dist/cjs/_shared/{controller.decorator-MUHA_A3z.cjs → controller.decorator-9nIxOp9y.cjs} +1 -1
  6. package/dist/cjs/_shared/{metadata-key.enum-BzzvGUId.cjs → metadata-key.enum-aABGdodb.cjs} +0 -4
  7. package/dist/cjs/_shared/{theme-Bz-D4RbT.cjs → theme-BXdSoifk.cjs} +4 -15
  8. package/dist/cjs/common/index.cjs +1 -5
  9. package/dist/cjs/core/index.cjs +67 -5
  10. package/dist/cjs/decorator/index.cjs +2 -2
  11. package/dist/cjs/enum/index.cjs +1 -1
  12. package/dist/cjs/testing/index.cjs +2 -6
  13. package/dist/esm/bin/app-template/meocord.config.ts.template +8 -13
  14. package/dist/esm/bin/app-template/package.json.template +12 -12
  15. package/dist/esm/bin/app-template/vitest.config.ts.template +6 -2
  16. package/dist/esm/bin/builder-template/builder/context-menu.builder.template +2 -2
  17. package/dist/esm/bin/builder-template/builder/primary-entry-point.builder.template +2 -2
  18. package/dist/esm/bin/builder-template/builder/slash.builder.template +2 -2
  19. package/dist/esm/bin/builder-template/controller/autocomplete.controller.template +4 -3
  20. package/dist/esm/bin/builder-template/controller/context-menu.controller.template +2 -2
  21. package/dist/esm/bin/builder-template/controller/primary-entry-point.controller.template +2 -2
  22. package/dist/esm/bin/builder-template/controller/slash.controller.template +2 -2
  23. package/dist/esm/bin/helper/controller-generator.helper.js +43 -26
  24. package/dist/esm/bin/helper/guard-generator.helper.js +8 -2
  25. package/dist/esm/bin/helper/service-generator.helper.js +8 -2
  26. package/dist/esm/bin/meocord.js +166 -142
  27. package/dist/esm/build/native-addons.js +235 -0
  28. package/dist/esm/build/rsbuild-config.js +169 -0
  29. package/dist/esm/common/decorator.js +0 -4
  30. package/dist/esm/common/theme.js +1 -5
  31. package/dist/esm/core/meocord-factory.js +4 -0
  32. package/dist/esm/enum/controller.enum.js +1 -5
  33. package/dist/esm/enum/metadata-key.enum.js +0 -4
  34. package/dist/esm/package.json.js +1 -1
  35. package/dist/esm/testing/mock-fn.js +1 -5
  36. package/dist/esm/util/generator-cli.util.js +54 -10
  37. package/dist/esm/util/json.util.js +0 -4
  38. package/dist/esm/util/meocord-cli.util.js +1 -1
  39. package/dist/esm/util/meocord-config-loader.util.js +14 -2
  40. package/dist/esm/util/platform.util.js +66 -0
  41. package/dist/esm/util/runtime.util.js +20 -7
  42. package/dist/esm/util/tsconfig.util.js +1 -1
  43. package/dist/esm/util/wait.util.js +1 -5
  44. package/dist/types/common/index.d.ts +0 -16
  45. package/dist/types/core/index.d.ts +0 -12
  46. package/dist/types/decorator/index.d.ts +1 -31
  47. package/dist/types/enum/index.d.ts +0 -5
  48. package/dist/types/interface/index.d.ts +46 -57
  49. package/dist/types/testing/index.d.ts +4 -19
  50. package/meocord.eslint.cjs +0 -6
  51. package/meocord.eslint.d.ts +0 -6
  52. package/meocord.eslint.mjs +0 -6
  53. package/package.json +25 -27
  54. package/webpack.config.js +0 -147
package/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # meocord
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
+
63
+ ## 3.2.2
64
+
65
+ ### Patch Changes
66
+
67
+ - [#13](https://github.com/l7aromeo/meocord/pull/13) [`f8a6b15`](https://github.com/l7aromeo/meocord/commit/f8a6b156e8d22abf88443ba77c605d6d47991ab4) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Update the shipped dependencies — `@clack/prompts` 1.8.0, `@swc/core` 1.16.2, and `webpack`
68
+ 5.110.3 — and state the copyright as `2025-present`, including in the notice the CLI prints
69
+ under `--license` and in its help banner.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Ukasyah Rahmatullah Zada
3
+ Copyright (c) 2025-present Ukasyah Rahmatullah Zada
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,7 +1,14 @@
1
1
  # MeoCord Framework
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/meocord.svg)](https://www.npmjs.com/package/meocord)
4
+ [![CI](https://github.com/l7aromeo/meocord/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/l7aromeo/meocord/actions/workflows/release.yml)
5
+ [![node](https://img.shields.io/node/v/meocord)](https://www.npmjs.com/package/meocord)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
7
+
3
8
  **MeoCord** is a decorator-based Discord bot framework built on top of discord.js. It brings a NestJS-style architecture — controllers, services, guards, and dependency injection — to bot development, with a full CLI, TypeScript-first design, and testing utilities included out of the box.
4
9
 
10
+ > **Upgrading from 3.x?** Follow the [migration guide](https://github.com/l7aromeo/meocord/blob/main/docs/MIGRATING.md).
11
+
5
12
  ---
6
13
 
7
14
  ## Table of Contents
@@ -35,10 +42,10 @@
35
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.
36
43
  - **Dependency injection** — Built on Inversify. Services are wired into controllers automatically; no manual instantiation or service locators.
37
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.
38
- - **Full CLI** — `meocord create`, `build`, `start`, `generate`. Scaffolds controllers, services, and guards; handles Webpack builds for both development and production.
45
+ - **Full CLI** — `meocord create`, `build`, `start`, `generate`. Scaffolds controllers, services, and guards; builds with Rsbuild for both development and production.
39
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.
40
47
  - **TypeScript-first** — Strict types throughout. Decorator metadata, `DeepMocked<T>` for test mocks, and typed config interfaces included.
41
- - **Extensible build** — Expose a Webpack config hook in `meocord.config.ts` to add rules, plugins, or loaders without ejecting.
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`.
42
49
 
43
50
  ---
44
51
 
@@ -47,8 +54,9 @@
47
54
  ### Prerequisites
48
55
 
49
56
  - **Runtime**: Node.js 22 or newer, or Bun 1.x+
50
- - **TypeScript**: 5.0+
57
+ - **TypeScript**: 5.0+ with `skipLibCheck` enabled, as generated apps have it; 5.8+ with it off
51
58
  - **Package manager**: npm, yarn, pnpm, or bun
59
+ - **Peer dependencies**: `discord.js` 14 and `dotenv` 18 — `meocord create` installs both
52
60
 
53
61
  MeoCord ships dual ESM/CJS builds. New projects generated by the CLI are preconfigured for ESM.
54
62
 
@@ -152,6 +160,9 @@ export class App {}
152
160
 
153
161
  ## Project Structure
154
162
 
163
+ <details>
164
+ <summary><b>The generated file tree</b></summary>
165
+
155
166
  ```
156
167
  .
157
168
  ├── README.md
@@ -200,13 +211,15 @@ export class App {}
200
211
  └── sample.service.spec.ts
201
212
  ```
202
213
 
214
+ </details>
215
+
203
216
  ---
204
217
 
205
218
  ## Configuration
206
219
 
207
220
  ### `meocord.config.ts`
208
221
 
209
- The top-level config file. At minimum it needs `discordToken`. The `webpack` hook lets you extend the build without ejecting.
222
+ The top-level config file. At minimum it needs `discordToken`. The `rsbuild` hook lets you adjust the build without ejecting.
210
223
 
211
224
  ```typescript
212
225
  import { type MeoCordConfig } from 'meocord/interface'
@@ -214,15 +227,50 @@ import { type MeoCordConfig } from 'meocord/interface'
214
227
  export default {
215
228
  appName: 'MyBot',
216
229
  discordToken: process.env.TOKEN!,
217
- webpack: config => {
218
- config.module.rules?.push({
219
- // add custom rules here
220
- })
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
+ }
221
236
  return config
222
237
  },
223
238
  } satisfies MeoCordConfig
224
239
  ```
225
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
+
226
274
  ### ESLint
227
275
 
228
276
  MeoCord exports a base ESLint config from `meocord/eslint`. Extend it as needed:
@@ -258,7 +306,7 @@ npx meocord --help
258
306
  | Command | Alias | Description |
259
307
  | ---------- | ----- | -------------------------------------- |
260
308
  | `create` | — | Scaffold a new MeoCord application |
261
- | `build` | — | Compile the application via Webpack |
309
+ | `build` | — | Compile the application via Rsbuild |
262
310
  | `start` | — | Start the application |
263
311
  | `generate` | `g` | Scaffold controllers, services, guards |
264
312
  | `show` | — | Display framework info |
@@ -307,11 +355,15 @@ Each one lands in its own directory, named after the type:
307
355
  src/controllers/<type>/
308
356
  ├── <name>.<type>.controller.ts
309
357
  ├── <name>.<type>.controller.spec.ts
310
- └── builders/sample.builder.ts # slash, context-menu and primary-entry-point only
358
+ └── builders/<name>.builder.ts # slash, context-menu and primary-entry-point only
311
359
  ```
312
360
 
313
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.
314
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
+
315
367
  `<name>` may contain `/` to nest: `npx meocord g co button "admin/ban"` writes into `src/controllers/button/admin/`.
316
368
 
317
369
  Directory layout is organisational only. Controllers are wired up by the `controllers` array on `@MeoCord()`, not by where they sit on disk.
@@ -405,7 +457,8 @@ preceded and followed by "/" or by the ends of the pattern. Write "a/{uuid}" rat
405
457
  than "a-{uuid}".
406
458
  ```
407
459
 
408
- ### Why the rule exists
460
+ <details>
461
+ <summary><b>Why the rule exists</b></summary>
409
462
 
410
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:
411
464
 
@@ -425,7 +478,10 @@ Segment counts then keep neighbours apart on their own:
425
478
 
426
479
  Each id matches exactly one of them.
427
480
 
428
- ### Overlapping patterns
481
+ </details>
482
+
483
+ <details>
484
+ <summary><b>Overlapping patterns</b></summary>
429
485
 
430
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:
431
487
 
@@ -438,7 +494,10 @@ Ties between equally literal patterns go to the one with fewer parameters. The r
438
494
 
439
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.
440
496
 
441
- ### When nothing matches
497
+ </details>
498
+
499
+ <details>
500
+ <summary><b>When nothing matches</b></summary>
442
501
 
443
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.
444
503
 
@@ -446,12 +505,17 @@ Autocomplete cannot be replied to, so an unclaimed option is answered with an em
446
505
 
447
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.
448
507
 
449
- ### Failures never take the bot down
508
+ </details>
509
+
510
+ <details>
511
+ <summary><b>Failures never take the bot down</b></summary>
450
512
 
451
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.
452
514
 
453
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.
454
516
 
517
+ </details>
518
+
455
519
  ---
456
520
 
457
521
  ## Subcommands
@@ -642,7 +706,8 @@ const module = MeoCordTestingModule.create({
642
706
  const controller = module.get(GreetingSlashController)
643
707
  ```
644
708
 
645
- ### `createMockInteraction`
709
+ <details>
710
+ <summary><b><code>createMockInteraction</code></b></summary>
646
711
 
647
712
  Creates a smart mock instance of any discord.js class. The full prototype chain is preserved so `instanceof` checks pass at every level.
648
713
 
@@ -713,7 +778,10 @@ const contextMenu = createMockInteraction(UserContextMenuCommandInteraction, {
713
778
 
714
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.
715
780
 
716
- ### `createChatInputOptions`
781
+ </details>
782
+
783
+ <details>
784
+ <summary><b><code>createChatInputOptions</code></b></summary>
717
785
 
718
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.
719
787
 
@@ -764,7 +832,10 @@ Omit it and `getFocused` throws, the same as the real resolver does when no opti
764
832
 
765
833
  All methods are mock functions — override any per test with `.mockReturnValue()`.
766
834
 
767
- ### `createMockUser` / `createMockClient` / `createMockGuild` / `createMockChannel`
835
+ </details>
836
+
837
+ <details>
838
+ <summary><b><code>createMockUser</code> / <code>createMockClient</code> / <code>createMockGuild</code> / <code>createMockChannel</code></b></summary>
768
839
 
769
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.
770
841
 
@@ -783,7 +854,10 @@ await (client.users as any).fetch('user-123')
783
854
  expect((client.users as any).fetch).toHaveBeenCalledWith('user-123')
784
855
  ```
785
856
 
786
- ### `createMockMessage`
857
+ </details>
858
+
859
+ <details>
860
+ <summary><b><code>createMockMessage</code></b></summary>
787
861
 
788
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.
789
863
 
@@ -806,7 +880,10 @@ edited.delete // → a mock fn
806
880
  expect(msg.delete).toHaveBeenCalledTimes(1)
807
881
  ```
808
882
 
809
- ### `createMock`
883
+ </details>
884
+
885
+ <details>
886
+ <summary><b><code>createMock</code></b></summary>
810
887
 
811
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.
812
889
 
@@ -829,7 +906,10 @@ expect(greetingService.buildGreeting).toHaveBeenCalledWith('Alice')
829
906
 
830
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.
831
908
 
832
- ### `overrideGuard`
909
+ </details>
910
+
911
+ <details>
912
+ <summary><b><code>overrideGuard</code></b></summary>
833
913
 
834
914
  Replaces a guard class in the DI container with a stub. No guard dependencies need to be provided.
835
915
 
@@ -840,14 +920,17 @@ const module = MeoCordTestingModule.create({
840
920
  })
841
921
  .overrideGuard(MetricsGuard)
842
922
  .useValue({ canActivate: () => true })
843
- .overrideGuard(RateLimiterGuard)
923
+ .overrideGuard(RateLimitGuard)
844
924
  .useValue({ canActivate: () => true })
845
925
  .compile()
846
926
  ```
847
927
 
848
928
  `canActivate: () => true` allows the method to run. `() => false` blocks it. Multiple guards chain fluently.
849
929
 
850
- ### `overrideProvider`
930
+ </details>
931
+
932
+ <details>
933
+ <summary><b><code>overrideProvider</code></b></summary>
851
934
 
852
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.
853
936
 
@@ -861,7 +944,10 @@ const module = MeoCordTestingModule.create({
861
944
  .compile()
862
945
  ```
863
946
 
864
- ### Full example
947
+ </details>
948
+
949
+ <details>
950
+ <summary><b>Full example</b></summary>
865
951
 
866
952
  ```typescript
867
953
  import {
@@ -887,7 +973,7 @@ describe('GreetingSlashController', () => {
887
973
  controllers: [GreetingSlashController],
888
974
  providers: [{ provide: GreetingService, useValue: greetingService }],
889
975
  })
890
- .overrideGuard(RateLimiterGuard)
976
+ .overrideGuard(RateLimitGuard)
891
977
  .useValue({ canActivate: () => true })
892
978
  .compile()
893
979
 
@@ -908,6 +994,8 @@ describe('GreetingSlashController', () => {
908
994
  })
909
995
  ```
910
996
 
997
+ </details>
998
+
911
999
  ---
912
1000
 
913
1001
  ## Deployment
@@ -943,6 +1031,47 @@ Start in production:
943
1031
  npx meocord start --prod
944
1032
  ```
945
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
+
946
1075
  ### Which runtime the bot runs on
947
1076
 
948
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:
@@ -958,7 +1087,8 @@ That matters for more than tidiness. Pinning `node` would oblige a bun-only imag
958
1087
 
959
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.
960
1089
 
961
- #### Running the CLI itself on bun
1090
+ <details>
1091
+ <summary><b>Running the CLI itself on bun</b></summary>
962
1092
 
963
1093
  The resolution above decides what the _bot_ runs on. The CLI process is decided earlier,
964
1094
  by the interpreter line `#!/usr/bin/env node`, which nothing in the package can influence
@@ -991,7 +1121,10 @@ bun = true
991
1121
 
992
1122
  Then plain `bun run start` runs the CLI and the bot on bun, and node need not exist.
993
1123
 
994
- #### Pinning a specific binary
1124
+ </details>
1125
+
1126
+ <details>
1127
+ <summary><b>Pinning a specific binary</b></summary>
995
1128
 
996
1129
  To override both signals — a particular install, or a different runtime for comparison — set `MEOCORD_RUNTIME`:
997
1130
 
@@ -999,44 +1132,26 @@ To override both signals — a particular install, or a different runtime for co
999
1132
  MEOCORD_RUNTIME=/usr/local/bin/bun npm run start
1000
1133
  ```
1001
1134
 
1135
+ </details>
1136
+
1002
1137
  ---
1003
1138
 
1004
1139
  ## Contributing
1005
1140
 
1006
- 1. Fork the repository
1007
- 2. Create a feature branch: `git checkout -b feat/your-feature`
1008
- 3. Commit with conventional commits: `git commit -m "feat: add X"`
1009
- 4. Run `bun run lint` and `bun run test` before pushing
1010
- 5. If you touched anything under `src/bin/`, also run `bun run build && bun run verify:generated`
1011
- 6. Push and open a pull request against `main`
1012
-
1013
- Include a description of what changed and why, and add tests for any new behaviour.
1014
-
1015
- `verify:generated` generates one controller of every type through the built CLI — flat and nested, in separate throwaway projects — and typechecks the result against the published package. Rendering a template says nothing about whether the code it produces compiles, and two bugs lived behind exactly that gap. It runs in CI as part of the Build job, so you do not have to remember it; running it locally is just faster than waiting.
1016
-
1017
- ### Commit messages and releases
1141
+ Issues, questions, and pull requests are welcome. [CONTRIBUTING.md](./CONTRIBUTING.md) covers getting set up, what each check exists to catch, and how releases work — a change that reaches the published package carries a [changeset](https://github.com/changesets/changesets), and merging the release pull request is what publishes it.
1018
1142
 
1019
- Commit messages drive versioning through [semantic-release](https://semantic-release.gitbook.io/). Only these publish:
1020
-
1021
- | Prefix | Release |
1022
- | ------------------------------ | ------- |
1023
- | `feat:` | minor |
1024
- | `fix:` | patch |
1025
- | `perf:` | patch |
1026
- | `BREAKING CHANGE:` in the body | major |
1027
-
1028
- Everything else — `docs:`, `test:`, `ci:`, `chore:`, `refactor:`, `style:` — lands on `main` without publishing and ships with whatever releasable commit comes next.
1029
-
1030
- Pick the prefix by what reaches the installed package, not by which file you edited. JSDoc is compiled into the published `.d.ts` and is what a user reads in their editor, so correcting a wrong `@example` is a `fix:` even though you only touched a comment. A README-only change is `docs:`.
1143
+ Participation is governed by the [Code of Conduct](./CODE_OF_CONDUCT.md). For vulnerabilities, follow [SECURITY.md](./SECURITY.md) rather than opening an issue.
1031
1144
 
1032
1145
  ---
1033
1146
 
1034
1147
  ## Release Notes
1035
1148
 
1036
- Full changelog is available on the [GitHub Releases](https://github.com/l7aromeo/meocord/releases) page.
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).
1037
1150
 
1038
1151
  ---
1039
1152
 
1040
1153
  ## License
1041
1154
 
1042
- **MeoCord Framework** is licensed under the [MIT License](./LICENSE).
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.
@@ -3,7 +3,7 @@
3
3
  require('reflect-metadata');
4
4
  var inversify = require('inversify');
5
5
  var enum_index = require('../enum/index.cjs');
6
- var metadataKey_enum = require('./metadata-key.enum-BzzvGUId.cjs');
6
+ var metadataKey_enum = require('./metadata-key.enum-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 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 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-Bz-D4RbT.cjs', document.baseURI).href)), {
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-Bz-D4RbT.cjs', document.baseURI).href)), {
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 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-Bz-D4RbT.cjs');
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 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