485 lines
17 KiB
JavaScript
485 lines
17 KiB
JavaScript
|
var __defProp = Object.defineProperty;
|
||
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
||
|
var __export = (target, all) => {
|
||
|
for (var name in all)
|
||
|
__defProp(target, name, { get: all[name], enumerable: true });
|
||
|
};
|
||
|
var __copyProps = (to, from, except, desc) => {
|
||
|
if (from && typeof from === "object" || typeof from === "function") {
|
||
|
for (let key of __getOwnPropNames(from))
|
||
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
||
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||
|
}
|
||
|
return to;
|
||
|
};
|
||
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||
|
|
||
|
// src/index.ts
|
||
|
var src_exports = {};
|
||
|
__export(src_exports, {
|
||
|
EndpointError: () => EndpointError,
|
||
|
customEndpointFunctions: () => customEndpointFunctions,
|
||
|
isIpAddress: () => isIpAddress,
|
||
|
isValidHostLabel: () => isValidHostLabel,
|
||
|
resolveEndpoint: () => resolveEndpoint
|
||
|
});
|
||
|
module.exports = __toCommonJS(src_exports);
|
||
|
|
||
|
// src/lib/isIpAddress.ts
|
||
|
var IP_V4_REGEX = new RegExp(
|
||
|
`^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$`
|
||
|
);
|
||
|
var isIpAddress = /* @__PURE__ */ __name((value) => IP_V4_REGEX.test(value) || value.startsWith("[") && value.endsWith("]"), "isIpAddress");
|
||
|
|
||
|
// src/lib/isValidHostLabel.ts
|
||
|
var VALID_HOST_LABEL_REGEX = new RegExp(`^(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$`);
|
||
|
var isValidHostLabel = /* @__PURE__ */ __name((value, allowSubDomains = false) => {
|
||
|
if (!allowSubDomains) {
|
||
|
return VALID_HOST_LABEL_REGEX.test(value);
|
||
|
}
|
||
|
const labels = value.split(".");
|
||
|
for (const label of labels) {
|
||
|
if (!isValidHostLabel(label)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}, "isValidHostLabel");
|
||
|
|
||
|
// src/utils/customEndpointFunctions.ts
|
||
|
var customEndpointFunctions = {};
|
||
|
|
||
|
// src/debug/debugId.ts
|
||
|
var debugId = "endpoints";
|
||
|
|
||
|
// src/debug/toDebugString.ts
|
||
|
function toDebugString(input) {
|
||
|
if (typeof input !== "object" || input == null) {
|
||
|
return input;
|
||
|
}
|
||
|
if ("ref" in input) {
|
||
|
return `$${toDebugString(input.ref)}`;
|
||
|
}
|
||
|
if ("fn" in input) {
|
||
|
return `${input.fn}(${(input.argv || []).map(toDebugString).join(", ")})`;
|
||
|
}
|
||
|
return JSON.stringify(input, null, 2);
|
||
|
}
|
||
|
__name(toDebugString, "toDebugString");
|
||
|
|
||
|
// src/types/EndpointError.ts
|
||
|
var _EndpointError = class _EndpointError extends Error {
|
||
|
constructor(message) {
|
||
|
super(message);
|
||
|
this.name = "EndpointError";
|
||
|
}
|
||
|
};
|
||
|
__name(_EndpointError, "EndpointError");
|
||
|
var EndpointError = _EndpointError;
|
||
|
|
||
|
// src/lib/booleanEquals.ts
|
||
|
var booleanEquals = /* @__PURE__ */ __name((value1, value2) => value1 === value2, "booleanEquals");
|
||
|
|
||
|
// src/lib/getAttrPathList.ts
|
||
|
var getAttrPathList = /* @__PURE__ */ __name((path) => {
|
||
|
const parts = path.split(".");
|
||
|
const pathList = [];
|
||
|
for (const part of parts) {
|
||
|
const squareBracketIndex = part.indexOf("[");
|
||
|
if (squareBracketIndex !== -1) {
|
||
|
if (part.indexOf("]") !== part.length - 1) {
|
||
|
throw new EndpointError(`Path: '${path}' does not end with ']'`);
|
||
|
}
|
||
|
const arrayIndex = part.slice(squareBracketIndex + 1, -1);
|
||
|
if (Number.isNaN(parseInt(arrayIndex))) {
|
||
|
throw new EndpointError(`Invalid array index: '${arrayIndex}' in path: '${path}'`);
|
||
|
}
|
||
|
if (squareBracketIndex !== 0) {
|
||
|
pathList.push(part.slice(0, squareBracketIndex));
|
||
|
}
|
||
|
pathList.push(arrayIndex);
|
||
|
} else {
|
||
|
pathList.push(part);
|
||
|
}
|
||
|
}
|
||
|
return pathList;
|
||
|
}, "getAttrPathList");
|
||
|
|
||
|
// src/lib/getAttr.ts
|
||
|
var getAttr = /* @__PURE__ */ __name((value, path) => getAttrPathList(path).reduce((acc, index) => {
|
||
|
if (typeof acc !== "object") {
|
||
|
throw new EndpointError(`Index '${index}' in '${path}' not found in '${JSON.stringify(value)}'`);
|
||
|
} else if (Array.isArray(acc)) {
|
||
|
return acc[parseInt(index)];
|
||
|
}
|
||
|
return acc[index];
|
||
|
}, value), "getAttr");
|
||
|
|
||
|
// src/lib/isSet.ts
|
||
|
var isSet = /* @__PURE__ */ __name((value) => value != null, "isSet");
|
||
|
|
||
|
// src/lib/not.ts
|
||
|
var not = /* @__PURE__ */ __name((value) => !value, "not");
|
||
|
|
||
|
// src/lib/parseURL.ts
|
||
|
var import_types3 = require("@smithy/types");
|
||
|
var DEFAULT_PORTS = {
|
||
|
[import_types3.EndpointURLScheme.HTTP]: 80,
|
||
|
[import_types3.EndpointURLScheme.HTTPS]: 443
|
||
|
};
|
||
|
var parseURL = /* @__PURE__ */ __name((value) => {
|
||
|
const whatwgURL = (() => {
|
||
|
try {
|
||
|
if (value instanceof URL) {
|
||
|
return value;
|
||
|
}
|
||
|
if (typeof value === "object" && "hostname" in value) {
|
||
|
const { hostname: hostname2, port, protocol: protocol2 = "", path = "", query = {} } = value;
|
||
|
const url = new URL(`${protocol2}//${hostname2}${port ? `:${port}` : ""}${path}`);
|
||
|
url.search = Object.entries(query).map(([k, v]) => `${k}=${v}`).join("&");
|
||
|
return url;
|
||
|
}
|
||
|
return new URL(value);
|
||
|
} catch (error) {
|
||
|
return null;
|
||
|
}
|
||
|
})();
|
||
|
if (!whatwgURL) {
|
||
|
console.error(`Unable to parse ${JSON.stringify(value)} as a whatwg URL.`);
|
||
|
return null;
|
||
|
}
|
||
|
const urlString = whatwgURL.href;
|
||
|
const { host, hostname, pathname, protocol, search } = whatwgURL;
|
||
|
if (search) {
|
||
|
return null;
|
||
|
}
|
||
|
const scheme = protocol.slice(0, -1);
|
||
|
if (!Object.values(import_types3.EndpointURLScheme).includes(scheme)) {
|
||
|
return null;
|
||
|
}
|
||
|
const isIp = isIpAddress(hostname);
|
||
|
const inputContainsDefaultPort = urlString.includes(`${host}:${DEFAULT_PORTS[scheme]}`) || typeof value === "string" && value.includes(`${host}:${DEFAULT_PORTS[scheme]}`);
|
||
|
const authority = `${host}${inputContainsDefaultPort ? `:${DEFAULT_PORTS[scheme]}` : ``}`;
|
||
|
return {
|
||
|
scheme,
|
||
|
authority,
|
||
|
path: pathname,
|
||
|
normalizedPath: pathname.endsWith("/") ? pathname : `${pathname}/`,
|
||
|
isIp
|
||
|
};
|
||
|
}, "parseURL");
|
||
|
|
||
|
// src/lib/stringEquals.ts
|
||
|
var stringEquals = /* @__PURE__ */ __name((value1, value2) => value1 === value2, "stringEquals");
|
||
|
|
||
|
// src/lib/substring.ts
|
||
|
var substring = /* @__PURE__ */ __name((input, start, stop, reverse) => {
|
||
|
if (start >= stop || input.length < stop) {
|
||
|
return null;
|
||
|
}
|
||
|
if (!reverse) {
|
||
|
return input.substring(start, stop);
|
||
|
}
|
||
|
return input.substring(input.length - stop, input.length - start);
|
||
|
}, "substring");
|
||
|
|
||
|
// src/lib/uriEncode.ts
|
||
|
var uriEncode = /* @__PURE__ */ __name((value) => encodeURIComponent(value).replace(/[!*'()]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`), "uriEncode");
|
||
|
|
||
|
// src/utils/endpointFunctions.ts
|
||
|
var endpointFunctions = {
|
||
|
booleanEquals,
|
||
|
getAttr,
|
||
|
isSet,
|
||
|
isValidHostLabel,
|
||
|
not,
|
||
|
parseURL,
|
||
|
stringEquals,
|
||
|
substring,
|
||
|
uriEncode
|
||
|
};
|
||
|
|
||
|
// src/utils/evaluateTemplate.ts
|
||
|
var evaluateTemplate = /* @__PURE__ */ __name((template, options) => {
|
||
|
const evaluatedTemplateArr = [];
|
||
|
const templateContext = {
|
||
|
...options.endpointParams,
|
||
|
...options.referenceRecord
|
||
|
};
|
||
|
let currentIndex = 0;
|
||
|
while (currentIndex < template.length) {
|
||
|
const openingBraceIndex = template.indexOf("{", currentIndex);
|
||
|
if (openingBraceIndex === -1) {
|
||
|
evaluatedTemplateArr.push(template.slice(currentIndex));
|
||
|
break;
|
||
|
}
|
||
|
evaluatedTemplateArr.push(template.slice(currentIndex, openingBraceIndex));
|
||
|
const closingBraceIndex = template.indexOf("}", openingBraceIndex);
|
||
|
if (closingBraceIndex === -1) {
|
||
|
evaluatedTemplateArr.push(template.slice(openingBraceIndex));
|
||
|
break;
|
||
|
}
|
||
|
if (template[openingBraceIndex + 1] === "{" && template[closingBraceIndex + 1] === "}") {
|
||
|
evaluatedTemplateArr.push(template.slice(openingBraceIndex + 1, closingBraceIndex));
|
||
|
currentIndex = closingBraceIndex + 2;
|
||
|
}
|
||
|
const parameterName = template.substring(openingBraceIndex + 1, closingBraceIndex);
|
||
|
if (parameterName.includes("#")) {
|
||
|
const [refName, attrName] = parameterName.split("#");
|
||
|
evaluatedTemplateArr.push(getAttr(templateContext[refName], attrName));
|
||
|
} else {
|
||
|
evaluatedTemplateArr.push(templateContext[parameterName]);
|
||
|
}
|
||
|
currentIndex = closingBraceIndex + 1;
|
||
|
}
|
||
|
return evaluatedTemplateArr.join("");
|
||
|
}, "evaluateTemplate");
|
||
|
|
||
|
// src/utils/getReferenceValue.ts
|
||
|
var getReferenceValue = /* @__PURE__ */ __name(({ ref }, options) => {
|
||
|
const referenceRecord = {
|
||
|
...options.endpointParams,
|
||
|
...options.referenceRecord
|
||
|
};
|
||
|
return referenceRecord[ref];
|
||
|
}, "getReferenceValue");
|
||
|
|
||
|
// src/utils/evaluateExpression.ts
|
||
|
var evaluateExpression = /* @__PURE__ */ __name((obj, keyName, options) => {
|
||
|
if (typeof obj === "string") {
|
||
|
return evaluateTemplate(obj, options);
|
||
|
} else if (obj["fn"]) {
|
||
|
return callFunction(obj, options);
|
||
|
} else if (obj["ref"]) {
|
||
|
return getReferenceValue(obj, options);
|
||
|
}
|
||
|
throw new EndpointError(`'${keyName}': ${String(obj)} is not a string, function or reference.`);
|
||
|
}, "evaluateExpression");
|
||
|
|
||
|
// src/utils/callFunction.ts
|
||
|
var callFunction = /* @__PURE__ */ __name(({ fn, argv }, options) => {
|
||
|
const evaluatedArgs = argv.map(
|
||
|
(arg) => ["boolean", "number"].includes(typeof arg) ? arg : evaluateExpression(arg, "arg", options)
|
||
|
);
|
||
|
const fnSegments = fn.split(".");
|
||
|
if (fnSegments[0] in customEndpointFunctions && fnSegments[1] != null) {
|
||
|
return customEndpointFunctions[fnSegments[0]][fnSegments[1]](...evaluatedArgs);
|
||
|
}
|
||
|
return endpointFunctions[fn](...evaluatedArgs);
|
||
|
}, "callFunction");
|
||
|
|
||
|
// src/utils/evaluateCondition.ts
|
||
|
var evaluateCondition = /* @__PURE__ */ __name(({ assign, ...fnArgs }, options) => {
|
||
|
var _a, _b;
|
||
|
if (assign && assign in options.referenceRecord) {
|
||
|
throw new EndpointError(`'${assign}' is already defined in Reference Record.`);
|
||
|
}
|
||
|
const value = callFunction(fnArgs, options);
|
||
|
(_b = (_a = options.logger) == null ? void 0 : _a.debug) == null ? void 0 : _b.call(_a, `${debugId} evaluateCondition: ${toDebugString(fnArgs)} = ${toDebugString(value)}`);
|
||
|
return {
|
||
|
result: value === "" ? true : !!value,
|
||
|
...assign != null && { toAssign: { name: assign, value } }
|
||
|
};
|
||
|
}, "evaluateCondition");
|
||
|
|
||
|
// src/utils/evaluateConditions.ts
|
||
|
var evaluateConditions = /* @__PURE__ */ __name((conditions = [], options) => {
|
||
|
var _a, _b;
|
||
|
const conditionsReferenceRecord = {};
|
||
|
for (const condition of conditions) {
|
||
|
const { result, toAssign } = evaluateCondition(condition, {
|
||
|
...options,
|
||
|
referenceRecord: {
|
||
|
...options.referenceRecord,
|
||
|
...conditionsReferenceRecord
|
||
|
}
|
||
|
});
|
||
|
if (!result) {
|
||
|
return { result };
|
||
|
}
|
||
|
if (toAssign) {
|
||
|
conditionsReferenceRecord[toAssign.name] = toAssign.value;
|
||
|
(_b = (_a = options.logger) == null ? void 0 : _a.debug) == null ? void 0 : _b.call(_a, `${debugId} assign: ${toAssign.name} := ${toDebugString(toAssign.value)}`);
|
||
|
}
|
||
|
}
|
||
|
return { result: true, referenceRecord: conditionsReferenceRecord };
|
||
|
}, "evaluateConditions");
|
||
|
|
||
|
// src/utils/getEndpointHeaders.ts
|
||
|
var getEndpointHeaders = /* @__PURE__ */ __name((headers, options) => Object.entries(headers).reduce(
|
||
|
(acc, [headerKey, headerVal]) => ({
|
||
|
...acc,
|
||
|
[headerKey]: headerVal.map((headerValEntry) => {
|
||
|
const processedExpr = evaluateExpression(headerValEntry, "Header value entry", options);
|
||
|
if (typeof processedExpr !== "string") {
|
||
|
throw new EndpointError(`Header '${headerKey}' value '${processedExpr}' is not a string`);
|
||
|
}
|
||
|
return processedExpr;
|
||
|
})
|
||
|
}),
|
||
|
{}
|
||
|
), "getEndpointHeaders");
|
||
|
|
||
|
// src/utils/getEndpointProperty.ts
|
||
|
var getEndpointProperty = /* @__PURE__ */ __name((property, options) => {
|
||
|
if (Array.isArray(property)) {
|
||
|
return property.map((propertyEntry) => getEndpointProperty(propertyEntry, options));
|
||
|
}
|
||
|
switch (typeof property) {
|
||
|
case "string":
|
||
|
return evaluateTemplate(property, options);
|
||
|
case "object":
|
||
|
if (property === null) {
|
||
|
throw new EndpointError(`Unexpected endpoint property: ${property}`);
|
||
|
}
|
||
|
return getEndpointProperties(property, options);
|
||
|
case "boolean":
|
||
|
return property;
|
||
|
default:
|
||
|
throw new EndpointError(`Unexpected endpoint property type: ${typeof property}`);
|
||
|
}
|
||
|
}, "getEndpointProperty");
|
||
|
|
||
|
// src/utils/getEndpointProperties.ts
|
||
|
var getEndpointProperties = /* @__PURE__ */ __name((properties, options) => Object.entries(properties).reduce(
|
||
|
(acc, [propertyKey, propertyVal]) => ({
|
||
|
...acc,
|
||
|
[propertyKey]: getEndpointProperty(propertyVal, options)
|
||
|
}),
|
||
|
{}
|
||
|
), "getEndpointProperties");
|
||
|
|
||
|
// src/utils/getEndpointUrl.ts
|
||
|
var getEndpointUrl = /* @__PURE__ */ __name((endpointUrl, options) => {
|
||
|
const expression = evaluateExpression(endpointUrl, "Endpoint URL", options);
|
||
|
if (typeof expression === "string") {
|
||
|
try {
|
||
|
return new URL(expression);
|
||
|
} catch (error) {
|
||
|
console.error(`Failed to construct URL with ${expression}`, error);
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
throw new EndpointError(`Endpoint URL must be a string, got ${typeof expression}`);
|
||
|
}, "getEndpointUrl");
|
||
|
|
||
|
// src/utils/evaluateEndpointRule.ts
|
||
|
var evaluateEndpointRule = /* @__PURE__ */ __name((endpointRule, options) => {
|
||
|
var _a, _b;
|
||
|
const { conditions, endpoint } = endpointRule;
|
||
|
const { result, referenceRecord } = evaluateConditions(conditions, options);
|
||
|
if (!result) {
|
||
|
return;
|
||
|
}
|
||
|
const endpointRuleOptions = {
|
||
|
...options,
|
||
|
referenceRecord: { ...options.referenceRecord, ...referenceRecord }
|
||
|
};
|
||
|
const { url, properties, headers } = endpoint;
|
||
|
(_b = (_a = options.logger) == null ? void 0 : _a.debug) == null ? void 0 : _b.call(_a, `${debugId} Resolving endpoint from template: ${toDebugString(endpoint)}`);
|
||
|
return {
|
||
|
...headers != void 0 && {
|
||
|
headers: getEndpointHeaders(headers, endpointRuleOptions)
|
||
|
},
|
||
|
...properties != void 0 && {
|
||
|
properties: getEndpointProperties(properties, endpointRuleOptions)
|
||
|
},
|
||
|
url: getEndpointUrl(url, endpointRuleOptions)
|
||
|
};
|
||
|
}, "evaluateEndpointRule");
|
||
|
|
||
|
// src/utils/evaluateErrorRule.ts
|
||
|
var evaluateErrorRule = /* @__PURE__ */ __name((errorRule, options) => {
|
||
|
const { conditions, error } = errorRule;
|
||
|
const { result, referenceRecord } = evaluateConditions(conditions, options);
|
||
|
if (!result) {
|
||
|
return;
|
||
|
}
|
||
|
throw new EndpointError(
|
||
|
evaluateExpression(error, "Error", {
|
||
|
...options,
|
||
|
referenceRecord: { ...options.referenceRecord, ...referenceRecord }
|
||
|
})
|
||
|
);
|
||
|
}, "evaluateErrorRule");
|
||
|
|
||
|
// src/utils/evaluateTreeRule.ts
|
||
|
var evaluateTreeRule = /* @__PURE__ */ __name((treeRule, options) => {
|
||
|
const { conditions, rules } = treeRule;
|
||
|
const { result, referenceRecord } = evaluateConditions(conditions, options);
|
||
|
if (!result) {
|
||
|
return;
|
||
|
}
|
||
|
return evaluateRules(rules, {
|
||
|
...options,
|
||
|
referenceRecord: { ...options.referenceRecord, ...referenceRecord }
|
||
|
});
|
||
|
}, "evaluateTreeRule");
|
||
|
|
||
|
// src/utils/evaluateRules.ts
|
||
|
var evaluateRules = /* @__PURE__ */ __name((rules, options) => {
|
||
|
for (const rule of rules) {
|
||
|
if (rule.type === "endpoint") {
|
||
|
const endpointOrUndefined = evaluateEndpointRule(rule, options);
|
||
|
if (endpointOrUndefined) {
|
||
|
return endpointOrUndefined;
|
||
|
}
|
||
|
} else if (rule.type === "error") {
|
||
|
evaluateErrorRule(rule, options);
|
||
|
} else if (rule.type === "tree") {
|
||
|
const endpointOrUndefined = evaluateTreeRule(rule, options);
|
||
|
if (endpointOrUndefined) {
|
||
|
return endpointOrUndefined;
|
||
|
}
|
||
|
} else {
|
||
|
throw new EndpointError(`Unknown endpoint rule: ${rule}`);
|
||
|
}
|
||
|
}
|
||
|
throw new EndpointError(`Rules evaluation failed`);
|
||
|
}, "evaluateRules");
|
||
|
|
||
|
// src/resolveEndpoint.ts
|
||
|
var resolveEndpoint = /* @__PURE__ */ __name((ruleSetObject, options) => {
|
||
|
var _a, _b, _c, _d, _e;
|
||
|
const { endpointParams, logger } = options;
|
||
|
const { parameters, rules } = ruleSetObject;
|
||
|
(_b = (_a = options.logger) == null ? void 0 : _a.debug) == null ? void 0 : _b.call(_a, `${debugId} Initial EndpointParams: ${toDebugString(endpointParams)}`);
|
||
|
const paramsWithDefault = Object.entries(parameters).filter(([, v]) => v.default != null).map(([k, v]) => [k, v.default]);
|
||
|
if (paramsWithDefault.length > 0) {
|
||
|
for (const [paramKey, paramDefaultValue] of paramsWithDefault) {
|
||
|
endpointParams[paramKey] = endpointParams[paramKey] ?? paramDefaultValue;
|
||
|
}
|
||
|
}
|
||
|
const requiredParams = Object.entries(parameters).filter(([, v]) => v.required).map(([k]) => k);
|
||
|
for (const requiredParam of requiredParams) {
|
||
|
if (endpointParams[requiredParam] == null) {
|
||
|
throw new EndpointError(`Missing required parameter: '${requiredParam}'`);
|
||
|
}
|
||
|
}
|
||
|
const endpoint = evaluateRules(rules, { endpointParams, logger, referenceRecord: {} });
|
||
|
if ((_c = options.endpointParams) == null ? void 0 : _c.Endpoint) {
|
||
|
try {
|
||
|
const givenEndpoint = new URL(options.endpointParams.Endpoint);
|
||
|
const { protocol, port } = givenEndpoint;
|
||
|
endpoint.url.protocol = protocol;
|
||
|
endpoint.url.port = port;
|
||
|
} catch (e) {
|
||
|
}
|
||
|
}
|
||
|
(_e = (_d = options.logger) == null ? void 0 : _d.debug) == null ? void 0 : _e.call(_d, `${debugId} Resolved endpoint: ${toDebugString(endpoint)}`);
|
||
|
return endpoint;
|
||
|
}, "resolveEndpoint");
|
||
|
// Annotate the CommonJS export names for ESM import in node:
|
||
|
|
||
|
0 && (module.exports = {
|
||
|
isIpAddress,
|
||
|
isValidHostLabel,
|
||
|
customEndpointFunctions,
|
||
|
resolveEndpoint,
|
||
|
EndpointError
|
||
|
});
|
||
|
|