meocord 4.1.0-beta.0 → 4.1.0-beta.3
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 +38 -0
- package/README.md +106 -3
- package/dist/cjs/_shared/{cooldown-store-DTqJP31S.cjs → cooldown-store-DJ7s2HYv.cjs} +7 -4
- package/dist/cjs/_shared/{input-runner-CucAPIqF.cjs → providers-CHJHjNb9.cjs} +194 -18
- package/dist/cjs/_shared/{shard-context-84GuH11q.cjs → shard-context-_UhaAa3O.cjs} +54 -54
- package/dist/cjs/common/index.cjs +31 -1
- package/dist/cjs/core/index.cjs +153 -85
- package/dist/cjs/decorator/index.cjs +89 -60
- package/dist/cjs/testing/index.cjs +329 -71
- package/dist/esm/bin/app-template/README.md.template +2 -1
- package/dist/esm/bin/app-template/package.json.template +1 -0
- package/dist/esm/bin/app-template/tsconfig.eslint.json.template +1 -1
- package/dist/esm/bin/app-template/vitest.config.ts.template +2 -1
- package/dist/esm/bin/app-template/vitest.setup.ts.template +6 -0
- package/dist/esm/common/index.js +1 -0
- package/dist/esm/common/response/response-state.js +6 -3
- package/dist/esm/common/token.js +30 -0
- package/dist/esm/core/guard-runner.js +13 -1
- package/dist/esm/core/handler-pipeline.js +1 -1
- package/dist/esm/core/lifecycle-order.js +7 -16
- package/dist/esm/core/meocord-factory.js +69 -20
- package/dist/esm/core/meocord.app.js +47 -26
- package/dist/esm/core/providers.js +170 -0
- package/dist/esm/decorator/app.decorator.js +8 -0
- package/dist/esm/decorator/index.js +1 -0
- package/dist/esm/decorator/inject.decorator.js +24 -0
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/index.js +1 -1
- package/dist/esm/testing/meocord-testing-module.js +57 -39
- package/dist/esm/testing/mock-fn.js +38 -1
- package/dist/esm/testing/mock-interaction.js +226 -24
- package/dist/types/common/index.d.cts +31 -2
- package/dist/types/common/index.d.ts +31 -2
- package/dist/types/decorator/index.d.cts +27 -2
- package/dist/types/decorator/index.d.ts +27 -2
- package/dist/types/{errors-DwvuoFVM.d.ts → errors-C6xmj0MA.d.cts} +57 -3
- package/dist/types/{errors-DwvuoFVM.d.cts → errors-C6xmj0MA.d.ts} +57 -3
- package/dist/types/interface/index.d.cts +10 -8
- package/dist/types/interface/index.d.ts +10 -8
- package/dist/types/testing/index.d.cts +113 -27
- package/dist/types/testing/index.d.ts +113 -27
- package/meocord.eslint.cjs +22 -5
- package/meocord.eslint.mjs +22 -5
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# meocord
|
|
2
2
|
|
|
3
|
+
## 4.1.0-beta.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#133](https://github.com/meocord/meocord/pull/133) [`9153786`](https://github.com/meocord/meocord/commit/9153786e695a7d98c5d6b777e77cc07381e2c22e) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Ships every change listed under 4.1.0-beta.2, which was not published on its own. Providers in `@MeoCord({ providers })` and in the testing module's `providers` can be listed in any order: a class listed there is bound once, even when a provider earlier in the list injects it.
|
|
8
|
+
|
|
9
|
+
## 4.1.0-beta.2
|
|
10
|
+
|
|
11
|
+
Not published to npm: these changes first ship in 4.1.0-beta.3.
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [#123](https://github.com/meocord/meocord/pull/123) [`3924763`](https://github.com/meocord/meocord/commit/3924763805de2c1b6c44ca2d94e72e823956b67d) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `@MeoCord({ providers })` provides values, classes and sync or async factories under a class, string, symbol or `createToken` token, injected with the new `@Inject(token)`, with their `onReady` and `onShutdown` hooks run in dependency order; `MeoCordTestingModule` takes the same providers.
|
|
16
|
+
|
|
17
|
+
- [#132](https://github.com/meocord/meocord/pull/132) [`9dcfec5`](https://github.com/meocord/meocord/commit/9dcfec5d37b7a2f0e13a8fdf9193be80f39e994f) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `meocord/testing` exports `clearAllMocks()` and `resetAllMocks()`, which reach every mock it made: `createMockFn`, and the mocks inside `createMockInteraction`, `createMockClient` and the rest. Vitest's `clearMocks` and jest's `clearAllMocks()` only reach their own `vi.fn()` and `jest.fn()`. New projects call `resetAllMocks()` after every test from `vitest.setup.ts`. To do the same in an existing project, add `afterEach(() => resetAllMocks())` to a Vitest setup file, and set what a mock returns in the test, or in `beforeEach`, that relies on it.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- [#125](https://github.com/meocord/meocord/pull/125) [`c8584a6`](https://github.com/meocord/meocord/commit/c8584a63e9b9d0a4db629338c137bf26a96fa765) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `createMockChannel` takes `ThreadChannel`, stubs `threads.create` on text, announcement, forum and media channels, and gives a subclass the managers of the channel class it extends.
|
|
22
|
+
|
|
23
|
+
- [#130](https://github.com/meocord/meocord/pull/130) [`1b81e6d`](https://github.com/meocord/meocord/commit/1b81e6d4035e41a02d5b2cabaed503927c8e09af) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Mocks from `meocord/testing` behave more like discord.js. An interaction mock has a `locale` of `'en-US'`, and a `guildLocale` of `'en-US'` with a `guildId` and `null` without, so `t.for(interaction, { public: true })` works on a default mock. A method that returns a promise in discord.js now resolves instead of returning `undefined`, so `await` and `.catch()` work without setup. `send()` and `reply()` resolve to a mock message. A manager's `fetch(id)`, `create()` and `edit()` resolve to a mock of its item, and a list fetch to an empty `Collection`. `createDM()` resolves to a DM channel, and a structure's own `edit()`, `fetch()` and setters to the structure itself. `mockResolvedValue` and `mockRejectedValue` still override them.
|
|
24
|
+
|
|
25
|
+
- [#126](https://github.com/meocord/meocord/pull/126) [`8cee75d`](https://github.com/meocord/meocord/commit/8cee75d56a250cc86ced77325cdffa20557431eb) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `respond()` reads discord.js's deprecated `ephemeral: true` as `flags: MessageFlags.Ephemeral`, so a private follow-up on a public deferred reply is no longer shown to everyone as an edit of that reply, and tests see the privacy the bot sends; `flags: MessageFlags.Ephemeral` is the supported form.
|
|
26
|
+
|
|
27
|
+
- [#131](https://github.com/meocord/meocord/pull/131) [`b5e7e63`](https://github.com/meocord/meocord/commit/b5e7e6358bdb325fac4a21b0415922a50ec1727a) Thanks [@l7aromeo](https://github.com/l7aromeo)! - A controller or service whose constructor parameter has no runtime type now stops `MeoCordFactory.create`, and the testing module, before anything is bound. The error names the class, the parameter and the classes that inject it, and says how to fix it. This happens when two services import each other, or a parameter is typed with an interface or an `import type`; the error inversify raised before pointed at the compiler options instead. `meocord/eslint` now warns on import cycles (`import-x/no-cycle`, type-only imports ignored) in projects that have `eslint-import-resolver-typescript`, which it needs to follow imports through `@src`. New projects include it; in a project without it the check stays off and lint is unchanged. Add it with `npm i -D eslint-import-resolver-typescript` to get the warning.
|
|
28
|
+
|
|
29
|
+
## 4.1.0-beta.1
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- [#120](https://github.com/meocord/meocord/pull/120) [`9d435d9`](https://github.com/meocord/meocord/commit/9d435d9da2bc62eab012ea2b80df2c1e001a926e) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `createMockMessage()` takes an `id`, `content`, `components`, `embeds` and `flags`, so a test can put controls on the message a button sits on, such as to check what `@Defer` locks; components and embeds may be API JSON, builders or discord.js instances.
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- [#118](https://github.com/meocord/meocord/pull/118) [`5c636aa`](https://github.com/meocord/meocord/commit/5c636aa930224aef9b4670a5e16b234a3f57397d) Thanks [@l7aromeo](https://github.com/l7aromeo)! - Tools that read the installed version with `require('meocord/package.json')` no longer fail with `ERR_PACKAGE_PATH_NOT_EXPORTED`.
|
|
38
|
+
|
|
39
|
+
- [#121](https://github.com/meocord/meocord/pull/121) [`c6990c2`](https://github.com/meocord/meocord/commit/c6990c250f7694ca5adb9963e89f151b7ed5dc4b) Thanks [@l7aromeo](https://github.com/l7aromeo)! - `inGuild()`, `inCachedGuild()` and `inRawGuild()` on a mock answer from its `guildId` and `guild`, so a mock created without a `guildId` is a DM and a guard that requires a guild returns `false`, not `undefined`.
|
|
40
|
+
|
|
3
41
|
## 4.1.0-beta.0
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# MeoCord Framework
|
|
2
2
|
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/meocord/meocord/main/docs/assets/brand/banner-dark.webp">
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/meocord/meocord/main/docs/assets/brand/banner-light.webp">
|
|
6
|
+
<img alt="MeoCord: a controller method with @Command, @UseGuard and @Cooldown, answering with respond()" src="https://raw.githubusercontent.com/meocord/meocord/main/docs/assets/brand/banner.webp" width="1280">
|
|
7
|
+
</picture>
|
|
8
|
+
|
|
3
9
|
[](https://www.npmjs.com/package/meocord)
|
|
4
10
|
[](https://github.com/meocord/meocord/actions/workflows/release.yml)
|
|
5
11
|
[](https://www.npmjs.com/package/meocord)
|
|
@@ -45,6 +51,7 @@
|
|
|
45
51
|
- [Custom Decorators](#custom-decorators)
|
|
46
52
|
- [Gateway Events](#gateway-events)
|
|
47
53
|
- [Handler Discovery](#handler-discovery)
|
|
54
|
+
- [Providers](#providers)
|
|
48
55
|
- [Lifecycle Hooks](#lifecycle-hooks)
|
|
49
56
|
- [Localisation](#localisation)
|
|
50
57
|
- [Testing](#testing)
|
|
@@ -64,7 +71,7 @@
|
|
|
64
71
|
## Features
|
|
65
72
|
|
|
66
73
|
- **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`, `@MessageHandler` and `@ReactionHandler`. No routing boilerplate.
|
|
67
|
-
- **Dependency injection** — Built on Inversify. Services are wired into controllers automatically; no manual instantiation or service locators.
|
|
74
|
+
- **Dependency injection** — Built on Inversify. Services are wired into controllers automatically; no manual instantiation or service locators. Providers add values, classes and async factories under any token, injected with `@Inject`.
|
|
68
75
|
- **A request pipeline** — [Guards](#guards) decide whether a handler runs, [interceptors](#interceptors) wrap it, [validation and pipes](#validation-and-pipes) check and transform its input, [cooldowns](#cooldowns) limit how often it runs, and [exception filters](#exception-filters) decide what the user is told when something throws. Each applies to a method, a controller, or the whole bot.
|
|
69
76
|
- **Interaction responses** — `respond(interaction)` answers every interaction type correctly from any state, deferred or replied, wherever a user-installed app is used; a presenter styles MeoCord's own answers.
|
|
70
77
|
- **Cooldowns** — `@Cooldown` limits how often a handler runs, per user, server, channel or for everyone, with a pluggable store to share the count across shards.
|
|
@@ -755,7 +762,7 @@ async profile(interaction: ChatInputCommandInteraction) {
|
|
|
755
762
|
|
|
756
763
|
`state` tells where the answer stands (`'unanswered'`, `'deferred'` or `'replied'`), re-read from the interaction on every call, so answers made directly with discord.js or by a collector still count. `message` is the message last sent or edited, `location` is what [`getInstallContext`](#where-the-interaction-happened) reports, and `original` holds the message's components and embeds from before `@Defer` locked it. `lock()` is `@Defer`'s second step, for a handler that acknowledges on its own. Interceptors and filters reach the same state as `context.response`.
|
|
757
764
|
|
|
758
|
-
Each call takes only the flags Discord accepts for it, computed afresh: an ephemeral follow-up never makes later messages ephemeral. `send()` and `followUp()` payloads are typed so an impossible flag does not compile. Once a message uses Components V2 its edits keep the flag, and content and embeds are dropped from them. When an edit re-sends an embed or Components V2 media whose image is one of the message's own Discord attachments, the URL is pointed at `attachment://` so the image survives the edit.
|
|
765
|
+
Each call takes only the flags Discord accepts for it, computed afresh: an ephemeral follow-up never makes later messages ephemeral. `send()` and `followUp()` payloads are typed so an impossible flag does not compile. Make a message private with `flags: MessageFlags.Ephemeral`; discord.js's deprecated `ephemeral: true` is read as that flag, under the same rules. Once a message uses Components V2 its edits keep the flag, and content and embeds are dropped from them. When an edit re-sends an embed or Components V2 media whose image is one of the message's own Discord attachments, the URL is pointed at `attachment://` so the image survives the edit.
|
|
759
766
|
|
|
760
767
|
### `@Defer`
|
|
761
768
|
|
|
@@ -1323,6 +1330,62 @@ export class HelpService {
|
|
|
1323
1330
|
|
|
1324
1331
|
---
|
|
1325
1332
|
|
|
1333
|
+
## Providers
|
|
1334
|
+
|
|
1335
|
+
A class that a controller or service injects by its type is bound for you. For anything else — a database pool, a configured client, settings, or the implementation behind an abstract class — list a provider in `@MeoCord({ providers })`, and inject it with `@Inject(token)`:
|
|
1336
|
+
|
|
1337
|
+
```typescript
|
|
1338
|
+
import { createToken } from 'meocord/common'
|
|
1339
|
+
import { Inject, MeoCord, Service } from 'meocord/decorator'
|
|
1340
|
+
import { GatewayIntentBits } from 'discord.js'
|
|
1341
|
+
import pg from 'pg'
|
|
1342
|
+
|
|
1343
|
+
export const DATABASE = createToken<pg.Pool>('Database')
|
|
1344
|
+
|
|
1345
|
+
@Service()
|
|
1346
|
+
export class NotesStore {
|
|
1347
|
+
constructor(@Inject(DATABASE) private readonly db: pg.Pool) {}
|
|
1348
|
+
|
|
1349
|
+
async list(userId: string) {
|
|
1350
|
+
const { rows } = await this.db.query('SELECT text FROM notes WHERE user_id = $1', [userId])
|
|
1351
|
+
return rows
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
@MeoCord({
|
|
1356
|
+
controllers: [NotesController],
|
|
1357
|
+
providers: [
|
|
1358
|
+
{ provide: 'databaseUrl', useValue: process.env.DATABASE_URL },
|
|
1359
|
+
{
|
|
1360
|
+
provide: DATABASE,
|
|
1361
|
+
useFactory: async (url: string) => {
|
|
1362
|
+
const pool = new pg.Pool({ connectionString: url })
|
|
1363
|
+
await pool.query('SELECT 1') // fail at startup, not at the first command
|
|
1364
|
+
return pool
|
|
1365
|
+
},
|
|
1366
|
+
inject: ['databaseUrl'],
|
|
1367
|
+
},
|
|
1368
|
+
],
|
|
1369
|
+
clientOptions: { intents: [GatewayIntentBits.Guilds] },
|
|
1370
|
+
})
|
|
1371
|
+
export default class App {}
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1374
|
+
| Provider | Provides |
|
|
1375
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------- |
|
|
1376
|
+
| `{ provide, useValue }` | The value, as it is. |
|
|
1377
|
+
| `{ provide, useClass }` | One instance of the class, with its own constructor dependencies injected. |
|
|
1378
|
+
| `{ provide, useFactory, inject? }` | What the function returns, called once with the values of `inject`. It may be `async`. |
|
|
1379
|
+
|
|
1380
|
+
- **Tokens** are a class, a string, a symbol, or a token from `createToken<T>(description)`: a symbol whose description names it in errors, and whose type `TestingModule.get` returns. A parameter typed as a class needs no `@Inject`; `{ provide: Storage, useClass: RedisStorage }` makes every `Storage` parameter a `RedisStorage`.
|
|
1381
|
+
- **When**: every factory runs once, in dependency order, when `app.start()` begins, before the listed services are made and before login, awaiting those that return a promise. Anything that injects the value gets the resolved one.
|
|
1382
|
+
- **Lifecycle**: a provided value that implements `onReady` or `onShutdown` gets them called like a service's, in dependency order, so it closes after the services that use it. `pg.Pool` has no such hook; end it from the `onShutdown` of the service that owns it, or provide a class that wraps it.
|
|
1383
|
+
- **Failures**: a factory that throws or rejects stops the bot before it logs in. The log names the token and the cause, `app.start()` rejects with an error `isExplainedError` recognises, and the exit code is 1. A class or factory that injects a token nothing provides stops `MeoCordFactory.create()`, naming both. A token may be provided once, and the tokens MeoCord binds itself, such as `Client`, cannot be provided.
|
|
1384
|
+
|
|
1385
|
+
In a test, `MeoCordTestingModule` takes the same providers, and `overrideProvider(token)` replaces one under any token. `invoke` and `emit` resolve asynchronous factories first; before calling `get` on something that depends on one, `await module.init()`.
|
|
1386
|
+
|
|
1387
|
+
---
|
|
1388
|
+
|
|
1326
1389
|
## Lifecycle Hooks
|
|
1327
1390
|
|
|
1328
1391
|
A controller or service can do work once the bot is online, and clean up before it stops, by implementing `OnReady` and `OnShutdown` from `meocord/interface`:
|
|
@@ -1354,7 +1417,7 @@ export class ReminderScheduler implements OnReady, OnShutdown {
|
|
|
1354
1417
|
}
|
|
1355
1418
|
```
|
|
1356
1419
|
|
|
1357
|
-
- **Which classes**: every controller and every service the app binds — the ones listed in `@MeoCord({ controllers, services })` and everything they depend on — including a service no handler has used yet. Guards are created per call and get no hooks.
|
|
1420
|
+
- **Which classes**: every controller and every service the app binds — the ones listed in `@MeoCord({ controllers, services })` and everything they depend on — including a service no handler has used yet, and every [provided](#providers) value that implements a hook. Guards are created per call and get no hooks.
|
|
1358
1421
|
- **`onReady`** runs once the client is ready. It receives the client and `{ primary }`, which says whether this process should do one-off work: `true` for a bot running in one process, and with [process sharding](#sharding) only in the process running shard 0.
|
|
1359
1422
|
- **Dependency order.** `onReady` hooks run one at a time, each class after the classes it injects: a `DatabaseService` is ready before the `ReminderScheduler` that injects it. Classes with no dependency between them run in declaration order, the `services` first, then the `controllers`. Command registration runs alongside and never delays the hooks. A hook still running after 10 seconds is named in a warning, and the hooks after it wait for it.
|
|
1360
1423
|
- **`onShutdown`** runs on SIGINT or SIGTERM, before the client is destroyed, in reverse order, so a class stops before the classes it uses. The bot waits for the whole sequence up to `shutdownTimeout` from `meocord.config.ts` (10 seconds by default), then shuts down whether or not it finished. A second signal more than a second after the first exits at once; one sooner is taken as the same request, since a terminal's Ctrl+C can arrive twice. If the bot never became ready, for example because the login failed, no `onShutdown` hook runs. A signal that arrives while the `onReady` hooks are still running shuts down only the classes whose `onReady` finished, and those without one; the class still starting, and those after it, are skipped, and no further `onReady` starts.
|
|
@@ -1470,6 +1533,8 @@ const module = MeoCordTestingModule.create({
|
|
|
1470
1533
|
const controller = module.get(GreetingSlashController)
|
|
1471
1534
|
```
|
|
1472
1535
|
|
|
1536
|
+
`providers` takes the same shapes as `@MeoCord({ providers })`: `useValue`, `useClass` and `useFactory`, under a class, string, symbol or `createToken` token. A factory that returns a promise is resolved by `await module.init()`, which `invoke` and `emit` call for you.
|
|
1537
|
+
|
|
1473
1538
|
### Running a handler with `invoke`
|
|
1474
1539
|
|
|
1475
1540
|
`module.invoke(Controller, 'method', ...args)` runs a handler through the [pipeline](#how-a-handler-runs) dispatch runs: `@Defer`, its guards, class guards first and each once, then its interceptors around validation, pipes, cooldowns and the handler, all inside its exception filters. Guards resolve from the module, so `overrideGuard` stubs and injected `ExecutionContext` work as they do in the bot. Pass the arguments dispatch would: the interaction, message or reaction, then the handler's params. An interaction must be one dispatch could route to the handler: a customId its pattern matches, or the command or subcommand path it handles. One that could not, such as `'something/else'` for `'profile/{id}'`, rejects with a message naming both, so a typo in a test does not pass silently. A mock built without a customId or command name is not checked.
|
|
@@ -1524,6 +1589,8 @@ Creates a smart mock instance of any discord.js class. The full prototype chain
|
|
|
1524
1589
|
|
|
1525
1590
|
**Type guards run real logic** — `isButton()`, `isRepliable()`, `isChatInputCommand()`, etc. are backed by the actual discord.js prototype methods. The right fields (`type`, `componentType`, `commandType`) are set based on the class you pass in, so no manual `.mockReturnValue(true)` setup is needed. All type guard methods are still mock functions and can be overridden per test.
|
|
1526
1591
|
|
|
1592
|
+
**Guild checks read the mock's data** — `inGuild()`, `inCachedGuild()` and `inRawGuild()` answer from the `guildId` and `guild` the mock is given, as discord.js does: a `guildId` means a guild, a `guildId` with a `guild` (such as `createMockGuild()`) a cached one, and a mock created without a `guildId` is a DM, where all three return `false`. A `member` set to `null` or `undefined` makes all three `false` too. Its `locale` is `'en-US'`, and its `guildLocale` is `'en-US'` with a `guildId` and `null` without, as Discord sends them; pass either to change it.
|
|
1593
|
+
|
|
1527
1594
|
**Reply state machine** — for repliable interactions, `replied` and `deferred` start as `false`. Calling `reply()` or `deferReply()` twice throws, just like a real interaction. `followUp()`, `editReply()`, and `deleteReply()` throw if called before any reply. The ephemeral flag is tracked on `interaction.ephemeral`, read from `flags` only — the deprecated `ephemeral: true` reply option is not honoured. All reply methods are still mock functions so call assertions work normally.
|
|
1528
1595
|
|
|
1529
1596
|
Autocomplete interactions are not repliable but get the equivalent for their own single-shot response: `responded` starts as `false`, `respond()` sets it, and a second call throws.
|
|
@@ -1531,6 +1598,8 @@ Autocomplete interactions are not repliable but get the equivalent for their own
|
|
|
1531
1598
|
Guards discord.js has deprecated are deliberately left unwired — `isSelectMenu()` returns `undefined` rather than reproducing behaviour the library is removing. Use `isStringSelectMenu()`.
|
|
1532
1599
|
|
|
1533
1600
|
> **Framework-agnostic** — the mocks returned here are plain mock functions that stamp `_isMockFunction` and expose `.mock.calls`, the exact contract both `jest` and `vitest` check. Use them with either framework's `expect(...).toHaveBeenCalledWith(...)` / `toHaveBeenCalledTimes(...)` — no jest or vitest import is required to produce them. For typed stubs in your own code, import `MockedFunction`, `createMockFn`, and `DeepMocked` from `meocord/testing`.
|
|
1601
|
+
>
|
|
1602
|
+
> **Resetting between tests** — Vitest's `clearMocks` and jest's `clearAllMocks()` reach only their own `vi.fn()` and `jest.fn()`. `clearAllMocks()` from `meocord/testing` clears the calls of every mock it made, and `resetAllMocks()` also undoes what a test set with `mockReturnValue`, `mockResolvedValue` and the rest, back to how each mock was created. Generated projects call `resetAllMocks()` after every test from `vitest.setup.ts`, so set a mock's behaviour in the test, or in `beforeEach`, that relies on it.
|
|
1534
1603
|
|
|
1535
1604
|
```typescript
|
|
1536
1605
|
import { createMockInteraction } from 'meocord/testing'
|
|
@@ -1547,6 +1616,10 @@ interaction.isChatInputCommand() // → true
|
|
|
1547
1616
|
interaction.isRepliable() // → true
|
|
1548
1617
|
interaction.isButton() // → false
|
|
1549
1618
|
|
|
1619
|
+
// created without a guildId, the mock is a DM
|
|
1620
|
+
interaction.inGuild() // → false
|
|
1621
|
+
interaction.inCachedGuild() // → false
|
|
1622
|
+
|
|
1550
1623
|
// reply state machine
|
|
1551
1624
|
interaction.replied // → false
|
|
1552
1625
|
await interaction.reply({ content: 'hi' })
|
|
@@ -1650,6 +1723,8 @@ All methods are mock functions — override any per test with `.mockReturnValue(
|
|
|
1650
1723
|
|
|
1651
1724
|
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.
|
|
1652
1725
|
|
|
1726
|
+
A method that returns a promise in discord.js resolves, so `await` and `.catch()` work without setup: `send()` and `reply()` to a mock `Message`, a manager's `fetch(id)`, `create()` and `edit()` to a mock of its item (`users.fetch(id)` to a `User`, `guild.members.fetch(id)` to a `GuildMember`), a list fetch such as `members.fetch()` to an empty `Collection`, `createDM()` to a `DMChannel`, and a structure's own `edit()`, `fetch()` and setters to the structure itself. Any other such method resolves to `undefined`. `mockResolvedValue` and `mockRejectedValue` still decide per test.
|
|
1727
|
+
|
|
1653
1728
|
```typescript
|
|
1654
1729
|
import { createMockFn, createMockUser, createMockClient, createMockGuild, createMockChannel } from 'meocord/testing'
|
|
1655
1730
|
import { TextChannel } from 'discord.js'
|
|
@@ -1665,6 +1740,17 @@ await (client.users as any).fetch('user-123')
|
|
|
1665
1740
|
expect((client.users as any).fetch).toHaveBeenCalledWith('user-123')
|
|
1666
1741
|
```
|
|
1667
1742
|
|
|
1743
|
+
`createMockChannel` sets up the managers each channel class has: `messages`; `threads` on text, announcement, forum and media channels; and `members` on a `ThreadChannel`. A subclass gets those of the class it extends.
|
|
1744
|
+
|
|
1745
|
+
```typescript
|
|
1746
|
+
import { TextChannel, ThreadChannel } from 'discord.js'
|
|
1747
|
+
|
|
1748
|
+
const text = createMockChannel(TextChannel)
|
|
1749
|
+
const thread = createMockChannel(ThreadChannel)
|
|
1750
|
+
// discord.js types a created thread as public or private, not as the ThreadChannel class
|
|
1751
|
+
text.threads.create.mockResolvedValue(thread as never)
|
|
1752
|
+
```
|
|
1753
|
+
|
|
1668
1754
|
</details>
|
|
1669
1755
|
|
|
1670
1756
|
<details>
|
|
@@ -1691,6 +1777,23 @@ edited.delete // → a mock fn
|
|
|
1691
1777
|
expect(msg.delete).toHaveBeenCalledTimes(1)
|
|
1692
1778
|
```
|
|
1693
1779
|
|
|
1780
|
+
Without arguments the message is empty. Give it an `id`, `content`, `components`, `embeds` and `flags` to test code that reads them, such as a button on a message whose controls `@Defer` locks. Components and embeds may be API JSON, builders or discord.js instances, and `flags` a number, flag names or a `MessageFlagsBitField`:
|
|
1781
|
+
|
|
1782
|
+
```typescript
|
|
1783
|
+
const message = createMockMessage({
|
|
1784
|
+
components: [
|
|
1785
|
+
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
|
1786
|
+
new ButtonBuilder().setCustomId('card/refresh').setLabel('Refresh').setStyle(ButtonStyle.Primary),
|
|
1787
|
+
),
|
|
1788
|
+
],
|
|
1789
|
+
embeds: [{ title: 'Card' }],
|
|
1790
|
+
flags: MessageFlags.Ephemeral,
|
|
1791
|
+
})
|
|
1792
|
+
const interaction = createMockInteraction(ButtonInteraction, { customId: 'card/refresh', message })
|
|
1793
|
+
```
|
|
1794
|
+
|
|
1795
|
+
API JSON and builders are kept as their JSON behind `toJSON()`, which is what `respond()` and `@Defer` read; discord.js instances are kept as they are.
|
|
1796
|
+
|
|
1694
1797
|
</details>
|
|
1695
1798
|
|
|
1696
1799
|
<details>
|
|
@@ -33,7 +33,7 @@ let configLoaded = false;
|
|
|
33
33
|
try {
|
|
34
34
|
// Called through a variable so a bundler does not try to resolve the path while building the
|
|
35
35
|
// application; the file is only there once the build is done.
|
|
36
|
-
const load = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/cooldown-store-
|
|
36
|
+
const load = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/cooldown-store-DJ7s2HYv.cjs', document.baseURI).href)));
|
|
37
37
|
const loaded = load(compiledPath);
|
|
38
38
|
return loaded.default ?? loaded;
|
|
39
39
|
} catch (error) {
|
|
@@ -485,11 +485,14 @@ function errorCode(error) {
|
|
|
485
485
|
return interaction.isCommand() || interaction.isModalSubmit() && !interaction.isFromMessage();
|
|
486
486
|
}
|
|
487
487
|
function toBody(payload) {
|
|
488
|
-
|
|
488
|
+
if (typeof payload === 'string') return {
|
|
489
489
|
content: payload
|
|
490
|
-
} : {
|
|
491
|
-
...payload
|
|
492
490
|
};
|
|
491
|
+
// discord.js's deprecated option, read as the flag it stands for before anything decides on flags, and
|
|
492
|
+
// kept out of the call, where discord.js would add Ephemeral back to an edit or an update
|
|
493
|
+
const { ephemeral, ...body } = payload;
|
|
494
|
+
if (ephemeral) body.flags = Number(discord_js.MessageFlagsBitField.resolve(body.flags ?? 0)) | discord_js.MessageFlags.Ephemeral;
|
|
495
|
+
return body;
|
|
493
496
|
}
|
|
494
497
|
/** Drops what a Components V2 message cannot carry. */ function forMode(body, v2) {
|
|
495
498
|
if (!v2) return body;
|
|
@@ -4,7 +4,7 @@ require('reflect-metadata');
|
|
|
4
4
|
var enum_index = require('../enum/index.cjs');
|
|
5
5
|
var metadataKey_enum = require('./metadata-key.enum-CXFdtEX9.cjs');
|
|
6
6
|
var discord_js = require('discord.js');
|
|
7
|
-
var cooldownStore = require('./cooldown-store-
|
|
7
|
+
var cooldownStore = require('./cooldown-store-DJ7s2HYv.cjs');
|
|
8
8
|
var inversify = require('inversify');
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -588,6 +588,18 @@ const PLACEHOLDER_PATTERN = /\{(\w+)}/g;
|
|
|
588
588
|
}
|
|
589
589
|
return tokens;
|
|
590
590
|
}
|
|
591
|
+
/**
|
|
592
|
+
* The position of the first constructor parameter with no runtime type and no `@Inject` token, or -1.
|
|
593
|
+
* Such a parameter reads as `Object` when it is typed with an interface or an `import type`, and as
|
|
594
|
+
* `Object` or `undefined` when its class's module had not finished loading, as when two classes
|
|
595
|
+
* import each other.
|
|
596
|
+
*/ function untypedParameter(cls) {
|
|
597
|
+
const types = Reflect.getMetadata(metadataKey_enum.MetadataKey.ParamTypes, cls) ?? [];
|
|
598
|
+
const metadata = Reflect.getMetadata(INVERSIFY_CLASS_METADATA, cls);
|
|
599
|
+
// Once bound, inversify records each parameter here too: an @Inject token, or the parameter's type
|
|
600
|
+
const tokens = types.map((type, index)=>metadata?.constructorArguments?.[index]?.value ?? type);
|
|
601
|
+
return tokens.findIndex((token)=>token === undefined || token === Object);
|
|
602
|
+
}
|
|
591
603
|
/**
|
|
592
604
|
* The error for a class resolved once and shared that asks for the per-call `ExecutionContext`,
|
|
593
605
|
* which would keep the first call's context for every later one.
|
|
@@ -834,21 +846,12 @@ function eventDecorator(event, once) {
|
|
|
834
846
|
*/ function lifecycleDependencies(container, cls) {
|
|
835
847
|
return injectedTokens(cls).filter((token)=>isAppClassToken(token) && container.isBound(token));
|
|
836
848
|
}
|
|
837
|
-
/**
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
const seen = new Set();
|
|
844
|
-
const visit = (cls)=>{
|
|
845
|
-
if (seen.has(cls)) return;
|
|
846
|
-
seen.add(cls);
|
|
847
|
-
lifecycleDependencies(container, cls).forEach(visit);
|
|
848
|
-
ordered.push(cls);
|
|
849
|
-
};
|
|
850
|
-
roots.forEach(visit);
|
|
851
|
-
return ordered;
|
|
849
|
+
/** The units for a list of classes already in dependency order, as the app runs them without providers. */ function classUnits(container, classes) {
|
|
850
|
+
return classes.map((cls)=>({
|
|
851
|
+
token: cls,
|
|
852
|
+
name: cls.name,
|
|
853
|
+
dependencies: lifecycleDependencies(container, cls)
|
|
854
|
+
}));
|
|
852
855
|
}
|
|
853
856
|
|
|
854
857
|
function isInterceptorWithParams(entry) {
|
|
@@ -1124,6 +1127,170 @@ const asList = (entries)=>Array.isArray(entries) ? entries : [
|
|
|
1124
1127
|
];
|
|
1125
1128
|
}
|
|
1126
1129
|
|
|
1130
|
+
const isValueProvider = (provider)=>'useValue' in provider;
|
|
1131
|
+
const isClassProvider = (provider)=>'useClass' in provider;
|
|
1132
|
+
const isFactoryProvider = (provider)=>'useFactory' in provider;
|
|
1133
|
+
/** A token as logs and errors name it: a class by its name, a symbol by its description, a string quoted. */ function tokenName(token) {
|
|
1134
|
+
if (typeof token === 'function') return token.name || 'an anonymous class';
|
|
1135
|
+
if (typeof token === 'symbol') return token.description ? `Symbol(${token.description})` : 'a symbol';
|
|
1136
|
+
return `'${String(token)}'`;
|
|
1137
|
+
}
|
|
1138
|
+
const isToken = (value)=>typeof value === 'function' || typeof value === 'string' || typeof value === 'symbol';
|
|
1139
|
+
/**
|
|
1140
|
+
* Checks each provider's shape and indexes them by token, throwing on the first that cannot be bound:
|
|
1141
|
+
* no token, not exactly one of `useValue`, `useClass` and `useFactory`, or a token provided twice.
|
|
1142
|
+
*/ function providerMap(providers, where) {
|
|
1143
|
+
const map = new Map();
|
|
1144
|
+
for (const provider of providers){
|
|
1145
|
+
const entry = provider;
|
|
1146
|
+
if (!entry || typeof entry !== 'object' || !isToken(entry.provide)) {
|
|
1147
|
+
throw new Error(`${where} has a provider without a token: set provide to a class, a string or a symbol.`);
|
|
1148
|
+
}
|
|
1149
|
+
const name = tokenName(entry.provide);
|
|
1150
|
+
const kinds = [
|
|
1151
|
+
'useValue',
|
|
1152
|
+
'useClass',
|
|
1153
|
+
'useFactory'
|
|
1154
|
+
].filter((kind)=>kind in entry);
|
|
1155
|
+
if (kinds.length !== 1) {
|
|
1156
|
+
throw new Error(`The provider for ${name} in ${where} needs exactly one of useValue, useClass and useFactory.`);
|
|
1157
|
+
}
|
|
1158
|
+
if ('useClass' in entry && typeof entry.useClass !== 'function') {
|
|
1159
|
+
throw new Error(`The provider for ${name} in ${where} has a useClass that is not a class.`);
|
|
1160
|
+
}
|
|
1161
|
+
if ('useFactory' in entry) {
|
|
1162
|
+
if (typeof entry.useFactory !== 'function') {
|
|
1163
|
+
throw new Error(`The provider for ${name} in ${where} has a useFactory that is not a function.`);
|
|
1164
|
+
}
|
|
1165
|
+
if (entry.inject !== undefined && !(Array.isArray(entry.inject) && entry.inject.every(isToken))) {
|
|
1166
|
+
throw new Error(`The provider for ${name} in ${where} has an inject that is not a list of tokens.`);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (map.has(entry.provide)) throw new Error(`${name} is provided twice in ${where}.`);
|
|
1170
|
+
map.set(entry.provide, provider);
|
|
1171
|
+
}
|
|
1172
|
+
return map;
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Binds one provider into `container` as a singleton. `bindClass` binds a class a provider depends
|
|
1176
|
+
* on, as the app binds its own, so a provided class or a factory's `inject` can name any class.
|
|
1177
|
+
*/ function bindProvider(container, provider, bindClass) {
|
|
1178
|
+
const token = provider.provide;
|
|
1179
|
+
if (isValueProvider(provider)) {
|
|
1180
|
+
container.bind(token).toConstantValue(provider.useValue);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
if (isClassProvider(provider)) {
|
|
1184
|
+
const cls = provider.useClass;
|
|
1185
|
+
if (injectedTokens(cls).includes(cooldownStore.ExecutionContext)) throw singletonContextError(cls);
|
|
1186
|
+
makeInjectable(cls);
|
|
1187
|
+
container.bind(token).to(cls).inSingletonScope();
|
|
1188
|
+
for (const dependency of injectedTokens(cls))if (isAppClassToken(dependency)) bindClass(dependency);
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
const { useFactory, inject = [] } = provider;
|
|
1192
|
+
// Resolved in dependency order before anything needs it, so each injected value is already made
|
|
1193
|
+
container.bind(token).toDynamicValue((context)=>useFactory(...inject.map((dependency)=>context.get(dependency)))).inSingletonScope();
|
|
1194
|
+
for (const dependency of inject)if (isAppClassToken(dependency)) bindClass(dependency);
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Throws when a class or a factory asks for a string or symbol token that nothing binds, naming both,
|
|
1198
|
+
* rather than failing when the class is first resolved. A class token is bound on demand, so it is
|
|
1199
|
+
* never missing.
|
|
1200
|
+
*/ function assertProvided(container, providers, classes, where) {
|
|
1201
|
+
const missing = (token)=>(typeof token === 'string' || typeof token === 'symbol') && !container.isBound(token);
|
|
1202
|
+
for (const cls of classes){
|
|
1203
|
+
const token = injectedTokens(cls).find(missing);
|
|
1204
|
+
if (token !== undefined) {
|
|
1205
|
+
throw new Error(`${cls.name} injects ${tokenName(token)}, which nothing provides: add a provider for it to ${where}.`);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
for (const [provided, provider] of providers){
|
|
1209
|
+
const dependencies = isFactoryProvider(provider) ? provider.inject ?? [] : isClassProvider(provider) ? injectedTokens(provider.useClass) : [];
|
|
1210
|
+
const token = dependencies.find(missing);
|
|
1211
|
+
if (token !== undefined) {
|
|
1212
|
+
throw new Error(`The provider for ${tokenName(provided)} injects ${tokenName(token)}, which nothing provides: add a provider for it to ${where}.`);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* The classes the app constructs, found from `roots` through what each injects, before anything is
|
|
1218
|
+
* bound: a token a provider stands in for is not constructed as itself, so it is only followed when
|
|
1219
|
+
* it is its own `useClass`. Providers' classes and factory dependencies are roots too.
|
|
1220
|
+
*/ function reachableClasses(roots, providers) {
|
|
1221
|
+
const found = [];
|
|
1222
|
+
const visit = (token)=>{
|
|
1223
|
+
if (!isAppClassToken(token) || found.includes(token)) return;
|
|
1224
|
+
const provider = providers.get(token);
|
|
1225
|
+
if (provider && !(isClassProvider(provider) && provider.useClass === token)) return;
|
|
1226
|
+
found.push(token);
|
|
1227
|
+
injectedTokens(token).forEach(visit);
|
|
1228
|
+
};
|
|
1229
|
+
for (const provider of providers.values()){
|
|
1230
|
+
if (isClassProvider(provider)) visit(provider.useClass);
|
|
1231
|
+
if (isFactoryProvider(provider)) (provider.inject ?? []).forEach(visit);
|
|
1232
|
+
}
|
|
1233
|
+
roots.forEach(visit);
|
|
1234
|
+
return found;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Throws for the first of `classes` with a constructor parameter nothing can inject: one with no
|
|
1238
|
+
* runtime type and no `@Inject` token. It names the classes among `classes` that inject it, the likely
|
|
1239
|
+
* other half when two classes import each other, rather than leaving inversify's error to point at
|
|
1240
|
+
* the compiler options.
|
|
1241
|
+
*/ function assertTypedParameters(classes) {
|
|
1242
|
+
for (const cls of classes){
|
|
1243
|
+
const index = untypedParameter(cls);
|
|
1244
|
+
if (index === -1) continue;
|
|
1245
|
+
const injectors = classes.filter((other)=>other !== cls && injectedTokens(other).includes(cls)).map((other)=>other.name);
|
|
1246
|
+
const injectedBy = injectors.length === 0 ? '' : ` (${injectors.length === 1 ? injectors[0] : `${injectors.slice(0, -1).join(', ')} and ${injectors.at(-1)}`} ` + `inject${injectors.length === 1 ? 's' : ''} ${cls.name})`;
|
|
1247
|
+
throw new Error(`${cls.name} cannot be created: parameter ${index + 1} of its constructor has no runtime type. Usually ` + `${cls.name} and a class it injects import each other${injectedBy}, or the parameter is typed with an ` + 'interface or an `import type`. Move what they both need into a third service, or inject the parameter ' + 'with @Inject(token).');
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* What must exist before `token`: a factory's `inject`, a provided or bound class's constructor
|
|
1252
|
+
* dependencies, nothing for a value. Only the app's classes and provided tokens count.
|
|
1253
|
+
*/ function tokenDependencies(container, providers, token) {
|
|
1254
|
+
const provider = providers.get(token);
|
|
1255
|
+
const dependencies = provider ? isFactoryProvider(provider) ? provider.inject ?? [] : isClassProvider(provider) ? injectedTokens(provider.useClass) : [] : isAppClassToken(token) ? injectedTokens(token) : [];
|
|
1256
|
+
return dependencies.filter((dependency)=>(providers.has(dependency) || isAppClassToken(dependency)) && container.isBound(dependency));
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* The bound tokens reachable from `roots`, each after everything it depends on: the order factories
|
|
1260
|
+
* are resolved and lifecycle hooks run in. Tokens with no dependency between them keep the roots' order.
|
|
1261
|
+
*/ function resolutionOrder(container, providers, roots) {
|
|
1262
|
+
const ordered = [];
|
|
1263
|
+
const seen = new Set();
|
|
1264
|
+
const visit = (token)=>{
|
|
1265
|
+
if (seen.has(token)) return;
|
|
1266
|
+
seen.add(token);
|
|
1267
|
+
tokenDependencies(container, providers, token).forEach(visit);
|
|
1268
|
+
ordered.push(token);
|
|
1269
|
+
};
|
|
1270
|
+
roots.forEach(visit);
|
|
1271
|
+
return ordered;
|
|
1272
|
+
}
|
|
1273
|
+
/** The error for a factory that threw or rejected, naming its token and carrying the original as `cause`. */ function providerFailure(token, error) {
|
|
1274
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1275
|
+
return new Error(`The factory providing ${tokenName(token)} failed: ${reason}`, {
|
|
1276
|
+
cause: error
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Runs every factory in `order`, awaiting those that return a promise, so each value is made before
|
|
1281
|
+
* anything that injects it is resolved. Rejects with {@link providerFailure} for the first that fails.
|
|
1282
|
+
*/ async function resolveProviders(container, providers, order) {
|
|
1283
|
+
for (const token of order){
|
|
1284
|
+
const provider = providers.get(token);
|
|
1285
|
+
if (!provider || !isFactoryProvider(provider)) continue;
|
|
1286
|
+
try {
|
|
1287
|
+
await container.getAsync(token);
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
throw providerFailure(token, error);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1127
1294
|
exports.Autocomplete = Autocomplete;
|
|
1128
1295
|
exports.CATCH_TYPES = CATCH_TYPES;
|
|
1129
1296
|
exports.CLASS_COOLDOWNS = CLASS_COOLDOWNS;
|
|
@@ -1147,15 +1314,18 @@ exports.Once = Once;
|
|
|
1147
1314
|
exports.PARAM_SEPARATOR = PARAM_SEPARATOR;
|
|
1148
1315
|
exports.ReactionHandler = ReactionHandler;
|
|
1149
1316
|
exports.appliesTo = appliesTo;
|
|
1317
|
+
exports.assertProvided = assertProvided;
|
|
1150
1318
|
exports.assertStageEntries = assertStageEntries;
|
|
1319
|
+
exports.assertTypedParameters = assertTypedParameters;
|
|
1320
|
+
exports.bindProvider = bindProvider;
|
|
1151
1321
|
exports.bindShared = bindShared;
|
|
1152
1322
|
exports.callFilter = callFilter;
|
|
1153
1323
|
exports.callGuardedHandler = callGuardedHandler;
|
|
1324
|
+
exports.classUnits = classUnits;
|
|
1154
1325
|
exports.consumeDispatchMark = consumeDispatchMark;
|
|
1155
1326
|
exports.declaringPrototype = declaringPrototype;
|
|
1156
1327
|
exports.deferMisuseError = deferMisuseError;
|
|
1157
1328
|
exports.defineStageTypes = defineStageTypes;
|
|
1158
|
-
exports.dependencyOrder = dependencyOrder;
|
|
1159
1329
|
exports.describeInteraction = describeInteraction;
|
|
1160
1330
|
exports.findAmbiguousRoutes = findAmbiguousRoutes;
|
|
1161
1331
|
exports.focusedOptionName = focusedOptionName;
|
|
@@ -1173,8 +1343,8 @@ exports.handlerInterceptors = handlerInterceptors;
|
|
|
1173
1343
|
exports.hasCustomId = hasCustomId;
|
|
1174
1344
|
exports.injectedTokens = injectedTokens;
|
|
1175
1345
|
exports.isAppClassToken = isAppClassToken;
|
|
1346
|
+
exports.isClassProvider = isClassProvider;
|
|
1176
1347
|
exports.isCustomIdRouted = isCustomIdRouted;
|
|
1177
|
-
exports.lifecycleDependencies = lifecycleDependencies;
|
|
1178
1348
|
exports.makeInjectable = makeInjectable;
|
|
1179
1349
|
exports.matchFilter = matchFilter;
|
|
1180
1350
|
exports.matchesCommandType = matchesCommandType;
|
|
@@ -1184,11 +1354,17 @@ exports.prepareFilter = prepareFilter;
|
|
|
1184
1354
|
exports.prepareHandlerArgs = prepareHandlerArgs;
|
|
1185
1355
|
exports.prepareInterceptor = prepareInterceptor;
|
|
1186
1356
|
exports.preparePipe = preparePipe;
|
|
1357
|
+
exports.providerMap = providerMap;
|
|
1358
|
+
exports.reachableClasses = reachableClasses;
|
|
1359
|
+
exports.resolutionOrder = resolutionOrder;
|
|
1187
1360
|
exports.resolveCommandPaths = resolveCommandPaths;
|
|
1188
1361
|
exports.resolveOptionParams = resolveOptionParams;
|
|
1362
|
+
exports.resolveProviders = resolveProviders;
|
|
1189
1363
|
exports.runGuards = runGuards;
|
|
1190
1364
|
exports.runInterceptors = runInterceptors;
|
|
1191
1365
|
exports.singletonContextError = singletonContextError;
|
|
1192
1366
|
exports.stageClass = stageClass;
|
|
1193
1367
|
exports.stageTypes = stageTypes;
|
|
1194
1368
|
exports.startDefer = startDefer;
|
|
1369
|
+
exports.tokenDependencies = tokenDependencies;
|
|
1370
|
+
exports.tokenName = tokenName;
|