meocord 4.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +104 -0
- package/README.md +23 -23
- package/dist/cjs/_shared/{controller.decorator-BywwkU9O.cjs → controller.decorator-CRn9kWob.cjs} +295 -87
- package/dist/cjs/_shared/{providers-BMMLp2SN.cjs → providers-ChvMqnLd.cjs} +74 -40
- package/dist/cjs/_shared/{sharded-cooldown-store-B65iaiAy.cjs → sharded-cooldown-store-BdAUTdYs.cjs} +1 -1
- package/dist/cjs/_shared/{startup-roots-lFc69AxC.cjs → startup-roots-CQo8mSP8.cjs} +158 -72
- package/dist/cjs/_shared/{translate-error-BnM8FbSH.cjs → translate-error-DhDVbT5M.cjs} +12 -3
- package/dist/cjs/common/index.cjs +4 -4
- package/dist/cjs/core/index.cjs +195 -157
- package/dist/cjs/decorator/index.cjs +66 -57
- package/dist/cjs/testing/index.cjs +736 -307
- package/dist/cli.json +1 -1
- package/dist/esm/bin/app-template/README.md.template +3 -1
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template +8 -4
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template +2 -2
- package/dist/esm/bin/app-template/src/guards/owner.guard.spec.ts.template +8 -4
- package/dist/esm/bin/app-template/src/guards/owner.guard.ts.template +1 -1
- package/dist/esm/bin/app-template/vitest.config.ts.template +1 -1
- package/dist/esm/bin/app-template/vitest.setup.ts.template +7 -3
- package/dist/esm/build/load-config.pre-entry.js +4 -1
- package/dist/esm/common/decorator.js +6 -6
- package/dist/esm/common/deprecation.js +2 -2
- package/dist/esm/common/errors.js +2 -2
- package/dist/esm/common/execution-context.js +1 -1
- package/dist/esm/common/metadata.js +1 -1
- package/dist/esm/common/route.js +10 -1
- package/dist/esm/common/translate-error.js +1 -1
- package/dist/esm/core/command-conflicts.js +34 -34
- package/dist/esm/core/component-routes.js +74 -31
- package/dist/esm/core/dispatcher.js +51 -7
- package/dist/esm/core/filter-runner.js +2 -2
- package/dist/esm/core/handler-pipeline.js +27 -14
- package/dist/esm/core/handler-registry.js +1 -1
- package/dist/esm/core/interceptor-runner.js +2 -2
- package/dist/esm/core/meocord-factory.js +196 -157
- package/dist/esm/core/message-routes.js +4 -3
- package/dist/esm/core/observer-runner.js +4 -3
- package/dist/esm/core/providers.js +38 -21
- package/dist/esm/core/route-specificity.js +4 -4
- package/dist/esm/core/scalar-types.js +15 -3
- package/dist/esm/core/shard-context.js +1 -1
- package/dist/esm/core/startup-roots.js +2 -3
- package/dist/esm/core/theme-runtime.js +2 -2
- package/dist/esm/decorator/app.decorator.js +12 -7
- package/dist/esm/decorator/command-builder.decorator.js +4 -4
- package/dist/esm/decorator/controller-class.decorator.js +9 -4
- package/dist/esm/decorator/controller.decorator.js +110 -67
- package/dist/esm/decorator/cooldown.decorator.js +3 -3
- package/dist/esm/decorator/defer.decorator.js +4 -4
- package/dist/esm/decorator/event.decorator.js +5 -5
- package/dist/esm/decorator/filter.decorator.js +7 -7
- package/dist/esm/decorator/guard.decorator.js +8 -8
- package/dist/esm/decorator/inject.decorator.js +4 -4
- package/dist/esm/decorator/interceptor.decorator.js +7 -7
- package/dist/esm/decorator/observer.decorator.js +4 -4
- package/dist/esm/decorator/service.decorator.js +4 -4
- package/dist/esm/decorator/theme.decorator.js +3 -3
- package/dist/esm/decorator/validation.decorator.js +10 -10
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/catalog.js +1 -1
- package/dist/esm/testing/cooldown-store-suite.js +18 -10
- package/dist/esm/testing/discord-defaults.js +60 -7
- package/dist/esm/testing/index.js +4 -2
- package/dist/esm/testing/inspect-handler.js +20 -3
- package/dist/esm/testing/meocord-testing-module.js +229 -188
- package/dist/esm/testing/mock-fn.js +80 -10
- package/dist/esm/testing/mock-interaction.js +226 -78
- package/dist/esm/testing/response.js +1 -1
- package/dist/esm/testing/routing.js +39 -14
- package/dist/esm/testing/startup-errors.js +26 -0
- package/dist/esm/testing/strict-mocks.js +45 -0
- package/dist/esm/testing/theme.js +1 -1
- package/dist/esm/util/bundle-entry.util.js +2 -1
- package/dist/esm/util/interaction.util.js +1 -1
- package/dist/esm/util/meocord-config-validation.util.js +1 -0
- package/dist/esm/util/metadata-keys.js +1 -0
- package/dist/esm/util/refusal.util.js +148 -8
- package/dist/types/common/index.d.cts +8 -8
- package/dist/types/common/index.d.ts +8 -8
- package/dist/types/core/index.d.cts +8 -6
- package/dist/types/core/index.d.ts +8 -6
- package/dist/types/decorator/index.d.cts +30 -29
- package/dist/types/decorator/index.d.ts +30 -29
- package/dist/types/{index-D7n0FBMm.d.ts → index-CdXsFgjE.d.ts} +62 -15
- package/dist/types/{index-DEP0NYrU.d.cts → index-nn5p7OoX.d.cts} +62 -15
- package/dist/types/interface/index.d.cts +1 -1
- package/dist/types/interface/index.d.ts +1 -1
- package/dist/types/{route-C-aMI7Sl.d.cts → route-BZgFoNeB.d.cts} +5 -2
- package/dist/types/{route-C-aMI7Sl.d.ts → route-BZgFoNeB.d.ts} +5 -2
- package/dist/types/testing/index.d.cts +245 -31
- package/dist/types/testing/index.d.ts +245 -31
- package/dist/types/{theme-resolvers-DedxyscT.d.ts → theme-resolvers-BcOJVzR9.d.ts} +1 -1
- package/dist/types/{theme-resolvers-COHdX5g6.d.cts → theme-resolvers-UKr3-6Ys.d.cts} +1 -1
- package/dist/types/{translator-J6tIUfdp.d.ts → translator-BViWKtLJ.d.ts} +1 -1
- package/dist/types/{translator-B4mc1lzJ.d.cts → translator-DHMkkCpg.d.cts} +1 -1
- package/package.json +3 -2
package/dist/cjs/_shared/{controller.decorator-BywwkU9O.cjs → controller.decorator-CRn9kWob.cjs}
RENAMED
|
@@ -27,6 +27,7 @@ var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
|
|
|
27
27
|
|
|
28
28
|
/** Where the built bundle's pre-entry records the bundle's own path. */ const BUNDLE_ENTRY_KEY = Symbol.for('meocord.bundleEntry');
|
|
29
29
|
/** Where the built bundle's pre-entry records the mode the bundle was built in. */ const BUILD_MODE_KEY = Symbol.for('meocord.buildMode');
|
|
30
|
+
/** Where the built bot's pre-entry records `startupErrors` from its config, before the application's modules load. */ const STARTUP_ERRORS_KEY = Symbol.for('meocord.startupErrorsMode');
|
|
30
31
|
/**
|
|
31
32
|
* The file a shard process runs: the built bundle, as its pre-entry recorded it, else the script this
|
|
32
33
|
* process was started with; undefined when there is none, as for code run with `node -e` outside a built bundle.
|
|
@@ -119,7 +120,7 @@ let problem;
|
|
|
119
120
|
try {
|
|
120
121
|
// Called through a variable so a bundler does not try to resolve the path while building the
|
|
121
122
|
// application; the file is only there once the build is done.
|
|
122
|
-
const load = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/controller.decorator-
|
|
123
|
+
const load = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/controller.decorator-CRn9kWob.cjs', document.baseURI).href)));
|
|
123
124
|
const loaded = load(compiledPath);
|
|
124
125
|
return loaded.default ?? loaded;
|
|
125
126
|
} catch (error) {
|
|
@@ -352,7 +353,7 @@ const warned = new Set();
|
|
|
352
353
|
*/ function warnPlaceholder(logger, value, outcome, how) {
|
|
353
354
|
warnOnce(logger, `${value} reads a placeholder here; in the next major version (5.0) ${outcome}. ${how}`);
|
|
354
355
|
}
|
|
355
|
-
function warnOnce(logger, text) {
|
|
356
|
+
/** Logs `text` once per run, however often it is asked for. */ function warnOnce(logger, text) {
|
|
356
357
|
if (warned.has(text)) return;
|
|
357
358
|
warned.add(text);
|
|
358
359
|
logger.warn(text);
|
|
@@ -692,10 +693,10 @@ const REFUSAL = Symbol.for('meocord.refusal');
|
|
|
692
693
|
// A stack frame's function and file: `at fn (file:line:col)` or `at file:line:col`, the file possibly a file:// URL
|
|
693
694
|
const FRAME = /^\s*at (?:(.*?) \()?(.+?):\d+:\d+\)?$/;
|
|
694
695
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
*/ function
|
|
696
|
+
* The first file of the application's own source an error's stack passes through, other than its entry, which only
|
|
697
|
+
* loads the rest, relative to `root`. The file alone: a line from a development build's source map can be off, and a
|
|
698
|
+
* compiler's decorate helper can be placed in any file, so those frames are skipped.
|
|
699
|
+
*/ function sourceFileOf(error, root, windows = process.platform === 'win32') {
|
|
699
700
|
const paths = windows ? path__default.default.win32 : path__default.default.posix;
|
|
700
701
|
const source = comparablePath(paths.join(root, 'src') + paths.sep, windows);
|
|
701
702
|
const entry = comparablePath(paths.join(root, 'src', 'main.ts'), windows);
|
|
@@ -706,9 +707,149 @@ const FRAME = /^\s*at (?:(.*?) \()?(.+?):\d+:\d+\)?$/;
|
|
|
706
707
|
const file = paths.normalize(framePath(name, windows));
|
|
707
708
|
const compared = comparablePath(file, windows);
|
|
708
709
|
if (!compared.startsWith(source) || compared === entry || compared.includes(dependencies)) continue;
|
|
709
|
-
return
|
|
710
|
+
return paths.relative(root, file).split(paths.sep).join('/');
|
|
710
711
|
}
|
|
711
|
-
return
|
|
712
|
+
return undefined;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* A refusal as the application reports it: the message, led by the declaration it is about where the message does not
|
|
716
|
+
* name it, and the file of the application's source it comes from; see {@link sourceFileOf}.
|
|
717
|
+
*/ function describeRefusal(error, root, windows = process.platform === 'win32') {
|
|
718
|
+
const declaration = declarationOf(error);
|
|
719
|
+
const message = declaration && !error.message.startsWith(declaration) ? `${declaration}: ${error.message}` : error.message;
|
|
720
|
+
const file = sourceFileOf(error, root, windows);
|
|
721
|
+
return file ? `${message}\n in ${file}` : message;
|
|
722
|
+
}
|
|
723
|
+
// ---------------------------------------------------------------------------
|
|
724
|
+
// Startup errors: those create() and a testing module's compile() find, and those decorators find at import
|
|
725
|
+
// ---------------------------------------------------------------------------
|
|
726
|
+
const STARTUP_ERRORS = Symbol.for('meocord.startupErrors');
|
|
727
|
+
const DECLARED = Symbol.for('meocord.declaredStartupErrors');
|
|
728
|
+
const ALL_DECLARED = Symbol.for('meocord.allDeclaredStartupErrors');
|
|
729
|
+
// The startup errors kept while checks run under collectStartupErrors; undefined outside them
|
|
730
|
+
let kept;
|
|
731
|
+
/**
|
|
732
|
+
* Reports an error a startup check finds. Outside {@link collectStartupErrors} it is thrown, as a refusal; inside, it
|
|
733
|
+
* is kept, and the check goes on to find the rest.
|
|
734
|
+
*/ function startupError(error) {
|
|
735
|
+
refuse(error);
|
|
736
|
+
if (!kept) throw error;
|
|
737
|
+
// Two checks of one mistake, such as a class needing a translator, name it once
|
|
738
|
+
if (!kept.some((other)=>other.message === error.message)) kept.push(error);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Runs startup checks, and once they are done throws the first error they found, unchanged, with every one found on it
|
|
742
|
+
* for {@link startupErrorsOf}. An error that ends the checks after one was found is left out, as one the found error
|
|
743
|
+
* caused; one that ends them before any was found is thrown as it is.
|
|
744
|
+
*/ function collectStartupErrors(checks) {
|
|
745
|
+
const outer = kept;
|
|
746
|
+
const found = [];
|
|
747
|
+
kept = found;
|
|
748
|
+
let result;
|
|
749
|
+
try {
|
|
750
|
+
result = checks();
|
|
751
|
+
} catch (error) {
|
|
752
|
+
if (found.length === 0) throw error;
|
|
753
|
+
if (isRefusal(error) && !found.includes(error)) found.push(error);
|
|
754
|
+
throw firstOf(found);
|
|
755
|
+
} finally{
|
|
756
|
+
kept = outer;
|
|
757
|
+
}
|
|
758
|
+
if (found.length > 0) throw firstOf(found);
|
|
759
|
+
return result;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Ends the checks under way with the errors found so far, before a step that is no check, such as binding or a warning:
|
|
763
|
+
* a run with one error stops where it stopped when the first error was thrown, and the errors of one pass of the
|
|
764
|
+
* checks are still reported together.
|
|
765
|
+
*/ function stopOnStartupErrors() {
|
|
766
|
+
if (kept && kept.length > 0) throw kept[0];
|
|
767
|
+
}
|
|
768
|
+
function firstOf(errors) {
|
|
769
|
+
const [first] = errors;
|
|
770
|
+
Object.defineProperty(first, STARTUP_ERRORS, {
|
|
771
|
+
value: errors,
|
|
772
|
+
configurable: true
|
|
773
|
+
});
|
|
774
|
+
return first;
|
|
775
|
+
}
|
|
776
|
+
/** Every startup error found together with `error`, the first of them; `error` alone when it was the only one. */ function startupErrorsOf(error) {
|
|
777
|
+
return error[STARTUP_ERRORS] ?? [
|
|
778
|
+
error
|
|
779
|
+
];
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Logs each startup error found with `error` as the application reports a refusal, and how many there were, when there
|
|
783
|
+
* was more than one; a lone error is left to whoever reports it now.
|
|
784
|
+
*/ function logStartupErrors(error, log, ending) {
|
|
785
|
+
const errors = startupErrorsOf(error);
|
|
786
|
+
if (errors.length < 2) return false;
|
|
787
|
+
for (const each of errors)log(describeRefusal(each, process.cwd()));
|
|
788
|
+
log(`MeoCord found ${errors.length} startup errors; ${ending}.`);
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
/** Sets how decorators report startup errors from here on, before the application's modules load. */ function setStartupErrorsMode(mode) {
|
|
792
|
+
globalThis[STARTUP_ERRORS_KEY] = mode;
|
|
793
|
+
}
|
|
794
|
+
/** Whether decorators keep their startup errors for create(), as `startupErrors: 'all'` has them. */ const keepsDeclaredErrors = ()=>globalThis[STARTUP_ERRORS_KEY] === 'all';
|
|
795
|
+
/** The declaration a decorator's startup error is about, `Class.method` or `Class`. */ function declarationOf(error) {
|
|
796
|
+
return error.declaration;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* A decorator that names, on each startup error it throws, the declaration it was applied to, `Class.method` or
|
|
800
|
+
* `Class`, as `declaration`, and the source file it is declared in, as `file`: the same error, thrown at the same
|
|
801
|
+
* moment, with its message unchanged. With `startupErrors: 'all'` the error is kept on the class instead, for create()
|
|
802
|
+
* to report with the rest, and the decorator applies nothing more.
|
|
803
|
+
*/ function declaring(decorate) {
|
|
804
|
+
return function(target, propertyKey, ...rest) {
|
|
805
|
+
try {
|
|
806
|
+
return decorate.call(this, target, propertyKey, ...rest);
|
|
807
|
+
} catch (error) {
|
|
808
|
+
if (!isRefusal(error)) throw error;
|
|
809
|
+
keepOrThrow(error, target, propertyKey);
|
|
810
|
+
return undefined;
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function keepOrThrow(error, target, propertyKey) {
|
|
815
|
+
var _globalThis, _ALL_DECLARED;
|
|
816
|
+
// A decorator applied through another, such as applyDecorators, names the declaration once, where it was first thrown
|
|
817
|
+
if (declarationOf(error) === undefined) {
|
|
818
|
+
Object.defineProperty(error, 'declaration', {
|
|
819
|
+
value: decoratedName(target, propertyKey),
|
|
820
|
+
configurable: true
|
|
821
|
+
});
|
|
822
|
+
Object.defineProperty(error, 'file', {
|
|
823
|
+
get: ()=>sourceFileOf(error, process.cwd()),
|
|
824
|
+
configurable: true
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
if (!keepsDeclaredErrors()) throw error;
|
|
828
|
+
const owner = typeof target === 'function' ? target : target.constructor;
|
|
829
|
+
if (!Object.prototype.hasOwnProperty.call(owner, DECLARED)) Object.defineProperty(owner, DECLARED, {
|
|
830
|
+
value: []
|
|
831
|
+
});
|
|
832
|
+
const declared = owner[DECLARED];
|
|
833
|
+
if (!declared.includes(error)) declared.push(error);
|
|
834
|
+
const all = (_globalThis = globalThis)[_ALL_DECLARED = ALL_DECLARED] ?? (_globalThis[_ALL_DECLARED] = []);
|
|
835
|
+
if (!all.includes(error)) all.push(error);
|
|
836
|
+
}
|
|
837
|
+
/** The startup errors decorators kept on `classes` and the classes they extend, each once, in the order they were found. */ function declaredErrorsOf(classes) {
|
|
838
|
+
const found = new Set();
|
|
839
|
+
for (const cls of classes){
|
|
840
|
+
for(let current = cls; typeof current === 'function' && current !== Function.prototype; current = Object.getPrototypeOf(current)){
|
|
841
|
+
if (Object.prototype.hasOwnProperty.call(current, DECLARED)) for (const error of current[DECLARED])found.add(error);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
const order = allDeclaredErrors();
|
|
845
|
+
return [
|
|
846
|
+
...found
|
|
847
|
+
].sort((a, b)=>order.indexOf(a) - order.indexOf(b));
|
|
848
|
+
}
|
|
849
|
+
/** Every startup error decorators kept in this process, in the order they were found. */ function allDeclaredErrors() {
|
|
850
|
+
return [
|
|
851
|
+
...globalThis[ALL_DECLARED] ?? []
|
|
852
|
+
];
|
|
712
853
|
}
|
|
713
854
|
/**
|
|
714
855
|
* Reports a refusal nothing catches as one line, with its source file, and exits 1; other errors keep the runtime's
|
|
@@ -902,9 +1043,9 @@ const PLAIN = "give a plain object, such as { ...value }, or a row's toObject(),
|
|
|
902
1043
|
*
|
|
903
1044
|
* @group Utilities
|
|
904
1045
|
* @see {@link createMetadata}
|
|
905
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
1046
|
+
* @see {@link https://meocord.dev/docs/4.2/custom-decorators | Custom decorators}
|
|
906
1047
|
*/ function applyDecorators(...decorators) {
|
|
907
|
-
return function(target, propertyKey, descriptor) {
|
|
1048
|
+
return declaring(function(target, propertyKey, descriptor) {
|
|
908
1049
|
// Last first, as TypeScript applies decorators stacked, each given what the one before it returned
|
|
909
1050
|
const ordered = [
|
|
910
1051
|
...decorators
|
|
@@ -917,7 +1058,7 @@ const PLAIN = "give a plain object, such as { ...value }, or a row's toObject(),
|
|
|
917
1058
|
let current = target;
|
|
918
1059
|
for (const decorator of ordered)current = decorator(current) ?? current;
|
|
919
1060
|
return current;
|
|
920
|
-
};
|
|
1061
|
+
});
|
|
921
1062
|
}
|
|
922
1063
|
const logger$5 = new Logger('SetMetadata');
|
|
923
1064
|
/** The prefix of every key MeoCord keeps its own metadata under. */ const MEOCORD_PREFIX = 'meocord:';
|
|
@@ -956,7 +1097,7 @@ const logger$5 = new Logger('SetMetadata');
|
|
|
956
1097
|
* @see {@link createMetadata}
|
|
957
1098
|
*/ function SetMetadata(metadataKey, metadataValue) {
|
|
958
1099
|
warnDeprecated(logger$5, 'SetMetadata', 'createMetadata');
|
|
959
|
-
return function(target, propertyKey) {
|
|
1100
|
+
return declaring(function(target, propertyKey) {
|
|
960
1101
|
// Checked where it applies, so the refusal names the handler or controller
|
|
961
1102
|
const owner = metadataKey.startsWith(MEOCORD_PREFIX) ? `MeoCord keeps its own metadata under keys beginning "${MEOCORD_PREFIX}"` : INJECTION_KEYS.has(metadataKey) ? 'dependency injection reads how to make the class from it' : undefined;
|
|
962
1103
|
if (owner) {
|
|
@@ -967,7 +1108,7 @@ const logger$5 = new Logger('SetMetadata');
|
|
|
967
1108
|
} else {
|
|
968
1109
|
Reflect.defineMetadata(metadataKey, metadataValue, target);
|
|
969
1110
|
}
|
|
970
|
-
};
|
|
1111
|
+
});
|
|
971
1112
|
}
|
|
972
1113
|
|
|
973
1114
|
/**
|
|
@@ -1456,7 +1597,7 @@ const issueTexts = new WeakMap();
|
|
|
1456
1597
|
* @group Responses
|
|
1457
1598
|
* @category Errors
|
|
1458
1599
|
* @see {@link Catch}
|
|
1459
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
1600
|
+
* @see {@link https://meocord.dev/docs/4.2/exception-filters | Exception filters}
|
|
1460
1601
|
*/ class CommandNotFoundError extends Error {
|
|
1461
1602
|
/** @param message - What the error says, `No handler matched the interaction.` unless given. */ constructor(message = 'No handler matched the interaction.'){
|
|
1462
1603
|
super(message);
|
|
@@ -1529,7 +1670,7 @@ const issueTexts = new WeakMap();
|
|
|
1529
1670
|
* @category Errors
|
|
1530
1671
|
* @see {@link MessageHandler}
|
|
1531
1672
|
* @see {@link translateError}
|
|
1532
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
1673
|
+
* @see {@link https://meocord.dev/docs/4.2/message-commands | Message commands}
|
|
1533
1674
|
*/ class MessageUsageError extends Error {
|
|
1534
1675
|
/**
|
|
1535
1676
|
* @param usage - The command as the user should type it, such as `!ban <target> [reason…]`, or one line per
|
|
@@ -3700,7 +3841,7 @@ const states = new WeakMap();
|
|
|
3700
3841
|
* @group Utilities
|
|
3701
3842
|
* @see {@link createMetadata}
|
|
3702
3843
|
* @see {@link StageParams}
|
|
3703
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
3844
|
+
* @see {@link https://meocord.dev/docs/4.2/guards | Guards}
|
|
3704
3845
|
*/ class ExecutionContext {
|
|
3705
3846
|
/**
|
|
3706
3847
|
* The response state of the interaction being handled, the same one `respond(interaction)` returns,
|
|
@@ -4399,10 +4540,10 @@ const messageOf = (error)=>String(error?.message ?? error);
|
|
|
4399
4540
|
}
|
|
4400
4541
|
|
|
4401
4542
|
/**
|
|
4402
|
-
* How specific a route pattern is, for ranking the patterns that can match one
|
|
4403
|
-
*
|
|
4404
|
-
* fewer params, then narrower typed params, whose
|
|
4405
|
-
* characters
|
|
4543
|
+
* How specific a route pattern is, for ranking the message patterns that can match one message: more literal
|
|
4544
|
+
* words first, then a fixed length before one taking the rest, then no optional param before one, then
|
|
4545
|
+
* fewer params, then narrower typed params, whose words take fewer values. Component patterns count literal
|
|
4546
|
+
* characters, for CommandMeta.specificity alone.
|
|
4406
4547
|
*/ function routeSpecificity({ literals, params, rest = false, optional = false, typed = 0 }) {
|
|
4407
4548
|
return literals * 1000 - (rest ? 500 : 0) - (optional ? 250 : 0) - params + typed / 1000;
|
|
4408
4549
|
}
|
|
@@ -4443,14 +4584,26 @@ const UNIT_MS = lookupTable({
|
|
|
4443
4584
|
return total + Number(amount) * UNIT_MS[unit];
|
|
4444
4585
|
}, 0);
|
|
4445
4586
|
}
|
|
4587
|
+
/** The largest snowflake: Discord's IDs are unsigned 64-bit integers. */ const MAX_SNOWFLAKE = 2n ** 64n - 1n;
|
|
4588
|
+
/**
|
|
4589
|
+
* A Discord ID, kept as its text: 17 to 20 digits, no leading zero, up to the largest 64-bit value. An ID's top 42 bits
|
|
4590
|
+
* are milliseconds since 2015-01-01, so every ID made from 2015-01-28 on has 17 digits or more, and none is a safe
|
|
4591
|
+
* integer.
|
|
4592
|
+
*/ const snowflake = (word)=>/^[1-9]\d{16,19}$/.test(word) && BigInt(word) <= MAX_SNOWFLAKE ? word : undefined;
|
|
4593
|
+
/** A UUID in its canonical 8-4-4-4-12 form, in either case, kept as written. */ const uuid = (word)=>/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/i.test(word) ? word : undefined;
|
|
4446
4594
|
/** The words a type such as `on|off` chooses from, or `undefined` for any other type. */ const choicesOf = (type)=>type.includes('|') ? type.split('|') : undefined;
|
|
4447
4595
|
/** The types a component's customId segment can take: each parsed from the segment alone, with no request. */ const SEGMENT_TYPES = lookupTable({
|
|
4448
4596
|
string: (segment)=>segment,
|
|
4449
4597
|
int: (segment)=>number(segment, true),
|
|
4450
4598
|
number: (segment)=>number(segment, false),
|
|
4451
|
-
bool
|
|
4599
|
+
bool,
|
|
4600
|
+
snowflake,
|
|
4601
|
+
uuid
|
|
4452
4602
|
});
|
|
4453
|
-
/**
|
|
4603
|
+
/**
|
|
4604
|
+
* Whether `type` can type a customId segment: `string`, `int`, `number`, `bool`, `snowflake`, `uuid`, or words to
|
|
4605
|
+
* choose from.
|
|
4606
|
+
*/ const isSegmentType = (type)=>type in SEGMENT_TYPES || choicesOf(type) !== undefined;
|
|
4454
4607
|
/** A customId segment as its type's value, words to choose from compared as written: `undefined` when it is not one. */ function parseSegment(type, segment) {
|
|
4455
4608
|
const choices = choicesOf(type);
|
|
4456
4609
|
if (choices) return choices.includes(segment) ? segment : undefined;
|
|
@@ -4481,6 +4634,7 @@ const UNIT_MS = lookupTable({
|
|
|
4481
4634
|
/** How many guard wrappers `@UseGuard` and `@Controller` put around one method. */ guardWrappers: 'meocord:guard-wrappers',
|
|
4482
4635
|
/** The prototype whose method a class-level `@UseGuard` re-declared on a subclass. */ inheritedFrom: 'meocord:inherited-from',
|
|
4483
4636
|
/** `false` when `@Controller({ inheritStages: false })` stops class stages at this class. */ inheritStages: 'meocord:inherit-stages',
|
|
4637
|
+
/** `true` when `@Controller({ inheritedRoutes: 'replace' })` drops the routes this class's re-decorated methods inherit. */ replacesInheritedRoutes: 'meocord:replaces-inherited-routes',
|
|
4484
4638
|
/** A controller's class-level `@Cooldown`s. */ classCooldowns: 'meocord:class-cooldowns',
|
|
4485
4639
|
/** A method's `@Cooldown`s, in declaration order. */ methodCooldowns: 'meocord:method-cooldowns',
|
|
4486
4640
|
/** The error types a filter's `@Catch` names; empty to catch everything. */ catchTypes: 'meocord:catch-types',
|
|
@@ -4555,6 +4709,42 @@ const autocompleteRoute = (method, commandPath, optionName)=>({
|
|
|
4555
4709
|
else handlers[index] = entry;
|
|
4556
4710
|
Reflect.defineMetadata(key, handlers, target);
|
|
4557
4711
|
}
|
|
4712
|
+
/**
|
|
4713
|
+
* Leaves each method the class's own decorators declare only those routes, of every kind, dropping the ones it inherits
|
|
4714
|
+
* for it: what `@Controller({ inheritedRoutes: 'replace' })` does once the class's method decorators have run.
|
|
4715
|
+
*/ function dropInheritedRoutes(prototype) {
|
|
4716
|
+
const redeclared = new Set(getDeclaredRoutes(prototype).map((route)=>route.method));
|
|
4717
|
+
if (redeclared.size === 0) return;
|
|
4718
|
+
const base = Object.getPrototypeOf(prototype);
|
|
4719
|
+
// An entry the class takes from its base, for a method it re-declares
|
|
4720
|
+
const inheritedFor = (inherited, method)=>(entry)=>inherited.includes(entry) && redeclared.has(method(entry));
|
|
4721
|
+
const commands = ownCommandMap(prototype);
|
|
4722
|
+
const baseCommands = Reflect.getMetadata(META.commands, base) ?? {};
|
|
4723
|
+
for (const [name, metas] of Object.entries(commands)){
|
|
4724
|
+
const kept = metas.filter((meta)=>!inheritedFor(baseCommands[name] ?? [], (entry)=>entry.methodName)(meta));
|
|
4725
|
+
if (kept.length > 0) commands[name] = kept;
|
|
4726
|
+
else delete commands[name];
|
|
4727
|
+
}
|
|
4728
|
+
Reflect.defineMetadata(META.commands, commands, prototype);
|
|
4729
|
+
const lists = [
|
|
4730
|
+
[
|
|
4731
|
+
META.messageHandlers,
|
|
4732
|
+
(entry)=>entry.method
|
|
4733
|
+
],
|
|
4734
|
+
[
|
|
4735
|
+
META.reactionHandlers,
|
|
4736
|
+
(entry)=>entry.method
|
|
4737
|
+
],
|
|
4738
|
+
[
|
|
4739
|
+
META.autocompleteHandlers,
|
|
4740
|
+
(entry)=>entry.methodName
|
|
4741
|
+
]
|
|
4742
|
+
];
|
|
4743
|
+
for (const [key, method] of lists){
|
|
4744
|
+
const inherited = Reflect.getMetadata(key, base) ?? [];
|
|
4745
|
+
Reflect.defineMetadata(key, ownHandlerList(key, prototype).filter((entry)=>!inheritedFor(inherited, method)(entry)), prototype);
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4558
4748
|
/** The class's own command map, started from a copy of the inherited one, for the same reason. */ function ownCommandMap(target) {
|
|
4559
4749
|
const own = Reflect.getOwnMetadata(META.commands, target);
|
|
4560
4750
|
if (own) return own;
|
|
@@ -4567,7 +4757,7 @@ const autocompleteRoute = (method, commandPath, optionName)=>({
|
|
|
4567
4757
|
]));
|
|
4568
4758
|
}
|
|
4569
4759
|
function MessageHandler(pattern, options = {}) {
|
|
4570
|
-
return function(target, propertyKey) {
|
|
4760
|
+
return declaring(function(target, propertyKey) {
|
|
4571
4761
|
refuseOnClass('@MessageHandler', target, propertyKey);
|
|
4572
4762
|
// An empty pattern means every message, as no pattern does
|
|
4573
4763
|
if (pattern === '') {
|
|
@@ -4581,7 +4771,7 @@ function MessageHandler(pattern, options = {}) {
|
|
|
4581
4771
|
};
|
|
4582
4772
|
addOwnHandler(META.messageHandlers, target, declared, (other)=>other.method === method && other.pattern === declared.pattern);
|
|
4583
4773
|
declareRoute(target, messageRoute(method, declared.pattern));
|
|
4584
|
-
};
|
|
4774
|
+
});
|
|
4585
4775
|
}
|
|
4586
4776
|
function ReactionHandler(emojiOrSettings, settings = {}) {
|
|
4587
4777
|
const [emoji, own] = typeof emojiOrSettings === 'object' ? [
|
|
@@ -4591,7 +4781,7 @@ function ReactionHandler(emojiOrSettings, settings = {}) {
|
|
|
4591
4781
|
emojiOrSettings,
|
|
4592
4782
|
settings
|
|
4593
4783
|
];
|
|
4594
|
-
return function(target, propertyKey) {
|
|
4784
|
+
return declaring(function(target, propertyKey) {
|
|
4595
4785
|
refuseOnClass('@ReactionHandler', target, propertyKey);
|
|
4596
4786
|
const method = propertyKey.toString();
|
|
4597
4787
|
addOwnHandler(META.reactionHandlers, target, {
|
|
@@ -4600,7 +4790,7 @@ function ReactionHandler(emojiOrSettings, settings = {}) {
|
|
|
4600
4790
|
settings: own
|
|
4601
4791
|
}, (other)=>other.method === method && other.emoji === emoji);
|
|
4602
4792
|
declareRoute(target, reactionRoute(method, emoji));
|
|
4603
|
-
};
|
|
4793
|
+
});
|
|
4604
4794
|
}
|
|
4605
4795
|
/** A custom emoji as Discord writes it in a message: `<:name:id>`, or `<a:name:id>` for an animated one. */ const CUSTOM_EMOJI = /^<a?:\w+:(\d+)>$/;
|
|
4606
4796
|
/**
|
|
@@ -4632,7 +4822,7 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4632
4822
|
/** The character a parameter will not cross, so one pattern segment maps to one value. */ const PARAM_SEPARATOR = '/';
|
|
4633
4823
|
/** Escapes a literal stretch of a pattern so only placeholders stay meaningful. */ const escapeLiteral = (literal)=>literal.replace(/[/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
4634
4824
|
/**
|
|
4635
|
-
* Compiles a pattern into a regex, its parameter names, their types and its specificity. A `{name}` takes a
|
|
4825
|
+
* Compiles a pattern into a regex, its parameter names, their types and its 4.1 specificity. A `{name}` takes a
|
|
4636
4826
|
* whole `/`-separated segment, so a uuid is captured whole and `profile/{uuid}` never overlaps
|
|
4637
4827
|
* `profile/{uuid}/{id}`; a param that shares a segment with literal text is refused.
|
|
4638
4828
|
*/ function createRegexFromPattern(pattern) {
|
|
@@ -4672,14 +4862,11 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4672
4862
|
literalLength += trailing.length;
|
|
4673
4863
|
regexPattern += escapeLiteral(trailing);
|
|
4674
4864
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
4675
|
-
//
|
|
4676
|
-
// more exactly than one leaving it to a parameter. Fewer parameters breaks a tie
|
|
4677
|
-
// between equal-length patterns, then narrower typed params; the order the controllers
|
|
4678
|
-
// and handlers are listed in settles what is left, and the startup warning names the pair.
|
|
4865
|
+
// What 4.1 ranked by, kept for code that reads CommandMeta.specificity; routing ranks segment by segment
|
|
4679
4866
|
const specificity = routeSpecificity({
|
|
4680
4867
|
literals: literalLength,
|
|
4681
4868
|
params: params.length,
|
|
4682
|
-
typed: Object.values(types).reduce((sum, type)=>sum +
|
|
4869
|
+
typed: Object.values(types).reduce((sum, type)=>sum + (choicesOf(type) ? 4 : type === 'bool' ? 3 : type === 'int' ? 2 : 1), 0)
|
|
4683
4870
|
});
|
|
4684
4871
|
return {
|
|
4685
4872
|
regex,
|
|
@@ -4688,9 +4875,8 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4688
4875
|
specificity
|
|
4689
4876
|
};
|
|
4690
4877
|
}
|
|
4691
|
-
/** How few values a segment type takes, so a narrower type ranks first: words to choose from, then bool, int, number. */ const narrowness = (type)=>choicesOf(type) ? 4 : type === 'bool' ? 3 : type === 'int' ? 2 : 1;
|
|
4692
4878
|
/** Why a `{name:type}` cannot type a customId segment, which holds text the bot wrote, with no message to read. */ function segmentTypeProblem(param, type) {
|
|
4693
|
-
const kinds = 'string, int, number, bool, or words to choose from such as {mode:on|off}';
|
|
4879
|
+
const kinds = 'string, int, number, bool, snowflake, uuid, or words to choose from such as {mode:on|off}';
|
|
4694
4880
|
if ([
|
|
4695
4881
|
'member',
|
|
4696
4882
|
'user',
|
|
@@ -4714,10 +4900,11 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4714
4900
|
* the keys the handler's params require are checked against the pattern or route, a typed segment's value against
|
|
4715
4901
|
* its type, and a select menu's choices, such as `values: string[]`, against what discord.js gives; a key a pipe
|
|
4716
4902
|
* produces is declared `Piped<T>`, and left to the pipe. Two component handlers of one type whose patterns match
|
|
4717
|
-
* exactly the same ids stop the bot at startup
|
|
4718
|
-
*
|
|
4719
|
-
*
|
|
4720
|
-
*
|
|
4903
|
+
* exactly the same ids stop the bot at startup. Patterns that both match an id rank segment by segment, left to
|
|
4904
|
+
* right: at the first segment one spells out as literal text and the other leaves to a param, the literal one runs,
|
|
4905
|
+
* so `profile/me/{section}` takes `profile/me/edit` from `profile/{userId}/edit`; between patterns that leaves tied,
|
|
4906
|
+
* the narrower type at the first param where they differ. Two still tied are warned about as the bot starts, and the
|
|
4907
|
+
* one listed first runs. A context menu handler receives the kind its builder's `setType()` names, and one
|
|
4721
4908
|
* declaring the other kind fails to compile; when the compiler cannot tell the kind, `@Command` checks the parameter
|
|
4722
4909
|
* type it emits as it applies. A subclass that re-declares an inherited handler on the same name or pattern takes its
|
|
4723
4910
|
* own builder and options; on another it still answers the inherited one too, which the bot warns about as it
|
|
@@ -4750,11 +4937,11 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4750
4937
|
* @category Handlers
|
|
4751
4938
|
* @see {@link CommandBuilder}
|
|
4752
4939
|
* @see {@link route}
|
|
4753
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
4754
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
4940
|
+
* @see {@link https://meocord.dev/docs/4.2/slash-commands | Slash commands}
|
|
4941
|
+
* @see {@link https://meocord.dev/docs/4.2/components | Buttons, selects and modals}
|
|
4755
4942
|
*/ function Command(name, builderOrType) {
|
|
4756
4943
|
const commandName = typeof name === 'string' ? name : name.pattern;
|
|
4757
|
-
return function(target, propertyKey, _descriptor) {
|
|
4944
|
+
return declaring(function(target, propertyKey, _descriptor) {
|
|
4758
4945
|
var _commands, _commandName;
|
|
4759
4946
|
refuseOnClass('@Command', target, propertyKey);
|
|
4760
4947
|
const originalMethod = _descriptor.value;
|
|
@@ -4835,10 +5022,9 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4835
5022
|
cause: error
|
|
4836
5023
|
}));
|
|
4837
5024
|
}
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
specificity = patternSpecificity;
|
|
5025
|
+
regex = pattern.regex;
|
|
5026
|
+
dynamicParams = pattern.params;
|
|
5027
|
+
specificity = pattern.specificity;
|
|
4842
5028
|
}
|
|
4843
5029
|
const declared = {
|
|
4844
5030
|
methodName: propertyKey,
|
|
@@ -4862,7 +5048,7 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4862
5048
|
else metas[replaced] = declared;
|
|
4863
5049
|
Reflect.defineMetadata(META.commands, commands, target);
|
|
4864
5050
|
declareRoute(target, commandRoute(propertyKey, commandType, commandName));
|
|
4865
|
-
};
|
|
5051
|
+
});
|
|
4866
5052
|
}
|
|
4867
5053
|
/** The interaction class Discord sends for each context menu kind. */ const CONTEXT_MENU_INTERACTIONS = new Map([
|
|
4868
5054
|
[
|
|
@@ -4934,9 +5120,9 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4934
5120
|
* @group Decorators
|
|
4935
5121
|
* @category Handlers
|
|
4936
5122
|
* @see {@link Command}
|
|
4937
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
5123
|
+
* @see {@link https://meocord.dev/docs/4.2/autocomplete | Autocomplete}
|
|
4938
5124
|
*/ function Autocomplete(commandPath, optionName) {
|
|
4939
|
-
return function(target, propertyKey, _descriptor) {
|
|
5125
|
+
return declaring(function(target, propertyKey, _descriptor) {
|
|
4940
5126
|
refuseOnClass('@Autocomplete', target, propertyKey);
|
|
4941
5127
|
const methodName = propertyKey.toString();
|
|
4942
5128
|
addOwnHandler(META.autocompleteHandlers, target, {
|
|
@@ -4945,7 +5131,7 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4945
5131
|
methodName
|
|
4946
5132
|
}, (other)=>other.methodName === methodName && other.commandPath === commandPath && other.optionName === optionName);
|
|
4947
5133
|
declareRoute(target, autocompleteRoute(methodName, commandPath, optionName));
|
|
4948
|
-
};
|
|
5134
|
+
});
|
|
4949
5135
|
}
|
|
4950
5136
|
/**
|
|
4951
5137
|
* Returns a controller's autocomplete handlers, option-specific ones first.
|
|
@@ -4967,52 +5153,63 @@ const PLACEHOLDER_PATTERN = /\{(\w+)(?::([^}/]*))?}/g;
|
|
|
4967
5153
|
const match = new RegExp(`^${PLACEHOLDER_PATTERN.source}$`).exec(segment);
|
|
4968
5154
|
return match ? typeKey(match[2]) : undefined;
|
|
4969
5155
|
}
|
|
4970
|
-
/**
|
|
5156
|
+
/**
|
|
5157
|
+
* How few values a segment type takes, so a narrower type ranks first: words to choose from, then bool, uuid,
|
|
5158
|
+
* snowflake, int, number, and text last. A snowflake is narrower than a number, which takes all its values.
|
|
5159
|
+
*/ const NARROWNESS = lookupTable({
|
|
5160
|
+
bool: 5,
|
|
5161
|
+
uuid: 4,
|
|
5162
|
+
snowflake: 3,
|
|
5163
|
+
int: 2,
|
|
5164
|
+
number: 1,
|
|
5165
|
+
'': 0
|
|
5166
|
+
});
|
|
5167
|
+
const narrowness = (type)=>choicesOf(type) ? 6 : NARROWNESS[type];
|
|
5168
|
+
/**
|
|
5169
|
+
* How narrow a pattern segment that is a param, `{name}` or `{name:type}`, is: from `0` for text to `4` for words to
|
|
5170
|
+
* choose from. `undefined` for a segment of literal text.
|
|
5171
|
+
*/ function paramNarrowness(segment) {
|
|
5172
|
+
const type = segmentParamType(segment);
|
|
5173
|
+
return type === undefined ? undefined : narrowness(type);
|
|
5174
|
+
}
|
|
5175
|
+
/** A value of each scalar segment type: an int, a bool, a snowflake and a uuid; text takes any. */ const SAMPLE_VALUES = [
|
|
5176
|
+
'1',
|
|
5177
|
+
'true',
|
|
5178
|
+
'10000000000000000',
|
|
5179
|
+
'00000000-0000-0000-0000-000000000000'
|
|
5180
|
+
];
|
|
5181
|
+
/** A value both readings of a segment take: a literal, text, or a typed param; `undefined` when they share none. */ function sharedValue(left, right) {
|
|
4971
5182
|
const [a, b] = [
|
|
4972
5183
|
segmentParamType(left),
|
|
4973
5184
|
segmentParamType(right)
|
|
4974
5185
|
];
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
5186
|
+
const takes = (type, segment, value)=>type === undefined ? segment === value : type === '' || parseSegment(type, value) !== undefined;
|
|
5187
|
+
// Every type takes one of these when it takes anything another reading takes: a literal, a listed word, or a value
|
|
5188
|
+
// of each scalar type
|
|
5189
|
+
const candidates = [
|
|
5190
|
+
...a === undefined ? [
|
|
4979
5191
|
left
|
|
4980
|
-
] : [
|
|
4981
|
-
|
|
5192
|
+
] : [],
|
|
5193
|
+
...b === undefined ? [
|
|
4982
5194
|
right
|
|
4983
|
-
]
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
// Words to choose from overlap another type when one of them is a value of it
|
|
4988
|
-
const [wordsA, wordsB] = [
|
|
4989
|
-
choicesOf(a),
|
|
4990
|
-
choicesOf(b)
|
|
5195
|
+
] : [],
|
|
5196
|
+
...choicesOf(a ?? '') ?? [],
|
|
5197
|
+
...choicesOf(b ?? '') ?? [],
|
|
5198
|
+
...SAMPLE_VALUES
|
|
4991
5199
|
];
|
|
4992
|
-
|
|
4993
|
-
if (wordsB) return wordsB.some((word)=>parseSegment(a, word) !== undefined);
|
|
4994
|
-
// Of the scalar types, only a whole number and a number share values
|
|
4995
|
-
return a === 'int' && b === 'number' || a === 'number' && b === 'int';
|
|
5200
|
+
return candidates.find((value)=>takes(a, left, value) && takes(b, right, value));
|
|
4996
5201
|
}
|
|
4997
5202
|
/**
|
|
4998
|
-
*
|
|
4999
|
-
*
|
|
5000
|
-
*/ function
|
|
5001
|
-
const
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
const disjoint = left.some((segment, index)=>!segmentsOverlap(segment, right[index]));
|
|
5009
|
-
if (!disjoint) collisions.push([
|
|
5010
|
-
patterns[i],
|
|
5011
|
-
patterns[j]
|
|
5012
|
-
]);
|
|
5013
|
-
}
|
|
5014
|
-
}
|
|
5015
|
-
return collisions;
|
|
5203
|
+
* A customId both patterns match, such as `ticket/1/close` for `ticket/{id}/close` and `ticket/{n:int}/close`;
|
|
5204
|
+
* `undefined` when no id matches both.
|
|
5205
|
+
*/ function sharedCustomId(left, right) {
|
|
5206
|
+
const [a, b] = [
|
|
5207
|
+
left.split(PARAM_SEPARATOR),
|
|
5208
|
+
right.split(PARAM_SEPARATOR)
|
|
5209
|
+
];
|
|
5210
|
+
if (a.length !== b.length) return undefined;
|
|
5211
|
+
const values = a.map((segment, index)=>sharedValue(segment, b[index]));
|
|
5212
|
+
return values.every((value)=>value !== undefined) ? values.join(PARAM_SEPARATOR) : undefined;
|
|
5016
5213
|
}
|
|
5017
5214
|
|
|
5018
5215
|
exports.Autocomplete = Autocomplete;
|
|
@@ -5041,6 +5238,7 @@ exports.Translator = Translator;
|
|
|
5041
5238
|
exports.UnroutedExecutionContext = UnroutedExecutionContext;
|
|
5042
5239
|
exports.UserError = UserError;
|
|
5043
5240
|
exports.ValidationError = ValidationError;
|
|
5241
|
+
exports.allDeclaredErrors = allDeclaredErrors;
|
|
5044
5242
|
exports.ambientThemeVersion = ambientThemeVersion;
|
|
5045
5243
|
exports.applyDecorators = applyDecorators;
|
|
5046
5244
|
exports.assertValidTheme = assertValidTheme;
|
|
@@ -5053,6 +5251,7 @@ exports.bundleEntry = bundleEntry;
|
|
|
5053
5251
|
exports.callOrder = callOrder;
|
|
5054
5252
|
exports.choicesOf = choicesOf;
|
|
5055
5253
|
exports.claimAmbientTheme = claimAmbientTheme;
|
|
5254
|
+
exports.collectStartupErrors = collectStartupErrors;
|
|
5056
5255
|
exports.compiledConfigMessage = compiledConfigMessage;
|
|
5057
5256
|
exports.cooldownMessage = cooldownMessage;
|
|
5058
5257
|
exports.cooldownStoreMessage = cooldownStoreMessage;
|
|
@@ -5060,6 +5259,8 @@ exports.cooldownText = cooldownText;
|
|
|
5060
5259
|
exports.copyLayer = copyLayer;
|
|
5061
5260
|
exports.createRegexFromPattern = createRegexFromPattern;
|
|
5062
5261
|
exports.createTranslator = createTranslator;
|
|
5262
|
+
exports.declaredErrorsOf = declaredErrorsOf;
|
|
5263
|
+
exports.declaring = declaring;
|
|
5063
5264
|
exports.decoratedName = decoratedName;
|
|
5064
5265
|
exports.defaultTheme = defaultTheme;
|
|
5065
5266
|
exports.defineCatalog = defineCatalog;
|
|
@@ -5067,10 +5268,10 @@ exports.deprecatedOnMethod = deprecatedOnMethod;
|
|
|
5067
5268
|
exports.describeInteraction = describeInteraction;
|
|
5068
5269
|
exports.describeRefusal = describeRefusal;
|
|
5069
5270
|
exports.describeValue = describeValue;
|
|
5271
|
+
exports.dropInheritedRoutes = dropInheritedRoutes;
|
|
5070
5272
|
exports.duration = duration;
|
|
5071
5273
|
exports.escapeForLog = escapeForLog;
|
|
5072
5274
|
exports.existingResponse = existingResponse;
|
|
5073
|
-
exports.findAmbiguousRoutes = findAmbiguousRoutes;
|
|
5074
5275
|
exports.focusedOptionName = focusedOptionName;
|
|
5075
5276
|
exports.forgetRecorded = forgetRecorded;
|
|
5076
5277
|
exports.getAutocompleteHandlers = getAutocompleteHandlers;
|
|
@@ -5096,6 +5297,7 @@ exports.isUserOutcome = isUserOutcome;
|
|
|
5096
5297
|
exports.listed = listed;
|
|
5097
5298
|
exports.loadMeoCordConfig = loadMeoCordConfig;
|
|
5098
5299
|
exports.logFailedSend = logFailedSend;
|
|
5300
|
+
exports.logStartupErrors = logStartupErrors;
|
|
5099
5301
|
exports.lookup = lookup;
|
|
5100
5302
|
exports.lookupLayers = lookupLayers;
|
|
5101
5303
|
exports.lookupTable = lookupTable;
|
|
@@ -5109,6 +5311,7 @@ exports.missingTranslatorError = missingTranslatorError;
|
|
|
5109
5311
|
exports.number = number;
|
|
5110
5312
|
exports.ownHandlerList = ownHandlerList;
|
|
5111
5313
|
exports.ownsAmbientTheme = ownsAmbientTheme;
|
|
5314
|
+
exports.paramNarrowness = paramNarrowness;
|
|
5112
5315
|
exports.parseSegment = parseSegment;
|
|
5113
5316
|
exports.patternShape = patternShape;
|
|
5114
5317
|
exports.placeholderNames = placeholderNames;
|
|
@@ -5132,8 +5335,12 @@ exports.runInThemeScope = runInThemeScope;
|
|
|
5132
5335
|
exports.runWithTheme = runWithTheme;
|
|
5133
5336
|
exports.setLegacyThemeLayer = setLegacyThemeLayer;
|
|
5134
5337
|
exports.setPresenter = setPresenter;
|
|
5338
|
+
exports.setStartupErrorsMode = setStartupErrorsMode;
|
|
5135
5339
|
exports.setThemeOutsideCalls = setThemeOutsideCalls;
|
|
5340
|
+
exports.sharedCustomId = sharedCustomId;
|
|
5136
5341
|
exports.stampCall = stampCall;
|
|
5342
|
+
exports.startupError = startupError;
|
|
5343
|
+
exports.stopOnStartupErrors = stopOnStartupErrors;
|
|
5137
5344
|
exports.textOfIssue = textOfIssue;
|
|
5138
5345
|
exports.textRenderer = textRenderer;
|
|
5139
5346
|
exports.themeForInteraction = themeForInteraction;
|
|
@@ -5148,6 +5355,7 @@ exports.useTheme = useTheme;
|
|
|
5148
5355
|
exports.userWords = userWords;
|
|
5149
5356
|
exports.warnDeprecated = warnDeprecated;
|
|
5150
5357
|
exports.warnDeprecatedBehaviour = warnDeprecatedBehaviour;
|
|
5358
|
+
exports.warnOnce = warnOnce;
|
|
5151
5359
|
exports.warnPlaceholder = warnPlaceholder;
|
|
5152
5360
|
exports.withRelease = withRelease;
|
|
5153
5361
|
exports.withSendableFiles = withSendableFiles;
|