@smithy/util-endpoints
Advanced tools
+12
-573
@@ -1,573 +0,12 @@ | ||
| 'use strict'; | ||
| var types = require('@smithy/types'); | ||
| class BinaryDecisionDiagram { | ||
| nodes; | ||
| root; | ||
| conditions; | ||
| results; | ||
| constructor(bdd, root, conditions, results) { | ||
| this.nodes = bdd; | ||
| this.root = root; | ||
| this.conditions = conditions; | ||
| this.results = results; | ||
| } | ||
| static from(bdd, root, conditions, results) { | ||
| return new BinaryDecisionDiagram(bdd, root, conditions, results); | ||
| } | ||
| } | ||
| class EndpointCache { | ||
| capacity; | ||
| data = new Map(); | ||
| parameters = []; | ||
| constructor({ size, params }) { | ||
| this.capacity = size ?? 50; | ||
| if (params) { | ||
| this.parameters = params; | ||
| } | ||
| } | ||
| get(endpointParams, resolver) { | ||
| const key = this.hash(endpointParams); | ||
| if (key === false) { | ||
| return resolver(); | ||
| } | ||
| if (!this.data.has(key)) { | ||
| if (this.data.size > this.capacity + 10) { | ||
| const keys = this.data.keys(); | ||
| let i = 0; | ||
| while (true) { | ||
| const { value, done } = keys.next(); | ||
| this.data.delete(value); | ||
| if (done || ++i > 10) { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| this.data.set(key, resolver()); | ||
| } | ||
| return this.data.get(key); | ||
| } | ||
| size() { | ||
| return this.data.size; | ||
| } | ||
| hash(endpointParams) { | ||
| let buffer = ""; | ||
| const { parameters } = this; | ||
| if (parameters.length === 0) { | ||
| return false; | ||
| } | ||
| for (const param of parameters) { | ||
| const val = String(endpointParams[param] ?? ""); | ||
| if (val.includes("|;")) { | ||
| return false; | ||
| } | ||
| buffer += val + "|;"; | ||
| } | ||
| return buffer; | ||
| } | ||
| } | ||
| class EndpointError extends Error { | ||
| constructor(message) { | ||
| super(message); | ||
| this.name = "EndpointError"; | ||
| } | ||
| } | ||
| const debugId = "endpoints"; | ||
| 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); | ||
| } | ||
| const customEndpointFunctions = {}; | ||
| const booleanEquals = (value1, value2) => value1 === value2; | ||
| function coalesce(...args) { | ||
| for (const arg of args) { | ||
| if (arg != null) { | ||
| return arg; | ||
| } | ||
| } | ||
| return undefined; | ||
| } | ||
| const getAttrPathList = (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; | ||
| }; | ||
| const getAttr = (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)) { | ||
| const i = parseInt(index); | ||
| return acc[i < 0 ? acc.length + i : i]; | ||
| } | ||
| return acc[index]; | ||
| }, value); | ||
| const isSet = (value) => value != null; | ||
| const VALID_HOST_LABEL_REGEX = new RegExp(`^(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$`); | ||
| const isValidHostLabel = (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; | ||
| }; | ||
| function ite(condition, trueValue, falseValue) { | ||
| return condition ? trueValue : falseValue; | ||
| } | ||
| const not = (value) => !value; | ||
| const 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}$`); | ||
| const isIpAddress = (value) => IP_V4_REGEX.test(value) || (value.startsWith("[") && value.endsWith("]")); | ||
| const DEFAULT_PORTS = { | ||
| [types.EndpointURLScheme.HTTP]: 80, | ||
| [types.EndpointURLScheme.HTTPS]: 443, | ||
| }; | ||
| const parseURL = (value) => { | ||
| const whatwgURL = (() => { | ||
| try { | ||
| if (value instanceof URL) { | ||
| return value; | ||
| } | ||
| if (typeof value === "object" && "hostname" in value) { | ||
| const { hostname, port, protocol = "", path = "", query = {} } = value; | ||
| const url = new URL(`${protocol}//${hostname}${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(types.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, | ||
| }; | ||
| }; | ||
| function split(value, delimiter, limit) { | ||
| if (limit === 1) { | ||
| return [value]; | ||
| } | ||
| if (value === "") { | ||
| return [""]; | ||
| } | ||
| const parts = value.split(delimiter); | ||
| if (limit === 0) { | ||
| return parts; | ||
| } | ||
| return parts.slice(0, limit - 1).concat(parts.slice(1).join(delimiter)); | ||
| } | ||
| const stringEquals = (value1, value2) => value1 === value2; | ||
| const substring = (input, start, stop, reverse) => { | ||
| if (input == null || start >= stop || input.length < stop || /[^\u0000-\u007f]/.test(input)) { | ||
| return null; | ||
| } | ||
| if (!reverse) { | ||
| return input.substring(start, stop); | ||
| } | ||
| return input.substring(input.length - stop, input.length - start); | ||
| }; | ||
| const uriEncode = (value) => encodeURIComponent(value).replace(/[!*'()]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`); | ||
| const endpointFunctions = { | ||
| booleanEquals, | ||
| coalesce, | ||
| getAttr, | ||
| isSet, | ||
| isValidHostLabel, | ||
| ite, | ||
| not, | ||
| parseURL, | ||
| split, | ||
| stringEquals, | ||
| substring, | ||
| uriEncode, | ||
| }; | ||
| const evaluateTemplate = (template, options) => { | ||
| const evaluatedTemplateArr = []; | ||
| const { referenceRecord, endpointParams } = options; | ||
| 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((referenceRecord[refName] ?? endpointParams[refName]), attrName)); | ||
| } | ||
| else { | ||
| evaluatedTemplateArr.push((referenceRecord[parameterName] ?? endpointParams[parameterName])); | ||
| } | ||
| currentIndex = closingBraceIndex + 1; | ||
| } | ||
| return evaluatedTemplateArr.join(""); | ||
| }; | ||
| const getReferenceValue = ({ ref }, options) => { | ||
| return options.referenceRecord[ref] ?? options.endpointParams[ref]; | ||
| }; | ||
| const evaluateExpression = (obj, keyName, options) => { | ||
| if (typeof obj === "string") { | ||
| return evaluateTemplate(obj, options); | ||
| } | ||
| else if (obj["fn"]) { | ||
| return group$2.callFunction(obj, options); | ||
| } | ||
| else if (obj["ref"]) { | ||
| return getReferenceValue(obj, options); | ||
| } | ||
| throw new EndpointError(`'${keyName}': ${String(obj)} is not a string, function or reference.`); | ||
| }; | ||
| const callFunction = ({ fn, argv }, options) => { | ||
| const evaluatedArgs = Array(argv.length); | ||
| for (let i = 0; i < evaluatedArgs.length; ++i) { | ||
| const arg = argv[i]; | ||
| if (typeof arg === "boolean" || typeof arg === "number") { | ||
| evaluatedArgs[i] = arg; | ||
| } | ||
| else { | ||
| evaluatedArgs[i] = group$2.evaluateExpression(arg, "arg", options); | ||
| } | ||
| } | ||
| const namespaceSeparatorIndex = fn.indexOf("."); | ||
| if (namespaceSeparatorIndex !== -1) { | ||
| const namespaceFunctions = customEndpointFunctions[fn.slice(0, namespaceSeparatorIndex)]; | ||
| const customFunction = namespaceFunctions?.[fn.slice(namespaceSeparatorIndex + 1)]; | ||
| if (typeof customFunction === "function") { | ||
| return customFunction(...evaluatedArgs); | ||
| } | ||
| } | ||
| const callable = endpointFunctions[fn]; | ||
| if (typeof callable === "function") { | ||
| return callable(...evaluatedArgs); | ||
| } | ||
| throw new Error(`function ${fn} not loaded in endpointFunctions.`); | ||
| }; | ||
| const group$2 = { | ||
| evaluateExpression, | ||
| callFunction, | ||
| }; | ||
| const evaluateCondition = (condition, options) => { | ||
| const { assign } = condition; | ||
| if (assign && assign in options.referenceRecord) { | ||
| throw new EndpointError(`'${assign}' is already defined in Reference Record.`); | ||
| } | ||
| const value = callFunction(condition, options); | ||
| options.logger?.debug?.(`${debugId} evaluateCondition: ${toDebugString(condition)} = ${toDebugString(value)}`); | ||
| const result = value === "" ? true : !!value; | ||
| if (assign != null) { | ||
| return { result, toAssign: { name: assign, value } }; | ||
| } | ||
| return { result }; | ||
| }; | ||
| const getEndpointHeaders = (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; | ||
| }); | ||
| return acc; | ||
| }, {}); | ||
| const getEndpointProperties = (properties, options) => Object.entries(properties).reduce((acc, [propertyKey, propertyVal]) => { | ||
| acc[propertyKey] = group$1.getEndpointProperty(propertyVal, options); | ||
| return acc; | ||
| }, {}); | ||
| const getEndpointProperty = (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 group$1.getEndpointProperties(property, options); | ||
| case "boolean": | ||
| return property; | ||
| default: | ||
| throw new EndpointError(`Unexpected endpoint property type: ${typeof property}`); | ||
| } | ||
| }; | ||
| const group$1 = { | ||
| getEndpointProperty, | ||
| getEndpointProperties, | ||
| }; | ||
| const getEndpointUrl = (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}`); | ||
| }; | ||
| const RESULT = 100_000_000; | ||
| const decideEndpoint = (bdd, options) => { | ||
| const { nodes, root, results, conditions } = bdd; | ||
| let ref = root; | ||
| const referenceRecord = {}; | ||
| const closure = { | ||
| referenceRecord, | ||
| endpointParams: options.endpointParams, | ||
| logger: options.logger, | ||
| }; | ||
| while (ref !== 1 && ref !== -1 && ref < RESULT) { | ||
| const node_i = 3 * (Math.abs(ref) - 1); | ||
| const [condition_i, highRef, lowRef] = [nodes[node_i], nodes[node_i + 1], nodes[node_i + 2]]; | ||
| const [fn, argv, assign] = conditions[condition_i]; | ||
| const evaluation = evaluateCondition({ fn, assign, argv }, closure); | ||
| if (evaluation.toAssign) { | ||
| const { name, value } = evaluation.toAssign; | ||
| referenceRecord[name] = value; | ||
| } | ||
| ref = ref >= 0 === evaluation.result ? highRef : lowRef; | ||
| } | ||
| if (ref >= RESULT) { | ||
| const result = results[ref - RESULT]; | ||
| if (result[0] === -1) { | ||
| const [, errorExpression] = result; | ||
| throw new EndpointError(evaluateExpression(errorExpression, "Error", closure)); | ||
| } | ||
| const [url, properties, headers] = result; | ||
| return { | ||
| url: getEndpointUrl(url, closure), | ||
| properties: getEndpointProperties(properties, closure), | ||
| headers: getEndpointHeaders(headers ?? {}, closure), | ||
| }; | ||
| } | ||
| throw new EndpointError(`No matching endpoint.`); | ||
| }; | ||
| const evaluateConditions = (conditions = [], options) => { | ||
| const conditionsReferenceRecord = {}; | ||
| const conditionOptions = { | ||
| ...options, | ||
| referenceRecord: { ...options.referenceRecord }, | ||
| }; | ||
| let didAssign = false; | ||
| for (const condition of conditions) { | ||
| const { result, toAssign } = evaluateCondition(condition, conditionOptions); | ||
| if (!result) { | ||
| return { result }; | ||
| } | ||
| if (toAssign) { | ||
| didAssign = true; | ||
| conditionsReferenceRecord[toAssign.name] = toAssign.value; | ||
| conditionOptions.referenceRecord[toAssign.name] = toAssign.value; | ||
| options.logger?.debug?.(`${debugId} assign: ${toAssign.name} := ${toDebugString(toAssign.value)}`); | ||
| } | ||
| } | ||
| if (didAssign) { | ||
| return { result: true, referenceRecord: conditionsReferenceRecord }; | ||
| } | ||
| return { result: true }; | ||
| }; | ||
| const evaluateEndpointRule = (endpointRule, options) => { | ||
| const { conditions, endpoint } = endpointRule; | ||
| const { result, referenceRecord } = evaluateConditions(conditions, options); | ||
| if (!result) { | ||
| return; | ||
| } | ||
| const endpointRuleOptions = referenceRecord | ||
| ? { | ||
| ...options, | ||
| referenceRecord: { ...options.referenceRecord, ...referenceRecord }, | ||
| } | ||
| : options; | ||
| const { url, properties, headers } = endpoint; | ||
| options.logger?.debug?.(`${debugId} Resolving endpoint from template: ${toDebugString(endpoint)}`); | ||
| const endpointToReturn = { url: getEndpointUrl(url, endpointRuleOptions) }; | ||
| if (headers != null) { | ||
| endpointToReturn.headers = getEndpointHeaders(headers, endpointRuleOptions); | ||
| } | ||
| if (properties != null) { | ||
| endpointToReturn.properties = getEndpointProperties(properties, endpointRuleOptions); | ||
| } | ||
| return endpointToReturn; | ||
| }; | ||
| const evaluateErrorRule = (errorRule, options) => { | ||
| const { conditions, error } = errorRule; | ||
| const { result, referenceRecord } = evaluateConditions(conditions, options); | ||
| if (!result) { | ||
| return; | ||
| } | ||
| const errorRuleOptions = referenceRecord | ||
| ? { | ||
| ...options, | ||
| referenceRecord: { ...options.referenceRecord, ...referenceRecord }, | ||
| } | ||
| : options; | ||
| throw new EndpointError(evaluateExpression(error, "Error", errorRuleOptions)); | ||
| }; | ||
| const evaluateRules = (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 = group.evaluateTreeRule(rule, options); | ||
| if (endpointOrUndefined) { | ||
| return endpointOrUndefined; | ||
| } | ||
| } | ||
| else { | ||
| throw new EndpointError(`Unknown endpoint rule: ${rule}`); | ||
| } | ||
| } | ||
| throw new EndpointError(`Rules evaluation failed`); | ||
| }; | ||
| const evaluateTreeRule = (treeRule, options) => { | ||
| const { conditions, rules } = treeRule; | ||
| const { result, referenceRecord } = evaluateConditions(conditions, options); | ||
| if (!result) { | ||
| return; | ||
| } | ||
| const treeRuleOptions = referenceRecord | ||
| ? { ...options, referenceRecord: { ...options.referenceRecord, ...referenceRecord } } | ||
| : options; | ||
| return group.evaluateRules(rules, treeRuleOptions); | ||
| }; | ||
| const group = { | ||
| evaluateRules, | ||
| evaluateTreeRule, | ||
| }; | ||
| const resolveEndpoint = (ruleSetObject, options) => { | ||
| const { endpointParams, logger } = options; | ||
| const { parameters, rules } = ruleSetObject; | ||
| options.logger?.debug?.(`${debugId} Initial EndpointParams: ${toDebugString(endpointParams)}`); | ||
| for (const paramKey in parameters) { | ||
| const parameter = parameters[paramKey]; | ||
| const endpointParam = endpointParams[paramKey]; | ||
| if (endpointParam == null && parameter.default != null) { | ||
| endpointParams[paramKey] = parameter.default; | ||
| continue; | ||
| } | ||
| if (parameter.required && endpointParam == null) { | ||
| throw new EndpointError(`Missing required parameter: '${paramKey}'`); | ||
| } | ||
| } | ||
| const endpoint = evaluateRules(rules, { endpointParams, logger, referenceRecord: {} }); | ||
| options.logger?.debug?.(`${debugId} Resolved endpoint: ${toDebugString(endpoint)}`); | ||
| return endpoint; | ||
| }; | ||
| exports.BinaryDecisionDiagram = BinaryDecisionDiagram; | ||
| exports.EndpointCache = EndpointCache; | ||
| exports.EndpointError = EndpointError; | ||
| exports.customEndpointFunctions = customEndpointFunctions; | ||
| exports.decideEndpoint = decideEndpoint; | ||
| exports.isIpAddress = isIpAddress; | ||
| exports.isValidHostLabel = isValidHostLabel; | ||
| exports.resolveEndpoint = resolveEndpoint; | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.EndpointError = exports.resolveEndpoint = exports.customEndpointFunctions = exports.isValidHostLabel = exports.isIpAddress = exports.decideEndpoint = exports.EndpointCache = exports.BinaryDecisionDiagram = void 0; | ||
| var endpoints_1 = require("@smithy/core/endpoints"); | ||
| Object.defineProperty(exports, "BinaryDecisionDiagram", { enumerable: true, get: function () { return endpoints_1.BinaryDecisionDiagram; } }); | ||
| Object.defineProperty(exports, "EndpointCache", { enumerable: true, get: function () { return endpoints_1.EndpointCache; } }); | ||
| Object.defineProperty(exports, "decideEndpoint", { enumerable: true, get: function () { return endpoints_1.decideEndpoint; } }); | ||
| Object.defineProperty(exports, "isIpAddress", { enumerable: true, get: function () { return endpoints_1.isIpAddress; } }); | ||
| Object.defineProperty(exports, "isValidHostLabel", { enumerable: true, get: function () { return endpoints_1.isValidHostLabel; } }); | ||
| Object.defineProperty(exports, "customEndpointFunctions", { enumerable: true, get: function () { return endpoints_1.customEndpointFunctions; } }); | ||
| Object.defineProperty(exports, "resolveEndpoint", { enumerable: true, get: function () { return endpoints_1.resolveEndpoint; } }); | ||
| Object.defineProperty(exports, "EndpointError", { enumerable: true, get: function () { return endpoints_1.EndpointError; } }); |
+1
-8
@@ -1,8 +0,1 @@ | ||
| export { BinaryDecisionDiagram } from "./bdd/BinaryDecisionDiagram"; | ||
| export * from "./cache/EndpointCache"; | ||
| export { decideEndpoint } from "./decideEndpoint"; | ||
| export * from "./lib/isIpAddress"; | ||
| export * from "./lib/isValidHostLabel"; | ||
| export * from "./utils/customEndpointFunctions"; | ||
| export * from "./resolveEndpoint"; | ||
| export * from "./types"; | ||
| export { BinaryDecisionDiagram, EndpointCache, decideEndpoint, isIpAddress, isValidHostLabel, customEndpointFunctions, resolveEndpoint, EndpointError, } from "@smithy/core/endpoints"; |
@@ -1,8 +0,3 @@ | ||
| export { BinaryDecisionDiagram } from "./bdd/BinaryDecisionDiagram"; | ||
| export * from "./cache/EndpointCache"; | ||
| export { decideEndpoint } from "./decideEndpoint"; | ||
| export * from "./lib/isIpAddress"; | ||
| export * from "./lib/isValidHostLabel"; | ||
| export * from "./utils/customEndpointFunctions"; | ||
| export * from "./resolveEndpoint"; | ||
| export * from "./types"; | ||
| /** @deprecated Use @smithy/core/endpoints instead. */ | ||
| export { BinaryDecisionDiagram, EndpointCache, decideEndpoint, isIpAddress, isValidHostLabel, customEndpointFunctions, resolveEndpoint, EndpointError, } from "@smithy/core/endpoints"; | ||
| export type { ConditionObject, DeprecatedObject, EndpointFunctions, EndpointObject, EndpointObjectHeaders, EndpointObjectProperties, EndpointParams, EndpointResolverOptions, EndpointRuleObject, ErrorRuleObject, EvaluateOptions, Expression, FunctionArgv, FunctionObject, FunctionReturn, ParameterObject, ReferenceObject, ReferenceRecord, RuleSetObject, RuleSetRules, TreeRuleObject, } from "@smithy/core/endpoints"; |
+5
-29
| { | ||
| "name": "@smithy/util-endpoints", | ||
| "version": "3.4.2", | ||
| "version": "3.5.0", | ||
| "description": "Utilities to help with endpoint resolution.", | ||
@@ -8,14 +8,5 @@ "main": "./dist-cjs/index.js", | ||
| "scripts": { | ||
| "build": "concurrently 'yarn:build:types' 'yarn:build:es:cjs'", | ||
| "build:es:cjs": "yarn g:tsc -p tsconfig.es.json && node ../../scripts/inline util-endpoints", | ||
| "build:types": "yarn g:tsc -p tsconfig.types.json", | ||
| "build:types:downlevel": "premove dist-types/ts3.4 && downlevel-dts dist-types dist-types/ts3.4", | ||
| "clean": "premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo", | ||
| "format": "prettier --config ../../prettier.config.js --ignore-path ../../.prettierignore --write \"**/*.{ts,md,json}\"", | ||
| "lint": "eslint -c ../../.eslintrc.js \"src/**/*.ts\"", | ||
| "stage-release": "premove .release && yarn pack && mkdir ./.release && tar zxvf ./package.tgz --directory ./.release && rm ./package.tgz", | ||
| "test": "yarn g:vitest run", | ||
| "test:integration": "yarn g:vitest run -c vitest.config.integ.mts", | ||
| "test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.mts", | ||
| "test:watch": "yarn g:vitest watch" | ||
| "build": "yarn g:tsc -p tsconfig.cjs.json && yarn g:tsc -p tsconfig.es.json && yarn g:tsc -p tsconfig.types.json", | ||
| "clean": "rm -rf dist-cjs dist-es dist-types", | ||
| "stage-release": "rm -rf .release && yarn pack && mkdir ./.release && tar zxvf ./package.tgz --directory ./.release && rm ./package.tgz" | ||
| }, | ||
@@ -32,13 +23,5 @@ "keywords": [ | ||
| "dependencies": { | ||
| "@smithy/node-config-provider": "^4.3.14", | ||
| "@smithy/types": "^4.14.1", | ||
| "@smithy/core": "^3.24.0", | ||
| "tslib": "^2.6.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^18.11.9", | ||
| "concurrently": "7.0.0", | ||
| "downlevel-dts": "0.10.1", | ||
| "premove": "4.0.0", | ||
| "typedoc": "0.23.23" | ||
| }, | ||
| "types": "./dist-types/index.d.ts", | ||
@@ -48,9 +31,2 @@ "engines": { | ||
| }, | ||
| "typesVersions": { | ||
| "<4.5": { | ||
| "types/*": [ | ||
| "types/ts3.4/*" | ||
| ] | ||
| } | ||
| }, | ||
| "files": [ | ||
@@ -57,0 +33,0 @@ "dist-*/**" |
| export class BinaryDecisionDiagram { | ||
| nodes; | ||
| root; | ||
| conditions; | ||
| results; | ||
| constructor(bdd, root, conditions, results) { | ||
| this.nodes = bdd; | ||
| this.root = root; | ||
| this.conditions = conditions; | ||
| this.results = results; | ||
| } | ||
| static from(bdd, root, conditions, results) { | ||
| return new BinaryDecisionDiagram(bdd, root, conditions, results); | ||
| } | ||
| } |
| export class EndpointCache { | ||
| capacity; | ||
| data = new Map(); | ||
| parameters = []; | ||
| constructor({ size, params }) { | ||
| this.capacity = size ?? 50; | ||
| if (params) { | ||
| this.parameters = params; | ||
| } | ||
| } | ||
| get(endpointParams, resolver) { | ||
| const key = this.hash(endpointParams); | ||
| if (key === false) { | ||
| return resolver(); | ||
| } | ||
| if (!this.data.has(key)) { | ||
| if (this.data.size > this.capacity + 10) { | ||
| const keys = this.data.keys(); | ||
| let i = 0; | ||
| while (true) { | ||
| const { value, done } = keys.next(); | ||
| this.data.delete(value); | ||
| if (done || ++i > 10) { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| this.data.set(key, resolver()); | ||
| } | ||
| return this.data.get(key); | ||
| } | ||
| size() { | ||
| return this.data.size; | ||
| } | ||
| hash(endpointParams) { | ||
| let buffer = ""; | ||
| const { parameters } = this; | ||
| if (parameters.length === 0) { | ||
| return false; | ||
| } | ||
| for (const param of parameters) { | ||
| const val = String(endpointParams[param] ?? ""); | ||
| if (val.includes("|;")) { | ||
| return false; | ||
| } | ||
| buffer += val + "|;"; | ||
| } | ||
| return buffer; | ||
| } | ||
| } |
| export const debugId = "endpoints"; |
| export * from "./debugId"; | ||
| export * from "./toDebugString"; |
| export 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); | ||
| } |
| import { EndpointError } from "./types"; | ||
| import { evaluateCondition } from "./utils/evaluateCondition"; | ||
| import { evaluateExpression } from "./utils/evaluateExpression"; | ||
| import { getEndpointHeaders } from "./utils/getEndpointHeaders"; | ||
| import { getEndpointProperties } from "./utils/getEndpointProperties"; | ||
| import { getEndpointUrl } from "./utils/getEndpointUrl"; | ||
| const RESULT = 100_000_000; | ||
| export const decideEndpoint = (bdd, options) => { | ||
| const { nodes, root, results, conditions } = bdd; | ||
| let ref = root; | ||
| const referenceRecord = {}; | ||
| const closure = { | ||
| referenceRecord, | ||
| endpointParams: options.endpointParams, | ||
| logger: options.logger, | ||
| }; | ||
| while (ref !== 1 && ref !== -1 && ref < RESULT) { | ||
| const node_i = 3 * (Math.abs(ref) - 1); | ||
| const [condition_i, highRef, lowRef] = [nodes[node_i], nodes[node_i + 1], nodes[node_i + 2]]; | ||
| const [fn, argv, assign] = conditions[condition_i]; | ||
| const evaluation = evaluateCondition({ fn, assign, argv }, closure); | ||
| if (evaluation.toAssign) { | ||
| const { name, value } = evaluation.toAssign; | ||
| referenceRecord[name] = value; | ||
| } | ||
| ref = ref >= 0 === evaluation.result ? highRef : lowRef; | ||
| } | ||
| if (ref >= RESULT) { | ||
| const result = results[ref - RESULT]; | ||
| if (result[0] === -1) { | ||
| const [, errorExpression] = result; | ||
| throw new EndpointError(evaluateExpression(errorExpression, "Error", closure)); | ||
| } | ||
| const [url, properties, headers] = result; | ||
| return { | ||
| url: getEndpointUrl(url, closure), | ||
| properties: getEndpointProperties(properties, closure), | ||
| headers: getEndpointHeaders(headers ?? {}, closure), | ||
| }; | ||
| } | ||
| throw new EndpointError(`No matching endpoint.`); | ||
| }; |
| const ENV_ENDPOINT_URL = "AWS_ENDPOINT_URL"; | ||
| const CONFIG_ENDPOINT_URL = "endpoint_url"; | ||
| export const getEndpointUrlConfig = (serviceId) => ({ | ||
| environmentVariableSelector: (env) => { | ||
| const serviceEndpointUrlSections = [ENV_ENDPOINT_URL, ...serviceId.split(" ").map((w) => w.toUpperCase())]; | ||
| const serviceEndpointUrl = env[serviceEndpointUrlSections.join("_")]; | ||
| if (serviceEndpointUrl) | ||
| return serviceEndpointUrl; | ||
| const endpointUrl = env[ENV_ENDPOINT_URL]; | ||
| if (endpointUrl) | ||
| return endpointUrl; | ||
| return undefined; | ||
| }, | ||
| configFileSelector: (profile) => { | ||
| const endpointUrl = profile[CONFIG_ENDPOINT_URL]; | ||
| if (endpointUrl) | ||
| return endpointUrl; | ||
| return undefined; | ||
| }, | ||
| default: undefined, | ||
| }); |
| export const booleanEquals = (value1, value2) => value1 === value2; |
| export function coalesce(...args) { | ||
| for (const arg of args) { | ||
| if (arg != null) { | ||
| return arg; | ||
| } | ||
| } | ||
| return undefined; | ||
| } |
| import { EndpointError } from "../types"; | ||
| import { getAttrPathList } from "./getAttrPathList"; | ||
| export const getAttr = (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)) { | ||
| const i = parseInt(index); | ||
| return acc[i < 0 ? acc.length + i : i]; | ||
| } | ||
| return acc[index]; | ||
| }, value); |
| import { EndpointError } from "../types"; | ||
| export const getAttrPathList = (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; | ||
| }; |
| export * from "./booleanEquals"; | ||
| export * from "./coalesce"; | ||
| export * from "./getAttr"; | ||
| export * from "./isSet"; | ||
| export * from "./isValidHostLabel"; | ||
| export * from "./ite"; | ||
| export * from "./not"; | ||
| export * from "./parseURL"; | ||
| export * from "./split"; | ||
| export * from "./stringEquals"; | ||
| export * from "./substring"; | ||
| export * from "./uriEncode"; |
| const 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}$`); | ||
| export const isIpAddress = (value) => IP_V4_REGEX.test(value) || (value.startsWith("[") && value.endsWith("]")); |
| export const isSet = (value) => value != null; |
| const VALID_HOST_LABEL_REGEX = new RegExp(`^(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$`); | ||
| export const isValidHostLabel = (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; | ||
| }; |
| export function ite(condition, trueValue, falseValue) { | ||
| return condition ? trueValue : falseValue; | ||
| } |
| export const not = (value) => !value; |
| import { EndpointURLScheme } from "@smithy/types"; | ||
| import { isIpAddress } from "./isIpAddress"; | ||
| const DEFAULT_PORTS = { | ||
| [EndpointURLScheme.HTTP]: 80, | ||
| [EndpointURLScheme.HTTPS]: 443, | ||
| }; | ||
| export const parseURL = (value) => { | ||
| const whatwgURL = (() => { | ||
| try { | ||
| if (value instanceof URL) { | ||
| return value; | ||
| } | ||
| if (typeof value === "object" && "hostname" in value) { | ||
| const { hostname, port, protocol = "", path = "", query = {} } = value; | ||
| const url = new URL(`${protocol}//${hostname}${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(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, | ||
| }; | ||
| }; |
| export function split(value, delimiter, limit) { | ||
| if (limit === 1) { | ||
| return [value]; | ||
| } | ||
| if (value === "") { | ||
| return [""]; | ||
| } | ||
| const parts = value.split(delimiter); | ||
| if (limit === 0) { | ||
| return parts; | ||
| } | ||
| return parts.slice(0, limit - 1).concat(parts.slice(1).join(delimiter)); | ||
| } |
| export const stringEquals = (value1, value2) => value1 === value2; |
| export const substring = (input, start, stop, reverse) => { | ||
| if (input == null || start >= stop || input.length < stop || /[^\u0000-\u007f]/.test(input)) { | ||
| return null; | ||
| } | ||
| if (!reverse) { | ||
| return input.substring(start, stop); | ||
| } | ||
| return input.substring(input.length - stop, input.length - start); | ||
| }; |
| export const uriEncode = (value) => encodeURIComponent(value).replace(/[!*'()]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`); |
| import { debugId, toDebugString } from "./debug"; | ||
| import { EndpointError } from "./types"; | ||
| import { evaluateRules } from "./utils"; | ||
| export const resolveEndpoint = (ruleSetObject, options) => { | ||
| const { endpointParams, logger } = options; | ||
| const { parameters, rules } = ruleSetObject; | ||
| options.logger?.debug?.(`${debugId} Initial EndpointParams: ${toDebugString(endpointParams)}`); | ||
| for (const paramKey in parameters) { | ||
| const parameter = parameters[paramKey]; | ||
| const endpointParam = endpointParams[paramKey]; | ||
| if (endpointParam == null && parameter.default != null) { | ||
| endpointParams[paramKey] = parameter.default; | ||
| continue; | ||
| } | ||
| if (parameter.required && endpointParam == null) { | ||
| throw new EndpointError(`Missing required parameter: '${paramKey}'`); | ||
| } | ||
| } | ||
| const endpoint = evaluateRules(rules, { endpointParams, logger, referenceRecord: {} }); | ||
| options.logger?.debug?.(`${debugId} Resolved endpoint: ${toDebugString(endpoint)}`); | ||
| return endpoint; | ||
| }; |
| export class EndpointError extends Error { | ||
| constructor(message) { | ||
| super(message); | ||
| this.name = "EndpointError"; | ||
| } | ||
| } |
| export {}; |
| export {}; |
| export {}; |
| export * from "./EndpointError"; | ||
| export * from "./EndpointFunctions"; | ||
| export * from "./EndpointRuleObject"; | ||
| export * from "./ErrorRuleObject"; | ||
| export * from "./RuleSetObject"; | ||
| export * from "./TreeRuleObject"; | ||
| export * from "./shared"; |
| export {}; |
| export {}; |
| export {}; |
| export { callFunction } from "./evaluateExpression"; |
| export const customEndpointFunctions = {}; |
| import { booleanEquals, coalesce, getAttr, isSet, isValidHostLabel, ite, not, parseURL, split, stringEquals, substring, uriEncode, } from "../lib"; | ||
| export const endpointFunctions = { | ||
| booleanEquals, | ||
| coalesce, | ||
| getAttr, | ||
| isSet, | ||
| isValidHostLabel, | ||
| ite, | ||
| not, | ||
| parseURL, | ||
| split, | ||
| stringEquals, | ||
| substring, | ||
| uriEncode, | ||
| }; |
| import { debugId, toDebugString } from "../debug"; | ||
| import { EndpointError } from "../types"; | ||
| import { callFunction } from "./callFunction"; | ||
| export const evaluateCondition = (condition, options) => { | ||
| const { assign } = condition; | ||
| if (assign && assign in options.referenceRecord) { | ||
| throw new EndpointError(`'${assign}' is already defined in Reference Record.`); | ||
| } | ||
| const value = callFunction(condition, options); | ||
| options.logger?.debug?.(`${debugId} evaluateCondition: ${toDebugString(condition)} = ${toDebugString(value)}`); | ||
| const result = value === "" ? true : !!value; | ||
| if (assign != null) { | ||
| return { result, toAssign: { name: assign, value } }; | ||
| } | ||
| return { result }; | ||
| }; |
| import { debugId, toDebugString } from "../debug"; | ||
| import { evaluateCondition } from "./evaluateCondition"; | ||
| export const evaluateConditions = (conditions = [], options) => { | ||
| const conditionsReferenceRecord = {}; | ||
| const conditionOptions = { | ||
| ...options, | ||
| referenceRecord: { ...options.referenceRecord }, | ||
| }; | ||
| let didAssign = false; | ||
| for (const condition of conditions) { | ||
| const { result, toAssign } = evaluateCondition(condition, conditionOptions); | ||
| if (!result) { | ||
| return { result }; | ||
| } | ||
| if (toAssign) { | ||
| didAssign = true; | ||
| conditionsReferenceRecord[toAssign.name] = toAssign.value; | ||
| conditionOptions.referenceRecord[toAssign.name] = toAssign.value; | ||
| options.logger?.debug?.(`${debugId} assign: ${toAssign.name} := ${toDebugString(toAssign.value)}`); | ||
| } | ||
| } | ||
| if (didAssign) { | ||
| return { result: true, referenceRecord: conditionsReferenceRecord }; | ||
| } | ||
| return { result: true }; | ||
| }; |
| import { debugId, toDebugString } from "../debug"; | ||
| import { evaluateConditions } from "./evaluateConditions"; | ||
| import { getEndpointHeaders } from "./getEndpointHeaders"; | ||
| import { getEndpointProperties } from "./getEndpointProperties"; | ||
| import { getEndpointUrl } from "./getEndpointUrl"; | ||
| export const evaluateEndpointRule = (endpointRule, options) => { | ||
| const { conditions, endpoint } = endpointRule; | ||
| const { result, referenceRecord } = evaluateConditions(conditions, options); | ||
| if (!result) { | ||
| return; | ||
| } | ||
| const endpointRuleOptions = referenceRecord | ||
| ? { | ||
| ...options, | ||
| referenceRecord: { ...options.referenceRecord, ...referenceRecord }, | ||
| } | ||
| : options; | ||
| const { url, properties, headers } = endpoint; | ||
| options.logger?.debug?.(`${debugId} Resolving endpoint from template: ${toDebugString(endpoint)}`); | ||
| const endpointToReturn = { url: getEndpointUrl(url, endpointRuleOptions) }; | ||
| if (headers != null) { | ||
| endpointToReturn.headers = getEndpointHeaders(headers, endpointRuleOptions); | ||
| } | ||
| if (properties != null) { | ||
| endpointToReturn.properties = getEndpointProperties(properties, endpointRuleOptions); | ||
| } | ||
| return endpointToReturn; | ||
| }; |
| import { EndpointError } from "../types"; | ||
| import { evaluateConditions } from "./evaluateConditions"; | ||
| import { evaluateExpression } from "./evaluateExpression"; | ||
| export const evaluateErrorRule = (errorRule, options) => { | ||
| const { conditions, error } = errorRule; | ||
| const { result, referenceRecord } = evaluateConditions(conditions, options); | ||
| if (!result) { | ||
| return; | ||
| } | ||
| const errorRuleOptions = referenceRecord | ||
| ? { | ||
| ...options, | ||
| referenceRecord: { ...options.referenceRecord, ...referenceRecord }, | ||
| } | ||
| : options; | ||
| throw new EndpointError(evaluateExpression(error, "Error", errorRuleOptions)); | ||
| }; |
| import { EndpointError } from "../types"; | ||
| import { customEndpointFunctions } from "./customEndpointFunctions"; | ||
| import { endpointFunctions } from "./endpointFunctions"; | ||
| import { evaluateTemplate } from "./evaluateTemplate"; | ||
| import { getReferenceValue } from "./getReferenceValue"; | ||
| export const evaluateExpression = (obj, keyName, options) => { | ||
| if (typeof obj === "string") { | ||
| return evaluateTemplate(obj, options); | ||
| } | ||
| else if (obj["fn"]) { | ||
| return group.callFunction(obj, options); | ||
| } | ||
| else if (obj["ref"]) { | ||
| return getReferenceValue(obj, options); | ||
| } | ||
| throw new EndpointError(`'${keyName}': ${String(obj)} is not a string, function or reference.`); | ||
| }; | ||
| export const callFunction = ({ fn, argv }, options) => { | ||
| const evaluatedArgs = Array(argv.length); | ||
| for (let i = 0; i < evaluatedArgs.length; ++i) { | ||
| const arg = argv[i]; | ||
| if (typeof arg === "boolean" || typeof arg === "number") { | ||
| evaluatedArgs[i] = arg; | ||
| } | ||
| else { | ||
| evaluatedArgs[i] = group.evaluateExpression(arg, "arg", options); | ||
| } | ||
| } | ||
| const namespaceSeparatorIndex = fn.indexOf("."); | ||
| if (namespaceSeparatorIndex !== -1) { | ||
| const namespaceFunctions = customEndpointFunctions[fn.slice(0, namespaceSeparatorIndex)]; | ||
| const customFunction = namespaceFunctions?.[fn.slice(namespaceSeparatorIndex + 1)]; | ||
| if (typeof customFunction === "function") { | ||
| return customFunction(...evaluatedArgs); | ||
| } | ||
| } | ||
| const callable = endpointFunctions[fn]; | ||
| if (typeof callable === "function") { | ||
| return callable(...evaluatedArgs); | ||
| } | ||
| throw new Error(`function ${fn} not loaded in endpointFunctions.`); | ||
| }; | ||
| export const group = { | ||
| evaluateExpression, | ||
| callFunction, | ||
| }; |
| import { EndpointError } from "../types"; | ||
| import { evaluateConditions } from "./evaluateConditions"; | ||
| import { evaluateEndpointRule } from "./evaluateEndpointRule"; | ||
| import { evaluateErrorRule } from "./evaluateErrorRule"; | ||
| export const evaluateRules = (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 = group.evaluateTreeRule(rule, options); | ||
| if (endpointOrUndefined) { | ||
| return endpointOrUndefined; | ||
| } | ||
| } | ||
| else { | ||
| throw new EndpointError(`Unknown endpoint rule: ${rule}`); | ||
| } | ||
| } | ||
| throw new EndpointError(`Rules evaluation failed`); | ||
| }; | ||
| export const evaluateTreeRule = (treeRule, options) => { | ||
| const { conditions, rules } = treeRule; | ||
| const { result, referenceRecord } = evaluateConditions(conditions, options); | ||
| if (!result) { | ||
| return; | ||
| } | ||
| const treeRuleOptions = referenceRecord | ||
| ? { ...options, referenceRecord: { ...options.referenceRecord, ...referenceRecord } } | ||
| : options; | ||
| return group.evaluateRules(rules, treeRuleOptions); | ||
| }; | ||
| export const group = { | ||
| evaluateRules, | ||
| evaluateTreeRule, | ||
| }; |
| import { getAttr } from "../lib"; | ||
| export const evaluateTemplate = (template, options) => { | ||
| const evaluatedTemplateArr = []; | ||
| const { referenceRecord, endpointParams } = options; | ||
| 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((referenceRecord[refName] ?? endpointParams[refName]), attrName)); | ||
| } | ||
| else { | ||
| evaluatedTemplateArr.push((referenceRecord[parameterName] ?? endpointParams[parameterName])); | ||
| } | ||
| currentIndex = closingBraceIndex + 1; | ||
| } | ||
| return evaluatedTemplateArr.join(""); | ||
| }; |
| export { evaluateTreeRule } from "./evaluateRules"; |
| import { EndpointError } from "../types"; | ||
| import { evaluateExpression } from "./evaluateExpression"; | ||
| export const getEndpointHeaders = (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; | ||
| }); | ||
| return acc; | ||
| }, {}); |
| import { EndpointError } from "../types"; | ||
| import { evaluateTemplate } from "./evaluateTemplate"; | ||
| export const getEndpointProperties = (properties, options) => Object.entries(properties).reduce((acc, [propertyKey, propertyVal]) => { | ||
| acc[propertyKey] = group.getEndpointProperty(propertyVal, options); | ||
| return acc; | ||
| }, {}); | ||
| export const getEndpointProperty = (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 group.getEndpointProperties(property, options); | ||
| case "boolean": | ||
| return property; | ||
| default: | ||
| throw new EndpointError(`Unexpected endpoint property type: ${typeof property}`); | ||
| } | ||
| }; | ||
| export const group = { | ||
| getEndpointProperty, | ||
| getEndpointProperties, | ||
| }; |
| export { getEndpointProperty } from "./getEndpointProperties"; |
| import { EndpointError } from "../types"; | ||
| import { evaluateExpression } from "./evaluateExpression"; | ||
| export const getEndpointUrl = (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}`); | ||
| }; |
| export const getReferenceValue = ({ ref }, options) => { | ||
| return options.referenceRecord[ref] ?? options.endpointParams[ref]; | ||
| }; |
| export * from "./customEndpointFunctions"; | ||
| export * from "./evaluateRules"; |
| import type { EndpointObjectHeaders, ParameterObject } from "@smithy/types"; | ||
| import type { Expression, FunctionArgv } from "../types/shared"; | ||
| /** | ||
| * @internal | ||
| */ | ||
| type BddCondition = [string, FunctionArgv] | [string, FunctionArgv, string]; | ||
| /** | ||
| * @internal | ||
| */ | ||
| type BddResult = [-1] | [-1, Expression] | [string, Record<string, ParameterObject>, EndpointObjectHeaders] | [string, Record<string, ParameterObject>]; | ||
| /** | ||
| * @internal | ||
| */ | ||
| export declare class BinaryDecisionDiagram { | ||
| nodes: Int32Array; | ||
| root: number; | ||
| conditions: BddCondition[]; | ||
| results: BddResult[]; | ||
| private constructor(); | ||
| static from(bdd: Int32Array, root: number, conditions: BddCondition[] | any[], results: BddResult[] | any[]): BinaryDecisionDiagram; | ||
| } | ||
| export {}; |
| import type { EndpointParams, EndpointV2 } from "@smithy/types"; | ||
| /** | ||
| * @internal | ||
| * | ||
| * Cache for endpoint ruleSet resolution. | ||
| */ | ||
| export declare class EndpointCache { | ||
| private capacity; | ||
| private data; | ||
| private parameters; | ||
| /** | ||
| * @param [size] - desired average maximum capacity. A buffer of 10 additional keys will be allowed | ||
| * before keys are dropped. | ||
| * @param [params] - list of params to consider as part of the cache key. | ||
| * | ||
| * If the params list is not populated, no caching will happen. | ||
| * This may be out of order depending on how the object is created and arrives to this class. | ||
| */ | ||
| constructor({ size, params }: { | ||
| size?: number; | ||
| params?: string[]; | ||
| }); | ||
| /** | ||
| * @param endpointParams - query for endpoint. | ||
| * @param resolver - provider of the value if not present. | ||
| * @returns endpoint corresponding to the query. | ||
| */ | ||
| get(endpointParams: EndpointParams, resolver: () => EndpointV2): EndpointV2; | ||
| size(): number; | ||
| /** | ||
| * @returns cache key or false if not cachable. | ||
| */ | ||
| private hash; | ||
| } |
| export declare const debugId = "endpoints"; |
| export * from "./debugId"; | ||
| export * from "./toDebugString"; |
| import type { EndpointParameters, EndpointV2 } from "@smithy/types"; | ||
| import type { GetAttrValue } from "../lib"; | ||
| import type { EndpointObject, FunctionObject, FunctionReturn } from "../types"; | ||
| export declare function toDebugString(input: EndpointParameters): string; | ||
| export declare function toDebugString(input: EndpointV2): string; | ||
| export declare function toDebugString(input: GetAttrValue): string; | ||
| export declare function toDebugString(input: FunctionObject): string; | ||
| export declare function toDebugString(input: FunctionReturn): string; | ||
| export declare function toDebugString(input: EndpointObject): string; |
| import type { EndpointV2 } from "@smithy/types"; | ||
| import type { BinaryDecisionDiagram } from "./bdd/BinaryDecisionDiagram"; | ||
| import type { EndpointResolverOptions } from "./types"; | ||
| /** | ||
| * Resolves an endpoint URL by processing the endpoints bdd and options. | ||
| */ | ||
| export declare const decideEndpoint: (bdd: BinaryDecisionDiagram, options: EndpointResolverOptions) => EndpointV2; |
| import type { LoadedConfigSelectors } from "@smithy/node-config-provider"; | ||
| export declare const getEndpointUrlConfig: (serviceId: string) => LoadedConfigSelectors<string | undefined>; |
| /** | ||
| * Evaluates two boolean values value1 and value2 for equality and returns | ||
| * true if both values match. | ||
| */ | ||
| export declare const booleanEquals: (value1: boolean, value2: boolean) => boolean; |
| /** | ||
| * Evaluates arguments in order and returns the first non-empty result, | ||
| * otherwise returns the result of the last argument. | ||
| * | ||
| * @internal | ||
| */ | ||
| export declare function coalesce<T>(...args: (T | undefined)[]): T | undefined; |
| export type GetAttrValue = string | boolean | { | ||
| [key: string]: GetAttrValue; | ||
| } | Array<GetAttrValue>; | ||
| /** | ||
| * Returns value corresponding to pathing string for an array or object. | ||
| */ | ||
| export declare const getAttr: (value: GetAttrValue, path: string) => GetAttrValue; |
| /** | ||
| * Parses path as a getAttr expression, returning a list of strings. | ||
| */ | ||
| export declare const getAttrPathList: (path: string) => Array<string>; |
| export * from "./booleanEquals"; | ||
| export * from "./coalesce"; | ||
| export * from "./getAttr"; | ||
| export * from "./isSet"; | ||
| export * from "./isValidHostLabel"; | ||
| export * from "./ite"; | ||
| export * from "./not"; | ||
| export * from "./parseURL"; | ||
| export * from "./split"; | ||
| export * from "./stringEquals"; | ||
| export * from "./substring"; | ||
| export * from "./uriEncode"; |
| /** | ||
| * Validates if the provided value is an IP address. | ||
| */ | ||
| export declare const isIpAddress: (value: string) => boolean; |
| /** | ||
| * Evaluates whether a value is set (aka not null or undefined). | ||
| * Returns true if the value is set, otherwise returns false. | ||
| */ | ||
| export declare const isSet: (value: unknown) => value is {}; |
| /** | ||
| * Evaluates whether one or more string values are valid host labels per RFC 1123. | ||
| * | ||
| * If allowSubDomains is true, then the provided value may be zero or more dotted | ||
| * subdomains which are each validated per RFC 1123. | ||
| */ | ||
| export declare const isValidHostLabel: (value: string, allowSubDomains?: boolean) => boolean; |
| /** | ||
| * An if-then-else function that returns one of two values based on a boolean condition. | ||
| * | ||
| * @internal | ||
| */ | ||
| export declare function ite<T>(condition: boolean, trueValue: T | undefined, falseValue: T | undefined): T | undefined; |
| /** | ||
| * Performs logical negation on the provided boolean value, | ||
| * returning the negated value. | ||
| */ | ||
| export declare const not: (value: boolean) => boolean; |
| import type { Endpoint, EndpointURL } from "@smithy/types"; | ||
| /** | ||
| * Parses a string, URL, or Endpoint into it’s Endpoint URL components. | ||
| */ | ||
| export declare const parseURL: (value: string | URL | Endpoint) => EndpointURL | null; |
| /** | ||
| * The split function divides a string into an array of substrings based on a non-empty delimiter. | ||
| * The behavior is controlled by the limit parameter: | ||
| * | ||
| * limit = 0: Split all occurrences (unlimited). | ||
| * limit = 1: No split performed (returns original string as single element array). | ||
| * limit > 1: Split into at most 'limit' parts (performs limit-1 splits). | ||
| * | ||
| * @internal | ||
| */ | ||
| export declare function split(value: string, delimiter: string, limit: number): string[]; |
| /** | ||
| * Evaluates two string values value1 and value2 for equality and returns | ||
| * true if both values match. | ||
| */ | ||
| export declare const stringEquals: (value1: string, value2: string) => boolean; |
| /** | ||
| * Computes the substring of a given string, conditionally indexing from the end of the string. | ||
| * When the string is long enough to fully include the substring, return the substring. | ||
| * Otherwise, return None. The start index is inclusive and the stop index is exclusive. | ||
| * The length of the returned string will always be stop-start. | ||
| */ | ||
| export declare const substring: (input: string, start: number, stop: number, reverse: boolean) => string | null; |
| /** | ||
| * Performs percent-encoding per RFC3986 section 2.1 | ||
| */ | ||
| export declare const uriEncode: (value: string) => string; |
| import type { EndpointV2 } from "@smithy/types"; | ||
| import type { EndpointResolverOptions, RuleSetObject } from "./types"; | ||
| /** | ||
| * Resolves an endpoint URL by processing the endpoints ruleset and options. | ||
| */ | ||
| export declare const resolveEndpoint: (ruleSetObject: RuleSetObject, options: EndpointResolverOptions) => EndpointV2; |
| export declare class EndpointError extends Error { | ||
| constructor(message: string); | ||
| } |
| import type { FunctionReturn } from "./shared"; | ||
| export type EndpointFunctions = Record<string, (...args: any[]) => FunctionReturn>; |
| import type { EndpointObject as __EndpointObject, EndpointObjectHeaders as __EndpointObjectHeaders, EndpointObjectProperties as __EndpointObjectProperties, EndpointRuleObject as __EndpointRuleObject } from "@smithy/types"; | ||
| export type EndpointObjectProperties = __EndpointObjectProperties; | ||
| export type EndpointObjectHeaders = __EndpointObjectHeaders; | ||
| export type EndpointObject = __EndpointObject; | ||
| export type EndpointRuleObject = __EndpointRuleObject; |
| import type { ErrorRuleObject as __ErrorRuleObject } from "@smithy/types"; | ||
| export type ErrorRuleObject = __ErrorRuleObject; |
| export * from "./EndpointError"; | ||
| export * from "./EndpointFunctions"; | ||
| export * from "./EndpointRuleObject"; | ||
| export * from "./ErrorRuleObject"; | ||
| export * from "./RuleSetObject"; | ||
| export * from "./TreeRuleObject"; | ||
| export * from "./shared"; |
| import type { DeprecatedObject as __DeprecatedObject, ParameterObject as __ParameterObject, RuleSetObject as __RuleSetObject } from "@smithy/types"; | ||
| export type DeprecatedObject = __DeprecatedObject; | ||
| export type ParameterObject = __ParameterObject; | ||
| export type RuleSetObject = __RuleSetObject; |
| import type { EndpointARN, EndpointPartition, EndpointURL, Logger } from "@smithy/types"; | ||
| export type ReferenceObject = { | ||
| ref: string; | ||
| }; | ||
| export type FunctionObject = { | ||
| fn: string; | ||
| argv: FunctionArgv; | ||
| }; | ||
| export type FunctionArgv = Array<Expression | boolean | number>; | ||
| export type FunctionReturn = string | boolean | number | EndpointARN | EndpointPartition | EndpointURL | { | ||
| [key: string]: FunctionReturn; | ||
| } | Array<FunctionReturn> | null; | ||
| export type ConditionObject = FunctionObject & { | ||
| assign?: string; | ||
| }; | ||
| export type Expression = string | ReferenceObject | FunctionObject; | ||
| export type EndpointParams = Record<string, string | boolean>; | ||
| export type EndpointResolverOptions = { | ||
| endpointParams: EndpointParams; | ||
| logger?: Logger; | ||
| }; | ||
| export type ReferenceRecord = Record<string, FunctionReturn>; | ||
| export type EvaluateOptions = EndpointResolverOptions & { | ||
| referenceRecord: ReferenceRecord; | ||
| }; |
| import type { RuleSetRules as __RuleSetRules, TreeRuleObject as __TreeRuleObject } from "@smithy/types"; | ||
| export type RuleSetRules = __RuleSetRules; | ||
| export type TreeRuleObject = __TreeRuleObject; |
| export { callFunction } from "./evaluateExpression"; |
| import type { EndpointFunctions } from "../types/EndpointFunctions"; | ||
| export declare const customEndpointFunctions: { | ||
| [key: string]: EndpointFunctions; | ||
| }; |
| import type { EndpointFunctions } from "../types"; | ||
| export declare const endpointFunctions: EndpointFunctions; |
| import type { ConditionObject, EvaluateOptions } from "../types"; | ||
| export declare const evaluateCondition: (condition: ConditionObject, options: EvaluateOptions) => { | ||
| result: boolean; | ||
| toAssign: { | ||
| name: string; | ||
| value: import("../types").FunctionReturn; | ||
| }; | ||
| } | { | ||
| result: boolean; | ||
| toAssign?: undefined; | ||
| }; |
| import type { ConditionObject, EvaluateOptions, FunctionReturn } from "../types"; | ||
| export declare const evaluateConditions: (conditions: ConditionObject[] | undefined, options: EvaluateOptions) => { | ||
| result: boolean; | ||
| referenceRecord: Record<string, FunctionReturn>; | ||
| } | { | ||
| result: boolean; | ||
| referenceRecord?: undefined; | ||
| }; |
| import type { EndpointV2 } from "@smithy/types"; | ||
| import type { EndpointRuleObject, EvaluateOptions } from "../types"; | ||
| export declare const evaluateEndpointRule: (endpointRule: EndpointRuleObject, options: EvaluateOptions) => EndpointV2 | undefined; |
| import type { ErrorRuleObject, EvaluateOptions } from "../types"; | ||
| export declare const evaluateErrorRule: (errorRule: ErrorRuleObject, options: EvaluateOptions) => void; |
| import type { EvaluateOptions, Expression, FunctionObject, FunctionReturn } from "../types"; | ||
| export declare const evaluateExpression: (obj: Expression, keyName: string, options: EvaluateOptions) => FunctionReturn; | ||
| export declare const callFunction: ({ fn, argv }: FunctionObject, options: EvaluateOptions) => FunctionReturn; | ||
| export declare const group: { | ||
| evaluateExpression: (obj: Expression, keyName: string, options: EvaluateOptions) => FunctionReturn; | ||
| callFunction: ({ fn, argv }: FunctionObject, options: EvaluateOptions) => FunctionReturn; | ||
| }; |
| import type { EndpointV2 } from "@smithy/types"; | ||
| import type { EvaluateOptions, RuleSetRules, TreeRuleObject } from "../types"; | ||
| export declare const evaluateRules: (rules: RuleSetRules, options: EvaluateOptions) => EndpointV2; | ||
| export declare const evaluateTreeRule: (treeRule: TreeRuleObject, options: EvaluateOptions) => EndpointV2 | undefined; | ||
| export declare const group: { | ||
| evaluateRules: (rules: RuleSetRules, options: EvaluateOptions) => EndpointV2; | ||
| evaluateTreeRule: (treeRule: TreeRuleObject, options: EvaluateOptions) => EndpointV2 | undefined; | ||
| }; |
| import type { EvaluateOptions } from "../types"; | ||
| export declare const evaluateTemplate: (template: string, options: EvaluateOptions) => string; |
| export { evaluateTreeRule } from "./evaluateRules"; |
| import type { EndpointObjectHeaders, EvaluateOptions } from "../types"; | ||
| export declare const getEndpointHeaders: (headers: EndpointObjectHeaders, options: EvaluateOptions) => Record<string, string[]>; |
| import type { EndpointObjectProperty } from "@smithy/types"; | ||
| import type { EndpointObjectProperties, EvaluateOptions } from "../types"; | ||
| export declare const getEndpointProperties: (properties: EndpointObjectProperties, options: EvaluateOptions) => Record<string, EndpointObjectProperty>; | ||
| export declare const getEndpointProperty: (property: EndpointObjectProperty, options: EvaluateOptions) => EndpointObjectProperty; | ||
| export declare const group: { | ||
| getEndpointProperty: (property: EndpointObjectProperty, options: EvaluateOptions) => EndpointObjectProperty; | ||
| getEndpointProperties: (properties: EndpointObjectProperties, options: EvaluateOptions) => Record<string, EndpointObjectProperty>; | ||
| }; |
| export { getEndpointProperty } from "./getEndpointProperties"; |
| import type { EvaluateOptions, Expression } from "../types"; | ||
| export declare const getEndpointUrl: (endpointUrl: Expression, options: EvaluateOptions) => URL; |
| import type { EvaluateOptions, ReferenceObject } from "../types"; | ||
| export declare const getReferenceValue: ({ ref }: ReferenceObject, options: EvaluateOptions) => string | number | boolean | import("@smithy/types").EndpointPartition | import("@smithy/types").EndpointARN | import("@smithy/types").EndpointURL | { | ||
| [key: string]: import("../types").FunctionReturn; | ||
| } | import("../types").FunctionReturn[]; |
| export * from "./customEndpointFunctions"; | ||
| export * from "./evaluateRules"; |
-17
| # @smithy/util-endpoints | ||
| [](https://www.npmjs.com/package/@smithy/util-endpoints) | ||
| [](https://www.npmjs.com/package/@smithy/util-endpoints) | ||
| ### :warning: Internal API :warning: | ||
| > This is an internal package. | ||
| > That means this is used as a dependency for other, public packages, but | ||
| > should not be taken directly as a dependency in your application's `package.json`. | ||
| > If you are updating the version of this package, for example to bring in a | ||
| > bug-fix, you should do so by updating your application lockfile with | ||
| > e.g. `npm up @scope/package` or equivalent command in another | ||
| > package manager, rather than taking a direct dependency. | ||
| --- |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
2
-33.33%0
-100%14883
-79.32%5
-95.1%17
-98.85%1
Infinity%0
-100%1
Infinity%+ Added
- Removed
- Removed
- Removed