meocord 4.2.2 → 4.2.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 +130 -0
- package/dist/cjs/_shared/{controller.decorator-U2NBWcoU.cjs → controller.decorator-hp5xdJU4.cjs} +512 -183
- package/dist/cjs/_shared/{startup-roots-lA5xkBFq.cjs → event-sources-DzqTODXg.cjs} +265 -80
- package/dist/cjs/_shared/{providers-B4MOFBrW.cjs → providers-BBvbnmLP.cjs} +780 -281
- package/dist/cjs/_shared/{sharded-cooldown-store-HTUb3Ii_.cjs → sharded-cooldown-store-B62g5O88.cjs} +11 -9
- package/dist/cjs/_shared/{translate-error-CbBHUu5P.cjs → translate-error-wsgfsUET.cjs} +1 -1
- package/dist/cjs/common/index.cjs +4 -104
- package/dist/cjs/core/index.cjs +432 -181
- package/dist/cjs/decorator/index.cjs +85 -120
- package/dist/cjs/testing/index.cjs +1664 -784
- package/dist/cli.json +1 -1
- package/dist/esm/bin/app-template/package.json.template +9 -9
- package/dist/esm/bin/app-template/src/main.ts.template +3 -2
- package/dist/esm/bin/generator.js +6 -4
- package/dist/esm/bin/helper/filter-generator.helper.js +6 -4
- package/dist/esm/bin/helper/guard-generator.helper.js +6 -4
- package/dist/esm/bin/helper/interceptor-generator.helper.js +6 -4
- package/dist/esm/bin/helper/observer-generator.helper.js +6 -4
- package/dist/esm/bin/helper/pipe-generator.helper.js +6 -4
- package/dist/esm/bin/helper/service-generator.helper.js +6 -4
- package/dist/esm/bin/meocord.js +106 -48
- package/dist/esm/build/native-addons.js +75 -19
- package/dist/esm/build/rsbuild-config.js +11 -3
- package/dist/esm/build/typed-externals.js +15 -2
- package/dist/esm/common/cooldown-store.js +38 -14
- package/dist/esm/common/deprecation.js +4 -1
- package/dist/esm/common/execution-context.js +3 -1
- package/dist/esm/common/explained-error.js +7 -3
- package/dist/esm/common/log-level.js +13 -9
- package/dist/esm/common/logger.js +4 -2
- package/dist/esm/common/meocord-text.js +11 -6
- package/dist/esm/common/redis-cooldown-store.js +3 -7
- package/dist/esm/common/response/flags.js +17 -1
- package/dist/esm/common/response/install-context.js +27 -16
- package/dist/esm/common/response/presenter.js +7 -3
- package/dist/esm/common/response/response-state.js +27 -5
- package/dist/esm/common/sharded-cooldown-store.js +11 -9
- package/dist/esm/common/theme.js +3 -1
- package/dist/esm/common/translator.js +17 -8
- package/dist/esm/core/autocomplete-routes.js +26 -0
- package/dist/esm/core/command-conflicts.js +15 -7
- package/dist/esm/core/command-registration.js +15 -1
- package/dist/esm/core/cooldown-runner.js +7 -9
- package/dist/esm/core/defer.js +2 -5
- package/dist/esm/core/dispatcher.js +83 -58
- package/dist/esm/core/event-requirements.js +67 -19
- package/dist/esm/core/event-sources.js +100 -0
- package/dist/esm/core/fallback.js +3 -1
- package/dist/esm/core/filter-runner.js +4 -4
- package/dist/esm/core/guard-runner.js +173 -52
- package/dist/esm/core/handler-input.js +24 -3
- package/dist/esm/core/handler-pipeline.js +44 -28
- package/dist/esm/core/handler-registry.js +51 -21
- package/dist/esm/core/input-runner.js +7 -8
- package/dist/esm/core/interceptor-runner.js +4 -5
- package/dist/esm/core/lifecycle-hooks.js +3 -8
- package/dist/esm/core/lifecycle-order.js +1 -15
- package/dist/esm/core/meocord-classes.js +14 -1
- package/dist/esm/core/meocord-factory.js +80 -53
- package/dist/esm/core/meocord.app.js +174 -89
- package/dist/esm/core/message-help.js +5 -9
- package/dist/esm/core/message-params.js +126 -46
- package/dist/esm/core/message-routes.js +225 -45
- package/dist/esm/core/message-words.js +30 -2
- package/dist/esm/core/observer-runner.js +2 -1
- package/dist/esm/core/process-state.js +19 -0
- package/dist/esm/core/providers.js +28 -3
- package/dist/esm/core/scalar-types.js +57 -16
- package/dist/esm/core/shard-context.js +11 -10
- package/dist/esm/core/shard-exit.js +3 -3
- package/dist/esm/core/shard-manager.js +16 -6
- package/dist/esm/core/startup-roots.js +28 -8
- package/dist/esm/core/theme-resolvers.js +26 -11
- package/dist/esm/core/theme-runtime.js +23 -13
- package/dist/esm/core/theme-scope.js +27 -29
- package/dist/esm/core/theme-validation.js +1 -1
- package/dist/esm/decorator/app.decorator.js +6 -1
- package/dist/esm/decorator/command-builder.decorator.js +14 -0
- package/dist/esm/decorator/controller-class.decorator.js +1 -1
- package/dist/esm/decorator/controller.decorator.js +48 -19
- package/dist/esm/decorator/defer.decorator.js +2 -2
- package/dist/esm/decorator/event.decorator.js +4 -5
- package/dist/esm/decorator/filter.decorator.js +2 -2
- package/dist/esm/decorator/guard.decorator.js +4 -61
- package/dist/esm/decorator/inject.decorator.js +4 -1
- package/dist/esm/decorator/interceptor.decorator.js +2 -2
- package/dist/esm/decorator/theme.decorator.js +4 -3
- package/dist/esm/decorator/validation.decorator.js +5 -5
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/catalog.js +29 -13
- package/dist/esm/testing/cooldown-store-suite.js +3 -3
- package/dist/esm/testing/discord-defaults.js +449 -355
- package/dist/esm/testing/inspect-handler.js +91 -10
- package/dist/esm/testing/meocord-testing-module.js +195 -77
- package/dist/esm/testing/mock-fn.js +8 -2
- package/dist/esm/testing/mock-interaction.js +639 -113
- package/dist/esm/testing/response-log.js +8 -0
- package/dist/esm/testing/response.js +7 -5
- package/dist/esm/testing/routing.js +48 -6
- package/dist/esm/testing/strict-mocks.js +1 -0
- package/dist/esm/testing/theme.js +3 -2
- package/dist/esm/util/common.util.js +30 -9
- package/dist/esm/util/generator-cli.util.js +6 -4
- package/dist/esm/util/inherited-env.util.js +72 -13
- package/dist/esm/util/json.util.js +10 -3
- package/dist/esm/util/meocord-cli.util.js +3 -2
- package/dist/esm/util/meocord-config-loader.util.js +14 -1
- package/dist/esm/util/meocord-config-validation.util.js +13 -3
- package/dist/esm/util/meocord-source-config.util.js +13 -3
- package/dist/esm/util/platform.util.js +3 -2
- package/dist/esm/util/refusal.util.js +29 -11
- package/dist/esm/util/registration-mode.util.js +14 -1
- package/dist/esm/util/timer-limit.util.js +4 -1
- package/dist/esm/util/tsconfig-paths.util.js +38 -7
- package/dist/esm/util/tsconfig.util.js +16 -58
- package/dist/esm/util/user-text.util.js +8 -4
- package/dist/esm/util/value.util.js +15 -4
- package/dist/types/common/index.d.cts +13 -9
- package/dist/types/common/index.d.ts +13 -9
- package/dist/types/core/index.d.cts +21 -11
- package/dist/types/core/index.d.ts +21 -11
- package/dist/types/decorator/index.d.cts +63 -63
- package/dist/types/decorator/index.d.ts +63 -63
- package/dist/types/{index-DKJclIi-.d.ts → index-Brkd6QGL.d.ts} +81 -35
- package/dist/types/{index-Dlf0ZZtS.d.cts → index-CqXImwF_.d.cts} +81 -35
- package/dist/types/interface/index.d.cts +1 -1
- package/dist/types/interface/index.d.ts +1 -1
- package/dist/types/{route-Bh1U13yy.d.ts → route-IQrK-MN2.d.cts} +11 -3
- package/dist/types/{route-Bh1U13yy.d.cts → route-bI-okq7g.d.ts} +11 -3
- package/dist/types/testing/index.d.cts +137 -54
- package/dist/types/testing/index.d.ts +137 -54
- package/dist/types/{theme-resolvers-CyEz9pnv.d.cts → theme-resolvers-AtgeaYqF.d.cts} +1 -1
- package/dist/types/{theme-resolvers-DA204O-q.d.ts → theme-resolvers-BgqysV8d.d.ts} +1 -1
- package/dist/types/{translator-Bg7hwxDF.d.cts → translator-D8aydlCE.d.ts} +12 -11
- package/dist/types/{translator-CeP4bCzp.d.ts → translator-iJhDlgwL.d.cts} +12 -11
- package/package.json +5 -2
|
@@ -49,9 +49,9 @@ import { RunningCalls } from './running-calls.js';
|
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Resolves each unit and runs its `onReady` one at a time, in the units' dependency order. A failure is
|
|
52
|
-
* reported and the next unit still runs.
|
|
53
|
-
* to
|
|
54
|
-
*/ async function runReadyHooks(container, units, client, info,
|
|
52
|
+
* reported and the next unit still runs. `settled` hears of each unit as its `onReady` settles, or once it is found
|
|
53
|
+
* to have none, which is what a shutdown that starts meanwhile reads.
|
|
54
|
+
*/ async function runReadyHooks(container, units, client, info, report, { stopped = ()=>false, slowAfterMs, settled, runOnReady } = {}) {
|
|
55
55
|
const failed = new Set();
|
|
56
56
|
// For each unit, the failed units it depends on, directly or through another dependency
|
|
57
57
|
const failedUpstream = new Map();
|
|
@@ -88,11 +88,7 @@ import { RunningCalls } from './running-calls.js';
|
|
|
88
88
|
continue;
|
|
89
89
|
}
|
|
90
90
|
firstToken.set(instance, unit.token);
|
|
91
|
-
const entry = lifecycleEntry(unit, instance);
|
|
92
|
-
// Only a unit whose onReady has settled, or that has none, is shut down: a stop mid-ready waits for the one still
|
|
93
|
-
// starting, within its time, and skips those not reached yet
|
|
94
91
|
if (typeof instance.onReady !== 'function') {
|
|
95
|
-
entries.push(entry);
|
|
96
92
|
settled?.(unit);
|
|
97
93
|
continue;
|
|
98
94
|
}
|
|
@@ -109,7 +105,6 @@ import { RunningCalls } from './running-calls.js';
|
|
|
109
105
|
} finally{
|
|
110
106
|
clearTimeout(slow);
|
|
111
107
|
}
|
|
112
|
-
entries.push(entry);
|
|
113
108
|
settled?.(unit);
|
|
114
109
|
}
|
|
115
110
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Client } from 'discord.js';
|
|
2
|
-
import { injectedTokens } from './guard-runner.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* The whole source the runtime gives a native function, such as `function NumberFormat() { [native code] }`. A class's
|
|
@@ -11,18 +10,5 @@ import { injectedTokens } from './guard-runner.js';
|
|
|
11
10
|
*/ function isAppClassToken(token) {
|
|
12
11
|
return typeof token === 'function' && token !== Client && !NATIVE_SOURCE.test(Function.prototype.toString.call(token));
|
|
13
12
|
}
|
|
14
|
-
/**
|
|
15
|
-
* The bound classes `cls` injects, by constructor type or `@inject` token: the classes whose hooks
|
|
16
|
-
* run before its own.
|
|
17
|
-
*/ function lifecycleDependencies(container, cls) {
|
|
18
|
-
return injectedTokens(cls).filter((token)=>isAppClassToken(token) && container.isBound(token));
|
|
19
|
-
}
|
|
20
|
-
/** The units for a list of classes already in dependency order, as the app runs them without providers. */ function classUnits(container, classes) {
|
|
21
|
-
return classes.map((cls)=>({
|
|
22
|
-
token: cls,
|
|
23
|
-
name: cls.name,
|
|
24
|
-
dependencies: lifecycleDependencies(container, cls)
|
|
25
|
-
}));
|
|
26
|
-
}
|
|
27
13
|
|
|
28
|
-
export {
|
|
14
|
+
export { isAppClassToken };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Client } from 'discord.js';
|
|
1
2
|
import { CooldownStore } from '../common/cooldown-store.js';
|
|
2
3
|
import { CooldownError, CooldownStoreError, GuardDeniedError, MessageUsageError, UserError, ValidationError } from '../common/errors.js';
|
|
3
4
|
import { ExecutionContext } from '../common/execution-context.js';
|
|
@@ -16,6 +17,18 @@ import { ThemeCache } from './theme-resolvers.js';
|
|
|
16
17
|
ThemeCache,
|
|
17
18
|
Translator
|
|
18
19
|
];
|
|
20
|
+
/**
|
|
21
|
+
* The tokens the bot binds before an app's providers, which `@MeoCord({ providers })` cannot provide: `Translator` too
|
|
22
|
+
* when the app gives `i18n`.
|
|
23
|
+
*/ const boundBeforeProviders = (i18n)=>[
|
|
24
|
+
Client,
|
|
25
|
+
HandlerRegistry,
|
|
26
|
+
ShardContext,
|
|
27
|
+
CooldownStore,
|
|
28
|
+
...i18n ? [
|
|
29
|
+
Translator
|
|
30
|
+
] : []
|
|
31
|
+
];
|
|
19
32
|
const created = (cls)=>`create it with \`new ${cls.name}(…)\``;
|
|
20
33
|
/** What to do in place of injecting each of MeoCord's classes an app makes itself; the testing module adds its own. */ const advice = new Map([
|
|
21
34
|
[
|
|
@@ -45,4 +58,4 @@ const created = (cls)=>`create it with \`new ${cls.name}(…)\``;
|
|
|
45
58
|
advice.set(cls, instead);
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
export { adviseInPlaceOfInjecting, meocordClassAdvice, meocordClasses };
|
|
61
|
+
export { adviseInPlaceOfInjecting, boundBeforeProviders, meocordClassAdvice, meocordClasses };
|
|
@@ -3,9 +3,7 @@ import { COOLDOWN_POLICY, cooldownPolicyFrom, handlerCooldowns } from './cooldow
|
|
|
3
3
|
import { Container } from 'inversify';
|
|
4
4
|
import { Client } from 'discord.js';
|
|
5
5
|
import { Logger } from '../common/logger.js';
|
|
6
|
-
import
|
|
7
|
-
import './theme-scope.js';
|
|
8
|
-
import './theme-validation.js';
|
|
6
|
+
import '../common/theme.js';
|
|
9
7
|
import '../common/decorator.js';
|
|
10
8
|
import { ExecutionContext } from '../common/execution-context.js';
|
|
11
9
|
import '../common/errors.js';
|
|
@@ -13,31 +11,34 @@ import { CooldownStore, MemoryCooldownStore } from '../common/cooldown-store.js'
|
|
|
13
11
|
import 'node:crypto';
|
|
14
12
|
import { getCommandMap, getMessageHandlers } from '../decorator/controller.decorator.js';
|
|
15
13
|
import './scalar-types.js';
|
|
14
|
+
import '../common/deprecation.js';
|
|
16
15
|
import { processShardingEnabled, shardingRole, clientOptionsWithSharding } from '../util/sharding-mode.util.js';
|
|
17
|
-
import { Translator
|
|
16
|
+
import { Translator } from '../common/translator.js';
|
|
18
17
|
import { registerClientTranslator } from '../common/meocord-text.js';
|
|
19
18
|
import '../common/response/response-state.js';
|
|
20
19
|
import { isExplainedError, markExplained } from '../common/explained-error.js';
|
|
21
|
-
import
|
|
20
|
+
import './theme-scope.js';
|
|
21
|
+
import { themeResolverClass } from './theme-resolvers.js';
|
|
22
22
|
import { MeoCordApp } from './meocord.app.js';
|
|
23
23
|
import { loadMeoCordConfig, compiledConfigMessage } from '../util/meocord-config-loader.util.js';
|
|
24
24
|
import { bunDevelopmentValues, bunDevelopmentWarning } from '../util/inherited-env.util.js';
|
|
25
25
|
import { isRegisterOnly } from '../util/registration-mode.util.js';
|
|
26
|
-
import {
|
|
26
|
+
import { prepareDirectCalls, injectedTokens, singletonContextError } from './guard-runner.js';
|
|
27
27
|
import { appStages, bindGlobalStages, prepareHandlerStages, prepareThemes, bindAppPresenter } from './handler-pipeline.js';
|
|
28
|
-
import { startupClasses, assertStartupClasses, lateGuardCheck, stageDependencies } from './startup-roots.js';
|
|
28
|
+
import { startupClasses, assertStartupClasses, lateGuardCheck, stageDependencies, perCallEventWarnings } from './startup-roots.js';
|
|
29
|
+
import { eventSources } from './event-sources.js';
|
|
29
30
|
import { markStartupChecked, assertStartupChecked } from './startup-checked.js';
|
|
30
31
|
import { makeInjectable } from '../util/injectable.util.js';
|
|
31
32
|
import { meocordClasses } from './meocord-classes.js';
|
|
32
33
|
import { buildMessageRoutes } from './message-routes.js';
|
|
33
34
|
import { appObservers, bindObservers } from './observer-runner.js';
|
|
34
35
|
import { isAppClassToken } from './lifecycle-order.js';
|
|
35
|
-
import {
|
|
36
|
-
import { HandlerRegistry } from './handler-registry.js';
|
|
36
|
+
import { providerMap, assertProvidedServices, bindsOwnToken, appProviderRefusal, bindProvider, resolutionOrder, isClassProvider, assertProvided, tokenDependencies, tokenName, resolveProviders, isValueProvider, forgetFactoryValues } from './providers.js';
|
|
37
|
+
import { HandlerRegistry, listEventSources } from './handler-registry.js';
|
|
37
38
|
import { ShardManager } from './shard-manager.js';
|
|
38
39
|
import { warnUnregisteredCommands, warnInheritedRoutes, warnOverlappingPatterns, warnHandlersOffControllers, assertDistinctCommands } from './command-conflicts.js';
|
|
39
40
|
import { ShardContext, SHARD_CALL_KEY, shardCallHandler } from './shard-context.js';
|
|
40
|
-
import { registerClientTheme } from './theme-runtime.js';
|
|
41
|
+
import { prepareLateThemes, registerClientTheme } from './theme-runtime.js';
|
|
41
42
|
import { isRefusal, logStartupErrors, describeRefusal, allDeclaredErrors, declaredErrorsOf, collectStartupErrors, refuse, startupError, stopOnStartupErrors } from '../util/refusal.util.js';
|
|
42
43
|
import { endFailedShard } from './shard-exit.js';
|
|
43
44
|
import { isBuiltApplication } from '../util/bundle-entry.util.js';
|
|
@@ -55,8 +56,7 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
55
56
|
container.bind(cls).toSelf().inSingletonScope();
|
|
56
57
|
// By constructor type or @inject token; an interface-typed parameter records Object, which is skipped
|
|
57
58
|
for (const dep of injectedTokens(cls)){
|
|
58
|
-
if (dep
|
|
59
|
-
else if (isAppClassToken(dep)) bindDependencies(container, dep, providers);
|
|
59
|
+
if (isAppClassToken(dep)) bindDependencies(container, dep, providers);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
@@ -145,6 +145,10 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
145
145
|
markExplained(error);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
// A built bot that refuses its app exits 1, as one whose start fails does, whatever main.ts does with the error
|
|
149
|
+
if (!shard && isBuiltApplication() && isRefusal(error) && (process.exitCode === undefined || process.exitCode === 0)) {
|
|
150
|
+
process.exitCode = 1;
|
|
151
|
+
}
|
|
148
152
|
throw error;
|
|
149
153
|
}
|
|
150
154
|
}
|
|
@@ -182,24 +186,24 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
182
186
|
});
|
|
183
187
|
// A themeFor class is bound on its own, as the cooldown store is, and resolved like a service
|
|
184
188
|
const themeResolver = themeResolverClass(options.themeFor);
|
|
185
|
-
//
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
189
|
+
// Every class the app runs, its stages and presenter and what they inject included
|
|
190
|
+
const stages = appStages(target);
|
|
191
|
+
const runs = startupClasses({
|
|
192
|
+
controllers: options.controllers,
|
|
193
|
+
services: options.services ?? [],
|
|
194
|
+
providers,
|
|
195
|
+
stages,
|
|
196
|
+
presenter: options.presenter,
|
|
197
|
+
cooldownStore: options.cooldownStore,
|
|
198
|
+
themeFor: options.themeFor,
|
|
199
|
+
observers: appObservers(target)
|
|
200
|
+
});
|
|
197
201
|
// A shard's manager runs the same checks, so a sharded bot warns once
|
|
198
202
|
if (!shard) {
|
|
199
203
|
warnUnregisteredCommands(options.controllers);
|
|
200
204
|
warnInheritedRoutes(options.controllers);
|
|
201
205
|
warnOverlappingPatterns(options.controllers);
|
|
202
|
-
warnHandlersOffControllers(options.controllers,
|
|
206
|
+
warnHandlersOffControllers(options.controllers, runs.classes);
|
|
203
207
|
const developmentEnv = bunDevelopmentValues();
|
|
204
208
|
if (developmentEnv.keys.length > 0) {
|
|
205
209
|
this.logger.warn(bunDevelopmentWarning(developmentEnv));
|
|
@@ -220,19 +224,8 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
220
224
|
}
|
|
221
225
|
// The checks of what the app binds and runs report every error each finds, and the first is thrown
|
|
222
226
|
return collectStartupErrors(()=>{
|
|
223
|
-
// Every class the app runs
|
|
224
|
-
//
|
|
225
|
-
const stages = appStages(target);
|
|
226
|
-
const runs = startupClasses({
|
|
227
|
-
controllers: options.controllers,
|
|
228
|
-
services: options.services ?? [],
|
|
229
|
-
providers,
|
|
230
|
-
stages,
|
|
231
|
-
presenter: options.presenter,
|
|
232
|
-
cooldownStore: options.cooldownStore,
|
|
233
|
-
themeFor: options.themeFor,
|
|
234
|
-
observers: appObservers(target)
|
|
235
|
-
});
|
|
227
|
+
// Every class the app runs checked before binding, where inversify would otherwise fail first with an error about
|
|
228
|
+
// compiler options, or a guard only at its first call
|
|
236
229
|
assertStartupClasses(runs, {
|
|
237
230
|
translator: options.i18n !== undefined || providers.has(Translator)
|
|
238
231
|
});
|
|
@@ -257,7 +250,8 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
257
250
|
}
|
|
258
251
|
// Bound before the app's classes, so a class that injects it gets this instance; filled once they are bound
|
|
259
252
|
const appClasses = [];
|
|
260
|
-
|
|
253
|
+
const registry = new HandlerRegistry(appClasses, options.messages, ()=>options.i18n, options.controllers);
|
|
254
|
+
container.bind(HandlerRegistry).toConstantValue(registry);
|
|
261
255
|
container.bind(ShardContext).toDynamicValue(()=>{
|
|
262
256
|
const client = container.get(Client);
|
|
263
257
|
return new ShardContext(client, (service, method, args)=>Reflect.get(client, SHARD_CALL_KEY)(service, method, args));
|
|
@@ -277,12 +271,9 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
277
271
|
// After MeoCord's own tokens, which a provider may not replace, and before the app's classes, so
|
|
278
272
|
// a class token that is provided is not also bound as itself
|
|
279
273
|
for (const [token, provider] of providers){
|
|
280
|
-
|
|
281
|
-
if (
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
if (container.isBound(token)) {
|
|
285
|
-
startupError(new Error(`${target.name}: @MeoCord({ providers }) cannot provide ${tokenName(token)}, which MeoCord binds itself.`));
|
|
274
|
+
const refusal = appProviderRefusal(`${target.name}`, token, (own)=>container.isBound(own), this.logger);
|
|
275
|
+
if (refusal) {
|
|
276
|
+
startupError(refusal);
|
|
286
277
|
continue;
|
|
287
278
|
}
|
|
288
279
|
bindProvider(container, provider, (cls)=>bindDependencies(container, cls, providers));
|
|
@@ -367,26 +358,35 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
367
358
|
buildMessageRoutes(options.controllers, options.messages);
|
|
368
359
|
// Every check has run, and touched only the container: from here the app is made, once none found an error
|
|
369
360
|
stopOnStartupErrors();
|
|
361
|
+
for (const warning of perCallEventWarnings(runs, providers, options.presenter))this.logger.warn(warning);
|
|
370
362
|
const discordClient = container.get(Client);
|
|
371
363
|
Reflect.set(discordClient, SHARD_CALL_KEY, shardCallHandler(container, ()=>callable, 'this app'));
|
|
372
|
-
//
|
|
373
|
-
|
|
374
|
-
Reflect.defineMetadata(META.container, container, cls);
|
|
375
|
-
}
|
|
376
|
-
recordInstanceContainers(container, runs.classes);
|
|
364
|
+
// Every class the app runs, so @UseGuard resolves guards on a direct call to any of them
|
|
365
|
+
prepareDirectCalls(container, runs.classes);
|
|
377
366
|
prepareThemes(container, appClasses);
|
|
378
367
|
// Run by start() before it logs in: what may inject a provided value is resolved once every factory
|
|
379
368
|
// has made its value, including those that return a promise
|
|
380
369
|
const logger = this.logger;
|
|
370
|
+
let sources;
|
|
381
371
|
const startup = async (control)=>{
|
|
372
|
+
let made;
|
|
382
373
|
try {
|
|
383
|
-
await resolveProviders(container, providers, order, control);
|
|
374
|
+
made = await resolveProviders(container, providers, order, control);
|
|
384
375
|
} catch (error) {
|
|
385
376
|
logger.error(`${error.message}. The bot cannot start without it.`);
|
|
386
377
|
logger.debug('Provider failure:', error.cause);
|
|
387
378
|
markExplained(error);
|
|
388
379
|
throw error;
|
|
389
380
|
}
|
|
381
|
+
// A stop came while the providers were being made: what follows resolves provided values, which would make the
|
|
382
|
+
// factories it left unmade
|
|
383
|
+
if (!made) return;
|
|
384
|
+
// Read once every provided value is made, and before anything the app makes could list its handlers
|
|
385
|
+
if (!sources) {
|
|
386
|
+
sources = eventSources(container, providers, lifecycle, appClasses, (text)=>logger.warn(text));
|
|
387
|
+
listEventSources(registry, sources);
|
|
388
|
+
prepareLateThemes(container, sources.map(({ cls })=>cls));
|
|
389
|
+
}
|
|
390
390
|
// The listed services are made now, so constructors that attach listeners or connect run before login; a stop
|
|
391
391
|
// meanwhile makes no more of them
|
|
392
392
|
for (const svc of options.services ?? []){
|
|
@@ -401,11 +401,38 @@ import { isShardProcess } from '../util/shard-process.util.js';
|
|
|
401
401
|
registerClientTheme(discordClient, container);
|
|
402
402
|
registerClientTranslator(discordClient, options.i18n);
|
|
403
403
|
};
|
|
404
|
-
return new MeoCordApp(options.controllers, container, discordClient, meocordConfig.discordToken, options.activities,
|
|
405
|
-
options.warnUnanswered ?? process.env.NODE_ENV === 'development', shard);
|
|
404
|
+
return new MeoCordApp(options.controllers, container, discordClient, meocordConfig.discordToken, options.activities, ()=>sources ?? [], meocordConfig.shutdownTimeout, startup, lifecycle, options.messages, // In development only, unless the app says otherwise
|
|
405
|
+
options.warnUnanswered ?? process.env.NODE_ENV === 'development', shard, startAttempt(container, lifecycle, providers));
|
|
406
406
|
});
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
409
|
MeoCordFactory.logger = new Logger();
|
|
410
|
+
/**
|
|
411
|
+
* What the app's starts make: each lifecycle unit is recorded as the container constructs it, as the testing module
|
|
412
|
+
* records them, and a container snapshot marks where a start begins, so a failed start drops every singleton it made,
|
|
413
|
+
* the values factory providers keep included.
|
|
414
|
+
*/ function startAttempt(container, lifecycle, providers) {
|
|
415
|
+
const constructed = new Set();
|
|
416
|
+
for (const { token } of lifecycle){
|
|
417
|
+
container.onActivation(token, (_context, instance)=>{
|
|
418
|
+
constructed.add(token);
|
|
419
|
+
return instance;
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
const drop = ()=>{
|
|
423
|
+
container.restore();
|
|
424
|
+
forgetFactoryValues(container);
|
|
425
|
+
};
|
|
426
|
+
const given = new Set(lifecycle.map(({ token })=>token).filter((token)=>{
|
|
427
|
+
const provider = providers.get(token);
|
|
428
|
+
return provider !== undefined && isValueProvider(provider);
|
|
429
|
+
}));
|
|
430
|
+
return {
|
|
431
|
+
constructed,
|
|
432
|
+
given,
|
|
433
|
+
begin: ()=>container.snapshot(),
|
|
434
|
+
drop
|
|
435
|
+
};
|
|
436
|
+
}
|
|
410
437
|
|
|
411
438
|
export { MeoCordFactory };
|