meocord 4.2.1 → 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 +188 -0
- package/dist/cjs/_shared/{controller.decorator-XKMlcUZz.cjs → controller.decorator-hp5xdJU4.cjs} +550 -189
- package/dist/cjs/_shared/{startup-roots-CgfPQrNl.cjs → event-sources-DzqTODXg.cjs} +274 -88
- package/dist/cjs/_shared/{providers-BZgJyfZS.cjs → providers-BBvbnmLP.cjs} +837 -295
- package/dist/cjs/_shared/{sharded-cooldown-store-CsJJyCZd.cjs → sharded-cooldown-store-B62g5O88.cjs} +11 -9
- package/dist/cjs/_shared/{translate-error-BUAR1WYG.cjs → translate-error-wsgfsUET.cjs} +1 -1
- package/dist/cjs/common/index.cjs +4 -104
- package/dist/cjs/core/index.cjs +435 -182
- package/dist/cjs/decorator/index.cjs +116 -121
- package/dist/cjs/testing/index.cjs +1923 -808
- 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 +32 -10
- package/dist/esm/common/sharded-cooldown-store.js +11 -9
- package/dist/esm/common/theme.js +3 -1
- package/dist/esm/common/translator.js +27 -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 +92 -66
- 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 +6 -6
- 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 +54 -32
- 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 +83 -54
- package/dist/esm/core/meocord.app.js +174 -89
- package/dist/esm/core/message-help.js +37 -15
- 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 +4 -2
- package/dist/esm/core/process-state.js +19 -0
- package/dist/esm/core/providers.js +39 -4
- 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/stage-scope.js +2 -1
- 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 +31 -30
- package/dist/esm/core/theme-validation.js +1 -1
- package/dist/esm/decorator/app.decorator.js +37 -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 +4 -4
- 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 +32 -25
- package/dist/esm/testing/cooldown-store-suite.js +3 -3
- package/dist/esm/testing/discord-defaults.js +449 -335
- package/dist/esm/testing/inspect-handler.js +91 -10
- package/dist/esm/testing/meocord-testing-module.js +217 -91
- package/dist/esm/testing/mock-fn.js +8 -2
- package/dist/esm/testing/mock-interaction.js +864 -124
- 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 +8 -4
- 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 +33 -5
- package/dist/types/common/index.d.cts +17 -10
- package/dist/types/common/index.d.ts +17 -10
- 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 +158 -61
- package/dist/types/testing/index.d.ts +158 -61
- 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
|
@@ -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,34 +1,33 @@
|
|
|
1
|
-
import { CATALOGS, placeholderNames } from '../common/translator.js';
|
|
1
|
+
import { isPluralShape, CATALOGS, placeholderNames } from '../common/translator.js';
|
|
2
2
|
import { MEOCORD_MESSAGES } from '../common/meocord-messages.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'many',
|
|
10
|
-
'other'
|
|
11
|
-
]);
|
|
12
|
-
/** A plural: an object whose every key is a plural category, as the translator's types read one; any other is a group. */ const isPlural = (value)=>typeof value === 'object' && value !== null && Object.keys(value).length > 0 && Object.keys(value).every((key)=>PLURAL_CATEGORIES.has(key));
|
|
13
|
-
/** Every message key of a catalog, and whether it is a plural. */ function leaves(catalog, prefix = '') {
|
|
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) {
|
|
14
9
|
return Object.entries(catalog).flatMap(([name, value])=>{
|
|
15
10
|
const key = `${prefix}${name}`;
|
|
16
|
-
|
|
11
|
+
// A null message is one where it stands, which the checks below read as missing
|
|
12
|
+
if (typeof value === 'string' || value === null) return [
|
|
17
13
|
{
|
|
18
14
|
key,
|
|
19
15
|
plural: false
|
|
20
16
|
}
|
|
21
17
|
];
|
|
22
|
-
|
|
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 [
|
|
23
21
|
{
|
|
24
22
|
key,
|
|
25
23
|
plural: true
|
|
26
24
|
}
|
|
27
25
|
];
|
|
28
|
-
return leaves(value, `${key}
|
|
26
|
+
return leaves(value, `${key}.`, shape);
|
|
29
27
|
});
|
|
30
28
|
}
|
|
31
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 === '';
|
|
32
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) {
|
|
33
32
|
return key.split('.').reduce((current, part)=>typeof current === 'object' && current !== null && Object.hasOwn(current, part) ? current[part] : undefined, catalog);
|
|
34
33
|
}
|
|
@@ -43,7 +42,7 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
43
42
|
/** A message as a report shows it: the text, or a plural's `other` form. */ const shown = (message)=>typeof message === 'string' ? message : String(message.other);
|
|
44
43
|
/** The `{params}` a translation uses that the message it translates doesn't take; a plural's `{count}` is taken. */ function strayParams(translation, original) {
|
|
45
44
|
const taken = placeholders(original);
|
|
46
|
-
if (
|
|
45
|
+
if (isPluralShape(original)) taken.add('count');
|
|
47
46
|
return [
|
|
48
47
|
...placeholders(translation)
|
|
49
48
|
].filter((name)=>!taken.has(name));
|
|
@@ -53,11 +52,13 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
53
52
|
*
|
|
54
53
|
* Use it in a test for a team that wants no fallback in production. It reports the messages a locale lacks, the
|
|
55
54
|
* messages the default catalog doesn't have, the plural forms a language needs but a plural lacks, such as `few` for
|
|
56
|
-
* Russian, a translation of another shape than the default's, a text for a plural or a plural for a text, and each
|
|
57
|
-
* `{param}` a translation uses that the message it translates doesn't take. An empty translation, `''`, counts
|
|
58
|
-
* lacking, as the translator reads it as missing. It reads the catalogs' own strings, so it checks the params of a
|
|
59
|
-
* catalog from a plain variable or a JSON file too, which the compiler can't. An object
|
|
60
|
-
* 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.
|
|
61
62
|
*
|
|
62
63
|
* @remarks
|
|
63
64
|
* MeoCord's own texts, a catalog's `meocord` group, fall back to English by design, so only a key MeoCord lacks, and
|
|
@@ -104,12 +105,12 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
104
105
|
const categories = new Intl.PluralRules(locale).resolvedOptions().pluralCategories;
|
|
105
106
|
for (const { key, plural } of reference){
|
|
106
107
|
const message = at(catalog, key);
|
|
107
|
-
if (message
|
|
108
|
+
if (isMissing(message)) {
|
|
108
109
|
problems.push(`missing ${key}`);
|
|
109
110
|
continue;
|
|
110
111
|
}
|
|
111
112
|
// A translation of another shape than the default's can't be read as it: a text for a plural, or the reverse
|
|
112
|
-
if (plural !==
|
|
113
|
+
if (plural !== isPluralShape(message) || !plural && typeof message !== 'string') {
|
|
113
114
|
problems.push(`${key} should be a ${plural ? 'plural' : 'text'}, as the default is`);
|
|
114
115
|
continue;
|
|
115
116
|
}
|
|
@@ -122,9 +123,11 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
122
123
|
for (const name of stray)problems.push(`${key} takes no {${name}}; the default is "${shown(original)}"`);
|
|
123
124
|
}
|
|
124
125
|
if (options.meocord && !locale.startsWith('en-')) {
|
|
125
|
-
for (const key of MEOCORD_KEYS)if (
|
|
126
|
+
for (const key of MEOCORD_KEYS)if (isMissing(at(catalog, key))) problems.push(`missing ${key}`);
|
|
126
127
|
}
|
|
127
|
-
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;
|
|
128
131
|
if (isMeoCordKey(key)) {
|
|
129
132
|
if (!MEOCORD_KEYS.has(key)) problems.push(`${key} is not one of MeoCord's texts`);
|
|
130
133
|
else {
|
|
@@ -132,7 +135,11 @@ const isMeoCordKey = (key)=>key.startsWith('meocord.');
|
|
|
132
135
|
const stray = strayParams(at(catalog, key), english);
|
|
133
136
|
for (const name of stray)problems.push(`${key} takes no {${name}}: MeoCord's English is "${shown(english)}"`);
|
|
134
137
|
}
|
|
135
|
-
} 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
|
+
}
|
|
136
143
|
}
|
|
137
144
|
if (problems.length > 0) gaps.push(` ${locale}: ${problems.join('; ')}`);
|
|
138
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 };
|