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.
Files changed (52) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +160 -28
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/dist/cjs/_shared/{controller.decorator-eT0oHb8D.cjs → controller.decorator-9nIxOp9y.cjs} +1 -1
  5. package/dist/cjs/_shared/{metadata-key.enum-DITxfZlw.cjs → metadata-key.enum-aABGdodb.cjs} +0 -4
  6. package/dist/cjs/_shared/{theme-EmG1ZEwV.cjs → theme-BXdSoifk.cjs} +4 -15
  7. package/dist/cjs/common/index.cjs +1 -5
  8. package/dist/cjs/core/index.cjs +67 -5
  9. package/dist/cjs/decorator/index.cjs +2 -2
  10. package/dist/cjs/enum/index.cjs +1 -1
  11. package/dist/cjs/testing/index.cjs +2 -6
  12. package/dist/esm/bin/app-template/meocord.config.ts.template +8 -13
  13. package/dist/esm/bin/app-template/package.json.template +12 -12
  14. package/dist/esm/bin/app-template/vitest.config.ts.template +6 -2
  15. package/dist/esm/bin/builder-template/builder/context-menu.builder.template +2 -2
  16. package/dist/esm/bin/builder-template/builder/primary-entry-point.builder.template +2 -2
  17. package/dist/esm/bin/builder-template/builder/slash.builder.template +2 -2
  18. package/dist/esm/bin/builder-template/controller/autocomplete.controller.template +4 -3
  19. package/dist/esm/bin/builder-template/controller/context-menu.controller.template +2 -2
  20. package/dist/esm/bin/builder-template/controller/primary-entry-point.controller.template +2 -2
  21. package/dist/esm/bin/builder-template/controller/slash.controller.template +2 -2
  22. package/dist/esm/bin/helper/controller-generator.helper.js +43 -26
  23. package/dist/esm/bin/helper/guard-generator.helper.js +8 -2
  24. package/dist/esm/bin/helper/service-generator.helper.js +8 -2
  25. package/dist/esm/bin/meocord.js +160 -140
  26. package/dist/esm/build/native-addons.js +235 -0
  27. package/dist/esm/build/rsbuild-config.js +169 -0
  28. package/dist/esm/common/decorator.js +0 -4
  29. package/dist/esm/common/theme.js +1 -5
  30. package/dist/esm/core/meocord-factory.js +4 -0
  31. package/dist/esm/enum/controller.enum.js +1 -5
  32. package/dist/esm/enum/metadata-key.enum.js +0 -4
  33. package/dist/esm/package.json.js +1 -1
  34. package/dist/esm/testing/mock-fn.js +1 -5
  35. package/dist/esm/util/generator-cli.util.js +54 -10
  36. package/dist/esm/util/json.util.js +0 -4
  37. package/dist/esm/util/meocord-config-loader.util.js +14 -2
  38. package/dist/esm/util/platform.util.js +66 -0
  39. package/dist/esm/util/runtime.util.js +20 -7
  40. package/dist/esm/util/tsconfig.util.js +1 -1
  41. package/dist/esm/util/wait.util.js +1 -5
  42. package/dist/types/common/index.d.ts +0 -16
  43. package/dist/types/core/index.d.ts +0 -12
  44. package/dist/types/decorator/index.d.ts +1 -31
  45. package/dist/types/enum/index.d.ts +0 -5
  46. package/dist/types/interface/index.d.ts +46 -57
  47. package/dist/types/testing/index.d.ts +4 -19
  48. package/meocord.eslint.cjs +0 -6
  49. package/meocord.eslint.d.ts +0 -6
  50. package/meocord.eslint.mjs +0 -6
  51. package/package.json +19 -23
  52. 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; 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.
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** — 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`.
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 `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.
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
- webpack: config => {
223
- config.module.rules?.push({
224
- // add custom rules here
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 Webpack |
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/sample.builder.ts # slash, context-menu and primary-entry-point only
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
- ### Why the rule exists
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
- ### Overlapping patterns
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
- ### When nothing matches
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
- ### Failures never take the bot down
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
- ### `createMockInteraction`
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
- ### `createChatInputOptions`
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
- ### `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>
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
- ### `createMockMessage`
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
- ### `createMock`
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
- ### `overrideGuard`
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(RateLimiterGuard)
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
- ### `overrideProvider`
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
- ### Full example
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(RateLimiterGuard)
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
- #### Running the CLI itself on bun
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
- #### Pinning a specific binary
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
- 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).
1020
1150
 
1021
1151
  ---
1022
1152
 
1023
1153
  ## License
1024
1154
 
1025
- **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-DITxfZlw.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-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-EmG1ZEwV.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-EmG1ZEwV.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-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-EmG1ZEwV.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-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