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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dropInheritedRoutes } from './controller.decorator.js';
|
|
2
|
-
import { guardOwnHandlersWithBaseGuards } from '
|
|
2
|
+
import { guardOwnHandlersWithBaseGuards } from '../core/guard-runner.js';
|
|
3
3
|
import { makeInjectable } from '../util/injectable.util.js';
|
|
4
4
|
import { META } from '../util/metadata-keys.js';
|
|
5
5
|
import { declaring, deprecatedOnMethod } from '../util/refusal.util.js';
|
|
@@ -7,8 +7,8 @@ import { wrapsMethod } from '../util/method-wrapper.util.js';
|
|
|
7
7
|
import { warnDeprecatedBehaviour } from '../common/deprecation.js';
|
|
8
8
|
import { Logger } from '../common/logger.js';
|
|
9
9
|
import { routeSpecificity } from '../core/route-specificity.js';
|
|
10
|
-
import { lookupTable, isSegmentType, choicesOf, parseSegment } from '../core/scalar-types.js';
|
|
11
|
-
import { declaring, refuse, refuseOnClass } from '../util/refusal.util.js';
|
|
10
|
+
import { lookupTable, isSegmentType, choicesOf, shortestSegment, parseSegment } from '../core/scalar-types.js';
|
|
11
|
+
import { declaring, refuse, refuseOnClass, decoratedName } from '../util/refusal.util.js';
|
|
12
12
|
import { withArticle, describeValue } from '../util/value.util.js';
|
|
13
13
|
import { META } from '../util/metadata-keys.js';
|
|
14
14
|
|
|
@@ -18,6 +18,12 @@ import { META } from '../util/metadata-keys.js';
|
|
|
18
18
|
return `; it was given ${describeValue(value)}`;
|
|
19
19
|
}
|
|
20
20
|
const logger = new Logger('Command');
|
|
21
|
+
/** The interaction types a method's `@Command` routes declare, each once. */ function routeTypesOf(prototype, method) {
|
|
22
|
+
const metas = Object.values(getCommandMap(prototype) ?? {}).flat();
|
|
23
|
+
return [
|
|
24
|
+
...new Set(metas.filter((meta)=>meta.methodName === method).map((meta)=>meta.type))
|
|
25
|
+
];
|
|
26
|
+
}
|
|
21
27
|
/**
|
|
22
28
|
* The class's own handler list, started from a copy of the inherited one, so a subclass's
|
|
23
29
|
* handlers never land in its base class's metadata.
|
|
@@ -43,6 +49,10 @@ const autocompleteRoute = (method, commandPath, optionName)=>({
|
|
|
43
49
|
method,
|
|
44
50
|
label: `autocomplete of ${optionName === undefined ? 'every option' : `"${optionName}"`} in "${commandPath}"`
|
|
45
51
|
});
|
|
52
|
+
/** An `@On` or `@Once` handler as a warning names its route; one event per method, as a subclass replaces it. */ const eventRoute = ({ method, event })=>({
|
|
53
|
+
method,
|
|
54
|
+
label: `event "${event}"`
|
|
55
|
+
});
|
|
46
56
|
/** Records a route a class's own decorator declares for a method, which the startup check reads. */ function declareRoute(target, declared) {
|
|
47
57
|
const routes = Reflect.getOwnMetadata(META.declaredRoutes, target) ?? [];
|
|
48
58
|
routes.push(declared);
|
|
@@ -70,13 +80,22 @@ const autocompleteRoute = (method, commandPath, optionName)=>({
|
|
|
70
80
|
else handlers[index] = entry;
|
|
71
81
|
Reflect.defineMetadata(key, handlers, target);
|
|
72
82
|
}
|
|
83
|
+
/** The `@On` and `@Once` entries a class's own decorators declare, without those it inherits from its base. */ function ownEventHandlers(prototype) {
|
|
84
|
+
const inherited = Reflect.getMetadata(META.eventHandlers, Object.getPrototypeOf(prototype)) ?? [];
|
|
85
|
+
return ownHandlerList(META.eventHandlers, prototype).filter((entry)=>!inherited.includes(entry));
|
|
86
|
+
}
|
|
73
87
|
/**
|
|
74
88
|
* Leaves each method the class's own decorators declare only those routes, of every kind, dropping the ones it inherits
|
|
75
89
|
* for it: what `@Controller({ inheritedRoutes: 'replace' })` does once the class's method decorators have run.
|
|
76
90
|
*/ function dropInheritedRoutes(prototype) {
|
|
77
|
-
const redeclared = new Set(getDeclaredRoutes(prototype).map((route)=>route.method));
|
|
78
|
-
if (redeclared.size === 0) return;
|
|
79
91
|
const base = Object.getPrototypeOf(prototype);
|
|
92
|
+
// The class's own @On and @Once re-declare their methods too
|
|
93
|
+
const ownEvents = ownEventHandlers(prototype);
|
|
94
|
+
const redeclared = new Set([
|
|
95
|
+
...getDeclaredRoutes(prototype).map((route)=>route.method),
|
|
96
|
+
...ownEvents.map((entry)=>entry.method)
|
|
97
|
+
]);
|
|
98
|
+
if (redeclared.size === 0) return;
|
|
80
99
|
// An entry the class takes from its base, for a method it re-declares
|
|
81
100
|
const inheritedFor = (inherited, method)=>(entry)=>inherited.includes(entry) && redeclared.has(method(entry));
|
|
82
101
|
const commands = ownCommandMap(prototype);
|
|
@@ -99,6 +118,10 @@ const autocompleteRoute = (method, commandPath, optionName)=>({
|
|
|
99
118
|
[
|
|
100
119
|
META.autocompleteHandlers,
|
|
101
120
|
(entry)=>entry.methodName
|
|
121
|
+
],
|
|
122
|
+
[
|
|
123
|
+
META.eventHandlers,
|
|
124
|
+
(entry)=>entry.method
|
|
102
125
|
]
|
|
103
126
|
];
|
|
104
127
|
for (const [key, method] of lists){
|
|
@@ -125,13 +148,14 @@ const autocompleteRoute = (method, commandPath, optionName)=>({
|
|
|
125
148
|
function MessageHandler(pattern, options = {}) {
|
|
126
149
|
return declaring(function(target, propertyKey) {
|
|
127
150
|
refuseOnClass('@MessageHandler', target, propertyKey);
|
|
128
|
-
// An empty pattern means every message, as no pattern does
|
|
129
|
-
|
|
130
|
-
|
|
151
|
+
// An empty pattern, or one of spaces alone, means every message, as no pattern does
|
|
152
|
+
const blank = pattern !== undefined && pattern.trim() === '';
|
|
153
|
+
if (blank) {
|
|
154
|
+
warnDeprecatedBehaviour(logger, `@MessageHandler('${pattern}') on ${decoratedName(target, propertyKey)}`, 'is refused', '@MessageHandler()');
|
|
131
155
|
}
|
|
132
156
|
const method = propertyKey.toString();
|
|
133
157
|
const declared = {
|
|
134
|
-
pattern:
|
|
158
|
+
pattern: blank ? undefined : pattern,
|
|
135
159
|
method,
|
|
136
160
|
options
|
|
137
161
|
};
|
|
@@ -253,13 +277,14 @@ const WHOLE_PLACEHOLDER = new RegExp(`^${PLACEHOLDER_PATTERN.source}$`);
|
|
|
253
277
|
params: params.length,
|
|
254
278
|
typed: Object.values(types).reduce((sum, type)=>sum + (choicesOf(type) ? 4 : type === 'bool' ? 3 : type === 'int' ? 2 : 1), 0)
|
|
255
279
|
});
|
|
256
|
-
// Each param takes at least
|
|
280
|
+
// Each param takes at least the shortest value of its type
|
|
281
|
+
const shortest = literalLength + params.reduce((sum, param)=>sum + shortestSegment(types[param] ?? 'string'), 0);
|
|
257
282
|
return {
|
|
258
283
|
regex,
|
|
259
284
|
params,
|
|
260
285
|
types: lookupTable(types),
|
|
261
286
|
specificity,
|
|
262
|
-
shortest
|
|
287
|
+
shortest
|
|
263
288
|
};
|
|
264
289
|
}
|
|
265
290
|
/** Why a `{name:type}` cannot type a customId segment, which holds text the bot wrote, with no message to read. */ function segmentTypeProblem(param, type) {
|
|
@@ -332,13 +357,17 @@ const WHOLE_PLACEHOLDER = new RegExp(`^${PLACEHOLDER_PATTERN.source}$`);
|
|
|
332
357
|
refuseOnClass('@Command', target, propertyKey);
|
|
333
358
|
const originalMethod = _descriptor.value;
|
|
334
359
|
if (!originalMethod) {
|
|
335
|
-
throw refuse(new Error(`${target
|
|
360
|
+
throw refuse(new Error(`${decoratedName(target, propertyKey)}: @Command is on something with no implementation.`));
|
|
336
361
|
}
|
|
337
362
|
// Wrap original method for interaction type validation
|
|
338
363
|
const declaredAs = typeof builderOrType === 'function' ? builderOrType.name : `CommandType.${builderOrType}`;
|
|
339
364
|
const checked = function(interaction, params) {
|
|
340
|
-
|
|
341
|
-
|
|
365
|
+
// Every type the method's routes declare, in the class the call is made on: each @Command wraps it, so one alone
|
|
366
|
+
// would refuse the others' interactions, and an override that adds a route type reaches this one through super
|
|
367
|
+
const called = typeof this === 'object' && this !== null && Object.prototype.isPrototypeOf.call(target, this) ? Object.getPrototypeOf(this) : target;
|
|
368
|
+
const types = routeTypesOf(called, propertyKey);
|
|
369
|
+
if (!types.some((type)=>matchesCommandType(type, interaction))) {
|
|
370
|
+
throw new Error(types.length <= 1 ? `${decoratedName(target, propertyKey)}: @Command('${commandName}', ${declaredAs}) takes ` + `${interactionClassName(commandType)}${givenInstead(interaction)}.` : `${decoratedName(target, propertyKey)}: its @Command routes take ${types.map(interactionClassName).join(' or ')}` + `${givenInstead(interaction)}.`);
|
|
342
371
|
}
|
|
343
372
|
return originalMethod.apply(this, [
|
|
344
373
|
interaction,
|
|
@@ -356,7 +385,7 @@ const WHOLE_PLACEHOLDER = new RegExp(`^${PLACEHOLDER_PATTERN.source}$`);
|
|
|
356
385
|
let guilds;
|
|
357
386
|
// Determine command type and builder
|
|
358
387
|
if (typeof builderOrType === 'function') {
|
|
359
|
-
const where =
|
|
388
|
+
const where = decoratedName(target, propertyKey);
|
|
360
389
|
// A subcommand is part of its command's builder, which belongs on the command's own name
|
|
361
390
|
const subcommandPath = Reflect.getMetadata(META.commandType, builderOrType) === CommandType.SLASH && commandName.includes(' ');
|
|
362
391
|
const command = commandName.split(' ')[0];
|
|
@@ -403,12 +432,12 @@ const WHOLE_PLACEHOLDER = new RegExp(`^${PLACEHOLDER_PATTERN.source}$`);
|
|
|
403
432
|
if (isCustomIdRouted(commandType)) {
|
|
404
433
|
// A route warned as it was made
|
|
405
434
|
const braces = typeof name === 'string' ? literalBraces(commandName) : undefined;
|
|
406
|
-
if (braces) logger.warn(`${target
|
|
435
|
+
if (braces) logger.warn(`${decoratedName(target, propertyKey)}: in the pattern "${commandName}", ${braces}`);
|
|
407
436
|
let pattern;
|
|
408
437
|
try {
|
|
409
438
|
pattern = createRegexFromPattern(commandName);
|
|
410
439
|
} catch (error) {
|
|
411
|
-
throw refuse(new Error(`${target
|
|
440
|
+
throw refuse(new Error(`${decoratedName(target, propertyKey)}: ${error.message}`, {
|
|
412
441
|
cause: error
|
|
413
442
|
}));
|
|
414
443
|
}
|
|
@@ -418,7 +447,7 @@ const WHOLE_PLACEHOLDER = new RegExp(`^${PLACEHOLDER_PATTERN.source}$`);
|
|
|
418
447
|
// Warned rather than refused, so a bot that carries such a handler still starts
|
|
419
448
|
if (pattern.shortest === 0 || pattern.shortest > MAX_CUSTOM_ID_LENGTH) {
|
|
420
449
|
const length = pattern.shortest === 0 ? 'the pattern is empty' : `its shortest is ${pattern.shortest} characters`;
|
|
421
|
-
logger.warn(`${target
|
|
450
|
+
logger.warn(`${decoratedName(target, propertyKey)}: the pattern "${commandName}" matches no customId: Discord's customIds ` + `are 1 to ${MAX_CUSTOM_ID_LENGTH} characters, and ${length}, so the handler never runs.`);
|
|
422
451
|
}
|
|
423
452
|
}
|
|
424
453
|
const declared = {
|
|
@@ -470,7 +499,7 @@ const WHOLE_PLACEHOLDER = new RegExp(`^${PLACEHOLDER_PATTERN.source}$`);
|
|
|
470
499
|
const registered = built?.type;
|
|
471
500
|
if (!declared || registered === undefined || declared.kind === registered) return;
|
|
472
501
|
const kind = registered === ApplicationCommandType.User ? 'user' : 'message';
|
|
473
|
-
throw refuse(new Error(`${target
|
|
502
|
+
throw refuse(new Error(`${decoratedName(target, propertyKey)}: it takes a ${declared.name} context menu interaction, but ${builderName} ` + `registers "${commandName}" as a ${kind} context menu command. Declare the handler's interaction as the kind the ` + `builder's setType() names.`));
|
|
474
503
|
}
|
|
475
504
|
/**
|
|
476
505
|
* Retrieves the command map for a given controller.
|
|
@@ -608,4 +637,4 @@ const narrowness = (type)=>choicesOf(type) ? 6 : NARROWNESS[type];
|
|
|
608
637
|
return values.every((value)=>value !== undefined) ? values.join(PARAM_SEPARATOR) : undefined;
|
|
609
638
|
}
|
|
610
639
|
|
|
611
|
-
export { Autocomplete, Command, MAX_CUSTOM_ID_LENGTH, MessageHandler, PARAM_SEPARATOR, ReactionHandler, commandsNamed, createRegexFromPattern, dropInheritedRoutes, getAutocompleteHandlers, getCommandMap, getDeclaredRoutes, getHandlerRoutes, getMessageHandlers, getReactionHandlers, literalBraces, matchesEmoji, ownHandlerList, paramNarrowness, patternShape, sharedCustomId };
|
|
640
|
+
export { Autocomplete, Command, MAX_CUSTOM_ID_LENGTH, MessageHandler, PARAM_SEPARATOR, ReactionHandler, addOwnHandler, commandsNamed, createRegexFromPattern, dropInheritedRoutes, eventRoute, getAutocompleteHandlers, getCommandMap, getDeclaredRoutes, getHandlerRoutes, getMessageHandlers, getReactionHandlers, literalBraces, matchesEmoji, ownEventHandlers, ownHandlerList, paramNarrowness, patternShape, sharedCustomId };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { nonInteractionHandler, deferMisuseError } from '../core/defer.js';
|
|
3
|
-
import { declaring, refuseOnClass, refuse } from '../util/refusal.util.js';
|
|
3
|
+
import { declaring, refuseOnClass, refuse, ownerName } from '../util/refusal.util.js';
|
|
4
4
|
import { META } from '../util/metadata-keys.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -43,7 +43,7 @@ import { META } from '../util/metadata-keys.js';
|
|
|
43
43
|
refuseOnClass('@Defer', target, propertyKey);
|
|
44
44
|
const methodName = String(propertyKey);
|
|
45
45
|
const kind = nonInteractionHandler(target, methodName);
|
|
46
|
-
if (kind) throw refuse(deferMisuseError(target
|
|
46
|
+
if (kind) throw refuse(deferMisuseError(ownerName(target), methodName, kind));
|
|
47
47
|
Reflect.defineMetadata(META.deferOptions, {
|
|
48
48
|
...options
|
|
49
49
|
}, target, methodName);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import {
|
|
2
|
+
import { addOwnHandler } from './controller.decorator.js';
|
|
3
3
|
import { META } from '../util/metadata-keys.js';
|
|
4
4
|
import { declaring, refuseOnClass } from '../util/refusal.util.js';
|
|
5
5
|
|
|
@@ -7,13 +7,12 @@ function eventDecorator(event, once) {
|
|
|
7
7
|
// Generic over the method, so it is checked against the event's arguments rather than required to match exactly
|
|
8
8
|
return declaring(function(target, propertyKey, _descriptor) {
|
|
9
9
|
refuseOnClass(once ? '@Once' : '@On', target, propertyKey);
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
// One the class inherits for the same method and event is replaced, as for every other kind of handler
|
|
11
|
+
addOwnHandler(META.eventHandlers, target, {
|
|
12
12
|
event,
|
|
13
13
|
method: propertyKey,
|
|
14
14
|
once
|
|
15
|
-
});
|
|
16
|
-
Reflect.defineMetadata(META.eventHandlers, handlers, target);
|
|
15
|
+
}, (other)=>other.method === propertyKey && other.event === event);
|
|
17
16
|
});
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
@@ -5,7 +5,7 @@ import { Logger } from '../common/logger.js';
|
|
|
5
5
|
import { warnDeprecatedBehaviour } from '../common/deprecation.js';
|
|
6
6
|
import { isConstructible, describeValue } from '../util/value.util.js';
|
|
7
7
|
import { META } from '../util/metadata-keys.js';
|
|
8
|
-
import { declaring, refuseOnMethod } from '../util/refusal.util.js';
|
|
8
|
+
import { declaring, refuseOnMethod, decoratedName } from '../util/refusal.util.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Marks a class as an exception filter for the given error types.
|
|
@@ -88,7 +88,7 @@ const ORDINALS = [
|
|
|
88
88
|
* @see {@link https://meocord.dev/docs/4.2/exception-filters | Exception filters}
|
|
89
89
|
*/ function UseFilter(...filters) {
|
|
90
90
|
return declaring(function(target, propertyKey) {
|
|
91
|
-
const where =
|
|
91
|
+
const where = decoratedName(target, propertyKey);
|
|
92
92
|
assertStageEntries('@UseFilter', 'filter', where, filters);
|
|
93
93
|
// Decorators apply bottom-up, so a higher decorator's filters are tried first.
|
|
94
94
|
if (propertyKey === undefined) {
|
|
@@ -1,47 +1,10 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import {
|
|
2
|
+
import { applyGuards, recordGuards, handlerMethods, classLevelGuards, declaringPrototype } from '../core/guard-runner.js';
|
|
3
3
|
import { makeInjectable } from '../util/injectable.util.js';
|
|
4
|
-
import { wrapsMethod } from '../util/method-wrapper.util.js';
|
|
5
4
|
import { defineStageTypes, assertStageEntries } from '../core/stage-scope.js';
|
|
6
5
|
import { META } from '../util/metadata-keys.js';
|
|
7
|
-
import { declaring, deprecatedOnMethod } from '../util/refusal.util.js';
|
|
6
|
+
import { declaring, deprecatedOnMethod, decoratedName } from '../util/refusal.util.js';
|
|
8
7
|
|
|
9
|
-
/**
|
|
10
|
-
* Adds guards to a method's class or method list, then republishes the effective list under
|
|
11
|
-
* `META.guards`, in the order they run: the bases' before the class's own, and within one class or method the
|
|
12
|
-
* latest decorator first, since it wraps outermost.
|
|
13
|
-
*/ function recordGuards(key, guards, prototype, methodName) {
|
|
14
|
-
const existing = Reflect.getOwnMetadata(key, prototype, methodName) ?? [];
|
|
15
|
-
const bases = key === META.classGuards ? Reflect.getOwnMetadata(META.baseClassGuards, prototype, methodName) ?? 0 : 0;
|
|
16
|
-
Reflect.defineMetadata(key, [
|
|
17
|
-
...existing.slice(0, bases),
|
|
18
|
-
...guards,
|
|
19
|
-
...existing.slice(bases)
|
|
20
|
-
], prototype, methodName);
|
|
21
|
-
const [classGuards, inheritedGuards, methodGuards] = [
|
|
22
|
-
META.classGuards,
|
|
23
|
-
META.inheritedGuards,
|
|
24
|
-
META.methodGuards
|
|
25
|
-
].map((list)=>Reflect.getOwnMetadata(list, prototype, methodName) ?? []);
|
|
26
|
-
Reflect.defineMetadata(META.guards, [
|
|
27
|
-
...inheritedGuards,
|
|
28
|
-
...classGuards,
|
|
29
|
-
...methodGuards
|
|
30
|
-
], prototype, methodName);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Wraps a method so a direct call runs its guards first: the wrapper entered first runs the whole
|
|
34
|
-
* chain dispatch resolves for the handler, and lets the ones inside it through. A call from dispatch,
|
|
35
|
-
* which has already run the guards, passes through every wrapper.
|
|
36
|
-
*/ function applyGuards(descriptor, prototype, propertyKey) {
|
|
37
|
-
const originalMethod = descriptor.value;
|
|
38
|
-
descriptor.value = wrapsMethod(async function(...args) {
|
|
39
|
-
if (consumeDispatchMark(args[0], this, propertyKey)) return originalMethod.apply(this, args);
|
|
40
|
-
return runDirectCall(this, propertyKey, args, ()=>originalMethod.apply(this, args));
|
|
41
|
-
}, originalMethod);
|
|
42
|
-
const wrappers = Reflect.getOwnMetadata(META.guardWrappers, prototype, propertyKey) ?? 0;
|
|
43
|
-
Reflect.defineMetadata(META.guardWrappers, wrappers + 1, prototype, propertyKey);
|
|
44
|
-
}
|
|
45
8
|
/**
|
|
46
9
|
* The class's own descriptor for a handler. An inherited handler gets one that calls the inherited
|
|
47
10
|
* method, starting from its guard lists and wrapper count, so class guards wrap it like an own method.
|
|
@@ -69,26 +32,6 @@ import { declaring, deprecatedOnMethod } from '../util/refusal.util.js';
|
|
|
69
32
|
...inherited
|
|
70
33
|
};
|
|
71
34
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Gives each handler a controller declares itself the class-level guards of the classes it extends,
|
|
74
|
-
* as its inherited handlers have: recorded under `META.guards`, and wrapped so a direct call
|
|
75
|
-
* runs them when no other guard wraps the handler. `@Controller` calls this.
|
|
76
|
-
*/ function guardOwnHandlersWithBaseGuards(target) {
|
|
77
|
-
const prototype = target.prototype;
|
|
78
|
-
for (const methodName of handlerMethods(prototype)){
|
|
79
|
-
const descriptor = Object.getOwnPropertyDescriptor(prototype, methodName);
|
|
80
|
-
// Inherited handlers have the chain of the class that declares them
|
|
81
|
-
if (typeof descriptor?.value !== 'function' || Reflect.getOwnMetadata(META.inheritedFrom, prototype, methodName)) continue;
|
|
82
|
-
// Base first, as dispatch runs them
|
|
83
|
-
const guards = classChain(prototype, prototype).slice(1).reverse().flatMap(classLevelGuards);
|
|
84
|
-
if (guards.length === 0) continue;
|
|
85
|
-
if (!Reflect.getOwnMetadata(META.guardWrappers, prototype, methodName)) {
|
|
86
|
-
applyGuards(descriptor, prototype, methodName);
|
|
87
|
-
Object.defineProperty(prototype, methodName, descriptor);
|
|
88
|
-
}
|
|
89
|
-
recordGuards(META.inheritedGuards, guards, prototype, methodName);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
35
|
/**
|
|
93
36
|
* Marks a class as a guard, which decides whether a handler runs.
|
|
94
37
|
*
|
|
@@ -162,7 +105,7 @@ import { declaring, deprecatedOnMethod } from '../util/refusal.util.js';
|
|
|
162
105
|
*/ function UseGuard(...entries) {
|
|
163
106
|
const guards = entries;
|
|
164
107
|
return declaring(function(target, propertyKey, descriptor) {
|
|
165
|
-
const where =
|
|
108
|
+
const where = decoratedName(target, propertyKey);
|
|
166
109
|
assertStageEntries('@UseGuard', 'guard', where, guards);
|
|
167
110
|
if (descriptor && propertyKey) {
|
|
168
111
|
// Method Decorator
|
|
@@ -189,4 +132,4 @@ import { declaring, deprecatedOnMethod } from '../util/refusal.util.js';
|
|
|
189
132
|
});
|
|
190
133
|
}
|
|
191
134
|
|
|
192
|
-
export { Guard, UseGuard
|
|
135
|
+
export { Guard, UseGuard };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { inject } from 'inversify';
|
|
2
|
-
import { declaring, refuseOnClass } from '../util/refusal.util.js';
|
|
2
|
+
import { declaring, refuseOnClass, refuse, decoratedName } from '../util/refusal.util.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Injects what a token provides into a constructor parameter.
|
|
@@ -30,6 +30,9 @@ import { declaring, refuseOnClass } from '../util/refusal.util.js';
|
|
|
30
30
|
return declaring((target, propertyKey, parameterIndex)=>{
|
|
31
31
|
// A constructor parameter has no property key either, but has an index
|
|
32
32
|
if (typeof parameterIndex !== 'number') refuseOnClass('@Inject', target, propertyKey, 'a constructor parameter or a property');
|
|
33
|
+
else if (propertyKey !== undefined) {
|
|
34
|
+
throw refuse(new Error(`${decoratedName(target, propertyKey)}: @Inject goes on a constructor parameter or a property, not a method parameter.`));
|
|
35
|
+
}
|
|
33
36
|
decorate(target, propertyKey, parameterIndex);
|
|
34
37
|
});
|
|
35
38
|
}
|
|
@@ -2,7 +2,7 @@ import 'reflect-metadata';
|
|
|
2
2
|
import { makeInjectable } from '../util/injectable.util.js';
|
|
3
3
|
import { defineStageTypes, assertStageEntries } from '../core/stage-scope.js';
|
|
4
4
|
import { META } from '../util/metadata-keys.js';
|
|
5
|
-
import { declaring, refuseOnMethod } from '../util/refusal.util.js';
|
|
5
|
+
import { declaring, refuseOnMethod, decoratedName } from '../util/refusal.util.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Marks a class as an interceptor, which wraps a handler to act before and after it.
|
|
@@ -76,7 +76,7 @@ import { declaring, refuseOnMethod } from '../util/refusal.util.js';
|
|
|
76
76
|
* @see {@link https://meocord.dev/docs/4.2/interceptors | Interceptors}
|
|
77
77
|
*/ function UseInterceptor(...interceptors) {
|
|
78
78
|
return declaring(function(target, propertyKey) {
|
|
79
|
-
const where =
|
|
79
|
+
const where = decoratedName(target, propertyKey);
|
|
80
80
|
assertStageEntries('@UseInterceptor', 'interceptor', where, interceptors);
|
|
81
81
|
// Decorators apply bottom-up, so a higher decorator's interceptors go first, as with @UseGuard.
|
|
82
82
|
if (propertyKey === undefined) {
|
|
@@ -2,7 +2,7 @@ import 'reflect-metadata';
|
|
|
2
2
|
import { assertValidTheme } from '../core/theme-validation.js';
|
|
3
3
|
import { THEMED_CLASSES } from '../core/theme-runtime.js';
|
|
4
4
|
import { copyLayer } from '../core/theme-scope.js';
|
|
5
|
-
import { declaring, refuse } from '../util/refusal.util.js';
|
|
5
|
+
import { declaring, decoratedName, refuse } from '../util/refusal.util.js';
|
|
6
6
|
import { META } from '../util/metadata-keys.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -37,8 +37,9 @@ import { META } from '../util/metadata-keys.js';
|
|
|
37
37
|
*/ function UseTheme(theme) {
|
|
38
38
|
return declaring(function(target, propertyKey) {
|
|
39
39
|
const onClass = propertyKey === undefined;
|
|
40
|
-
|
|
41
|
-
const
|
|
40
|
+
// The class it themes: the target itself on a class or a static member, else the target's constructor
|
|
41
|
+
const owner = onClass || typeof target === 'function' ? target : target.constructor;
|
|
42
|
+
const where = `${decoratedName(target, propertyKey)}: @UseTheme`;
|
|
42
43
|
assertValidTheme(theme, where);
|
|
43
44
|
const key = onClass ? META.classTheme : META.methodTheme;
|
|
44
45
|
const existing = onClass ? Reflect.getOwnMetadata(key, target) : Reflect.getOwnMetadata(key, target, propertyKey);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { makeInjectable } from '../util/injectable.util.js';
|
|
3
3
|
import { assertStageEntries } from '../core/stage-scope.js';
|
|
4
|
-
import { declaring, refuseOnClass, refuse, refuseOnMethod } from '../util/refusal.util.js';
|
|
4
|
+
import { declaring, refuseOnClass, refuse, decoratedName, refuseOnMethod } from '../util/refusal.util.js';
|
|
5
5
|
import { META } from '../util/metadata-keys.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -43,15 +43,15 @@ import { META } from '../util/metadata-keys.js';
|
|
|
43
43
|
refuseOnClass('@Validate', target, propertyKey);
|
|
44
44
|
// Checked where it applies, so the refusal names the handler
|
|
45
45
|
if (typeof schema?.['~standard']?.validate !== 'function') {
|
|
46
|
-
throw refuse(new Error(`${target
|
|
46
|
+
throw refuse(new Error(`${decoratedName(target, propertyKey)}: @Validate takes a Standard Schema, such as a zod, valibot or arktype schema.`));
|
|
47
47
|
}
|
|
48
48
|
if (Reflect.hasOwnMetadata(META.methodValidation, target, propertyKey)) {
|
|
49
|
-
throw refuse(new Error(`${target
|
|
49
|
+
throw refuse(new Error(`${decoratedName(target, propertyKey)}: more than one @Validate; one @Validate per handler: combine the schemas into one.`));
|
|
50
50
|
}
|
|
51
51
|
const inlinePipes = Object.values(options.pipes ?? {}).flatMap((entries)=>Array.isArray(entries) ? entries : [
|
|
52
52
|
entries
|
|
53
53
|
]);
|
|
54
|
-
assertStageEntries('@Validate', 'pipe',
|
|
54
|
+
assertStageEntries('@Validate', 'pipe', decoratedName(target, propertyKey), inlinePipes);
|
|
55
55
|
const metadata = {
|
|
56
56
|
schema,
|
|
57
57
|
pipes: options.pipes ?? {}
|
|
@@ -93,7 +93,7 @@ import { META } from '../util/metadata-keys.js';
|
|
|
93
93
|
*/ function UsePipe(key, ...pipes) {
|
|
94
94
|
return declaring(function(target, propertyKey, _descriptor) {
|
|
95
95
|
refuseOnClass('@UsePipe', target, propertyKey);
|
|
96
|
-
assertStageEntries('@UsePipe', 'pipe',
|
|
96
|
+
assertStageEntries('@UsePipe', 'pipe', decoratedName(target, propertyKey), pipes);
|
|
97
97
|
// Decorators apply bottom-up, so a higher @UsePipe's pipes go first, in the order they read.
|
|
98
98
|
const existing = Reflect.getOwnMetadata(META.methodPipes, target, propertyKey) ?? [];
|
|
99
99
|
Reflect.defineMetadata(META.methodPipes, [
|
package/dist/esm/package.json.js
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
import { isPluralShape, CATALOGS, placeholderNames } from '../common/translator.js';
|
|
2
2
|
import { MEOCORD_MESSAGES } from '../common/meocord-messages.js';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Every message key of a catalog, and whether it is a plural. `reference` decides whether an object is a plural or a
|
|
6
|
+
* group, as the translator reads a translation by the default catalog's shapes; where it lacks a key, the object's own
|
|
7
|
+
* keys decide.
|
|
8
|
+
*/ function leaves(catalog, prefix = '', reference = catalog) {
|
|
5
9
|
return Object.entries(catalog).flatMap(([name, value])=>{
|
|
6
10
|
const key = `${prefix}${name}`;
|
|
7
|
-
|
|
11
|
+
// A null message is one where it stands, which the checks below read as missing
|
|
12
|
+
if (typeof value === 'string' || value === null) return [
|
|
8
13
|
{
|
|
9
14
|
key,
|
|
10
15
|
plural: false
|
|
11
16
|
}
|
|
12
17
|
];
|
|
13
|
-
|
|
18
|
+
const shape = typeof reference === 'object' && reference !== null && Object.hasOwn(reference, name) ? reference[name] : undefined;
|
|
19
|
+
const plural = typeof shape === 'object' && shape !== null ? isPluralShape(shape) : isPluralShape(value);
|
|
20
|
+
if (plural) return [
|
|
14
21
|
{
|
|
15
22
|
key,
|
|
16
23
|
plural: true
|
|
17
24
|
}
|
|
18
25
|
];
|
|
19
|
-
return leaves(value, `${key}
|
|
26
|
+
return leaves(value, `${key}.`, shape);
|
|
20
27
|
});
|
|
21
28
|
}
|
|
22
29
|
/** A translation's text, which an empty string isn't: the translator reads `''` as missing, as tools export one untranslated. */ const isText = (value)=>typeof value === 'string' && value !== '';
|
|
30
|
+
/** A message the translator reads as missing: none, `null` or `''`, as tools export one untranslated. */ const isMissing = (value)=>value === undefined || value === null || value === '';
|
|
23
31
|
/** The value at a key's path, through own properties only, so `constructor` or `toString` is not read off a prototype. */ function at(catalog, key) {
|
|
24
32
|
return key.split('.').reduce((current, part)=>typeof current === 'object' && current !== null && Object.hasOwn(current, part) ? current[part] : undefined, catalog);
|
|
25
33
|
}
|
|
@@ -44,11 +52,13 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
44
52
|
*
|
|
45
53
|
* Use it in a test for a team that wants no fallback in production. It reports the messages a locale lacks, the
|
|
46
54
|
* messages the default catalog doesn't have, the plural forms a language needs but a plural lacks, such as `few` for
|
|
47
|
-
* Russian, a translation of another shape than the default's, a text for a plural or a plural for a text, and each
|
|
48
|
-
* `{param}` a translation uses that the message it translates doesn't take. An empty translation, `''`, counts
|
|
49
|
-
* lacking, as the translator reads it as missing. It reads the catalogs' own strings, so it checks the params of a
|
|
50
|
-
* catalog from a plain variable or a JSON file too, which the compiler can't. An object
|
|
51
|
-
* its every key is a plural category, so a group with an `other` key is a group
|
|
55
|
+
* Russian, a translation of another shape than the default's, a text for a plural or a group, or a plural for a text, and each
|
|
56
|
+
* `{param}` a translation uses that the message it translates doesn't take. An empty translation, `''` or `null`, counts
|
|
57
|
+
* as lacking, as the translator reads it as missing. It reads the catalogs' own strings, so it checks the params of a
|
|
58
|
+
* catalog from a plain variable or a JSON file too, which the compiler can't. An object in the default catalog counts as
|
|
59
|
+
* a plural only when its every key is a plural category, so a group with an `other` key is a group, and a translation
|
|
60
|
+
* is read by the default catalog's shapes, as the translator reads it: one that keeps only a group's `other` message
|
|
61
|
+
* lacks the group's other messages.
|
|
52
62
|
*
|
|
53
63
|
* @remarks
|
|
54
64
|
* MeoCord's own texts, a catalog's `meocord` group, fall back to English by design, so only a key MeoCord lacks, and
|
|
@@ -95,7 +105,7 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
95
105
|
const categories = new Intl.PluralRules(locale).resolvedOptions().pluralCategories;
|
|
96
106
|
for (const { key, plural } of reference){
|
|
97
107
|
const message = at(catalog, key);
|
|
98
|
-
if (message
|
|
108
|
+
if (isMissing(message)) {
|
|
99
109
|
problems.push(`missing ${key}`);
|
|
100
110
|
continue;
|
|
101
111
|
}
|
|
@@ -113,9 +123,11 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
113
123
|
for (const name of stray)problems.push(`${key} takes no {${name}}; the default is "${shown(original)}"`);
|
|
114
124
|
}
|
|
115
125
|
if (options.meocord && !locale.startsWith('en-')) {
|
|
116
|
-
for (const key of MEOCORD_KEYS)if (
|
|
126
|
+
for (const key of MEOCORD_KEYS)if (isMissing(at(catalog, key))) problems.push(`missing ${key}`);
|
|
117
127
|
}
|
|
118
|
-
for (const { key } of leaves(catalog)){
|
|
128
|
+
for (const { key } of leaves(catalog, '', defaultCatalog)){
|
|
129
|
+
// A message left out, as a null one is, that the default has is reported missing above, and is nothing extra
|
|
130
|
+
if (isMissing(at(catalog, key)) && at(defaultCatalog, key) !== undefined) continue;
|
|
119
131
|
if (isMeoCordKey(key)) {
|
|
120
132
|
if (!MEOCORD_KEYS.has(key)) problems.push(`${key} is not one of MeoCord's texts`);
|
|
121
133
|
else {
|
|
@@ -123,7 +135,11 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
123
135
|
const stray = strayParams(at(catalog, key), english);
|
|
124
136
|
for (const name of stray)problems.push(`${key} takes no {${name}}: MeoCord's English is "${shown(english)}"`);
|
|
125
137
|
}
|
|
126
|
-
} else if (!referenceKeys.has(key))
|
|
138
|
+
} else if (!referenceKeys.has(key)) {
|
|
139
|
+
// A text where the default has a group: the key is the default's, in another shape
|
|
140
|
+
const original = at(defaultCatalog, key);
|
|
141
|
+
problems.push(typeof original === 'object' && original !== null ? `${key} should be a group, as the default is` : `${key} is not in the default catalog`);
|
|
142
|
+
}
|
|
127
143
|
}
|
|
128
144
|
if (problems.length > 0) gaps.push(` ${locale}: ${problems.join('; ')}`);
|
|
129
145
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { isDefaultBatchMethod } from '../common/cooldown-store.js';
|
|
3
3
|
|
|
4
4
|
/** Long enough for a store over the network to answer each case, short enough that the suite takes seconds. */ const CASE_TIMEOUT_MS = 10000;
|
|
5
5
|
const sleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
@@ -408,10 +408,10 @@ const sleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
|
408
408
|
});
|
|
409
409
|
}
|
|
410
410
|
/** Whether a store overrides `consumeMany`, as a store that counts a batch at once does. */ function overridesConsumeMany(store) {
|
|
411
|
-
return store.consumeMany
|
|
411
|
+
return !isDefaultBatchMethod(store.consumeMany);
|
|
412
412
|
}
|
|
413
413
|
/** Whether a store overrides `peekMany`, as a store that can check a batch without recording it does. */ function overridesPeekMany(store) {
|
|
414
|
-
return store.peekMany
|
|
414
|
+
return !isDefaultBatchMethod(store.peekMany);
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
export { testCooldownStore };
|