This commit addresses the issue of Chromium v126 and later not displaying error messages correctly when the error object's `message` property uses a getter. It refactors the code to utilize an immutable Error object with recursive context, improves error message formatting and leverages the `cause` property. Changes: - Refactor error wrapping internals to use an immutable error object, eliminating `message` getters. - Utilize the `cause` property in contextual errors for enhanced error display in the console. - Enhance message formatting with better indentation and listing. - Improve clarity by renaming values thrown during validations.
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import type {
|
|
FunctionCallData,
|
|
FunctionCallsData,
|
|
FunctionCallParametersData,
|
|
} from '@/application/collections/';
|
|
import { isArray, isPlainObject } from '@/TypeHelpers';
|
|
import { createTypeValidator, type TypeValidator } from '@/application/Parser/Common/TypeValidator';
|
|
import { FunctionCallArgumentCollection } from './Argument/FunctionCallArgumentCollection';
|
|
import { ParsedFunctionCall } from './ParsedFunctionCall';
|
|
import { createFunctionCallArgument, type FunctionCallArgumentFactory } from './Argument/FunctionCallArgument';
|
|
import type { FunctionCall } from './FunctionCall';
|
|
|
|
export interface FunctionCallsParser {
|
|
(
|
|
calls: FunctionCallsData,
|
|
utilities?: FunctionCallParsingUtilities,
|
|
): FunctionCall[];
|
|
}
|
|
|
|
interface FunctionCallParsingUtilities {
|
|
readonly typeValidator: TypeValidator;
|
|
readonly createCallArgument: FunctionCallArgumentFactory;
|
|
}
|
|
|
|
const DefaultUtilities: FunctionCallParsingUtilities = {
|
|
typeValidator: createTypeValidator(),
|
|
createCallArgument: createFunctionCallArgument,
|
|
};
|
|
|
|
export const parseFunctionCalls: FunctionCallsParser = (
|
|
calls,
|
|
utilities = DefaultUtilities,
|
|
) => {
|
|
const sequence = getCallSequence(calls, utilities.typeValidator);
|
|
return sequence.map((call) => parseFunctionCall(call, utilities));
|
|
};
|
|
|
|
function getCallSequence(calls: FunctionCallsData, validator: TypeValidator): FunctionCallData[] {
|
|
if (!isPlainObject(calls) && !isArray(calls)) {
|
|
throw new Error('called function(s) must be an object or array');
|
|
}
|
|
if (isArray(calls)) {
|
|
validator.assertNonEmptyCollection({
|
|
value: calls,
|
|
valueName: 'Function call sequence',
|
|
});
|
|
return calls as FunctionCallData[];
|
|
}
|
|
const singleCall = calls as FunctionCallData;
|
|
return [singleCall];
|
|
}
|
|
|
|
function parseFunctionCall(
|
|
call: FunctionCallData,
|
|
utilities: FunctionCallParsingUtilities,
|
|
): FunctionCall {
|
|
utilities.typeValidator.assertObject({
|
|
value: call,
|
|
valueName: 'Function call',
|
|
allowedProperties: ['function', 'parameters'],
|
|
});
|
|
const callArgs = parseArgs(call.parameters, utilities.createCallArgument);
|
|
return new ParsedFunctionCall(call.function, callArgs);
|
|
}
|
|
|
|
function parseArgs(
|
|
parameters: FunctionCallParametersData | undefined,
|
|
createArgument: FunctionCallArgumentFactory,
|
|
): FunctionCallArgumentCollection {
|
|
const parametersMap = parameters ?? {};
|
|
return Object.keys(parametersMap)
|
|
.map((parameterName) => {
|
|
const argumentValue = parametersMap[parameterName];
|
|
return createArgument(parameterName, argumentValue);
|
|
})
|
|
.reduce((args, arg) => {
|
|
args.addArgument(arg);
|
|
return args;
|
|
}, new FunctionCallArgumentCollection());
|
|
}
|