UNPKG

awscdk-construct-scte-scheduler

Version:

AWS CDK Construct for scheduling SCTE-35 events using the MediaLive schedule API

893 lines (845 loc) 33.3 kB
'use strict'; var config = require('@smithy/core/config'); var protocols = require('@smithy/core/protocols'); var client = require('@smithy/core/client'); var types = require('@smithy/types'); const ENV_ENDPOINT_URL = "AWS_ENDPOINT_URL"; const CONFIG_ENDPOINT_URL = "endpoint_url"; const getEndpointUrlConfig = (serviceId) => ({ environmentVariableSelector: (env) => { const serviceSuffixParts = serviceId.split(" ").map((w) => w.toUpperCase()); const serviceEndpointUrl = env[[ENV_ENDPOINT_URL, ...serviceSuffixParts].join("_")]; if (serviceEndpointUrl) return serviceEndpointUrl; const endpointUrl = env[ENV_ENDPOINT_URL]; if (endpointUrl) return endpointUrl; return undefined; }, configFileSelector: (profile, config$1) => { if (config$1 && profile.services) { const servicesSection = config$1[["services", profile.services].join(config.CONFIG_PREFIX_SEPARATOR)]; if (servicesSection) { const servicePrefixParts = serviceId.split(" ").map((w) => w.toLowerCase()); const endpointUrl = servicesSection[[servicePrefixParts.join("_"), CONFIG_ENDPOINT_URL].join(config.CONFIG_PREFIX_SEPARATOR)]; if (endpointUrl) return endpointUrl; } } const endpointUrl = profile[CONFIG_ENDPOINT_URL]; if (endpointUrl) return endpointUrl; return undefined; }, default: undefined, }); const getEndpointFromConfig = async (serviceId) => config.loadConfig(getEndpointUrlConfig(serviceId ?? ""))(); const resolveParamsForS3 = async (endpointParams) => { const bucket = endpointParams?.Bucket || ""; if (typeof endpointParams.Bucket === "string") { endpointParams.Bucket = bucket.replace(/#/g, encodeURIComponent("#")).replace(/\?/g, encodeURIComponent("?")); } if (isArnBucketName(bucket)) { if (endpointParams.ForcePathStyle === true) { throw new Error("Path-style addressing cannot be used with ARN buckets"); } } else if (!isDnsCompatibleBucketName(bucket) || (bucket.indexOf(".") !== -1 && !String(endpointParams.Endpoint).startsWith("http:")) || bucket.toLowerCase() !== bucket || bucket.length < 3) { endpointParams.ForcePathStyle = true; } if (endpointParams.DisableMultiRegionAccessPoints) { endpointParams.disableMultiRegionAccessPoints = true; endpointParams.DisableMRAP = true; } return endpointParams; }; const DOMAIN_PATTERN = /^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$/; const IP_ADDRESS_PATTERN = /(\d+\.){3}\d+/; const DOTS_PATTERN = /\.\./; const isDnsCompatibleBucketName = (bucketName) => DOMAIN_PATTERN.test(bucketName) && !IP_ADDRESS_PATTERN.test(bucketName) && !DOTS_PATTERN.test(bucketName); const isArnBucketName = (bucketName) => { const [arn, partition, service, , , bucket] = bucketName.split(":"); const isArn = arn === "arn" && bucketName.split(":").length >= 6; const isValidArn = Boolean(isArn && partition && service && bucket); if (isArn && !isValidArn) { throw new Error(`Invalid ARN: ${bucketName} was an invalid ARN.`); } return isValidArn; }; const createConfigValueProvider = (configKey, canonicalEndpointParamKey, config, isClientContextParam = false) => { const configProvider = async () => { let configValue; if (isClientContextParam) { const clientContextParams = config.clientContextParams; const nestedValue = clientContextParams?.[configKey]; configValue = nestedValue ?? config[configKey] ?? config[canonicalEndpointParamKey]; } else { configValue = config[configKey] ?? config[canonicalEndpointParamKey]; } if (typeof configValue === "function") { return configValue(); } return configValue; }; if (configKey === "credentialScope" || canonicalEndpointParamKey === "CredentialScope") { return async () => { const credentials = typeof config.credentials === "function" ? await config.credentials() : config.credentials; const configValue = credentials?.credentialScope ?? credentials?.CredentialScope; return configValue; }; } if (configKey === "accountId" || canonicalEndpointParamKey === "AccountId") { return async () => { const credentials = typeof config.credentials === "function" ? await config.credentials() : config.credentials; const configValue = credentials?.accountId ?? credentials?.AccountId; return configValue; }; } if (configKey === "endpoint" || canonicalEndpointParamKey === "endpoint") { return async () => { if (config.isCustomEndpoint === false) { return undefined; } const endpoint = await configProvider(); if (endpoint && typeof endpoint === "object") { if ("url" in endpoint) { return endpoint.url.href; } if ("hostname" in endpoint) { const { protocol, hostname, port, path } = endpoint; return `${protocol}//${hostname}${port ? ":" + port : ""}${path}`; } } return endpoint; }; } return configProvider; }; const toEndpointV1 = (endpoint) => { if (typeof endpoint === "object") { if ("url" in endpoint) { const v1Endpoint = protocols.parseUrl(endpoint.url); if (endpoint.headers) { v1Endpoint.headers = {}; for (const name in endpoint.headers) { v1Endpoint.headers[name.toLowerCase()] = endpoint.headers[name].join(", "); } } return v1Endpoint; } return endpoint; } return protocols.parseUrl(endpoint); }; function bindGetEndpointFromInstructions(getEndpointFromConfig) { return async (commandInput, instructionsSupplier, clientConfig, context) => { if (!clientConfig.isCustomEndpoint) { let endpointFromConfig; if (clientConfig.serviceConfiguredEndpoint) { endpointFromConfig = await clientConfig.serviceConfiguredEndpoint(); } else { endpointFromConfig = await getEndpointFromConfig(clientConfig.serviceId); } if (endpointFromConfig) { clientConfig.endpoint = () => Promise.resolve(toEndpointV1(endpointFromConfig)); clientConfig.isCustomEndpoint = true; } } const endpointParams = await resolveParams(commandInput, instructionsSupplier, clientConfig); if (typeof clientConfig.endpointProvider !== "function") { throw new Error("config.endpointProvider is not set."); } const endpoint = clientConfig.endpointProvider(endpointParams, context); if (clientConfig.isCustomEndpoint && clientConfig.endpoint) { const customEndpoint = await clientConfig.endpoint(); if (customEndpoint?.headers) { endpoint.headers ??= {}; for (const [name, value] of Object.entries(customEndpoint.headers)) { endpoint.headers[name] = Array.isArray(value) ? value : [value]; } } } return endpoint; }; } const resolveParams = async (commandInput, instructionsSupplier, clientConfig) => { const endpointParams = {}; const instructions = instructionsSupplier?.getEndpointParameterInstructions?.() || {}; for (const [name, instruction] of Object.entries(instructions)) { switch (instruction.type) { case "staticContextParams": endpointParams[name] = instruction.value; break; case "contextParams": endpointParams[name] = commandInput[instruction.name]; break; case "clientContextParams": case "builtInParams": endpointParams[name] = await createConfigValueProvider(instruction.name, name, clientConfig, instruction.type !== "builtInParams")(); break; case "operationContextParams": endpointParams[name] = instruction.get(commandInput); break; default: throw new Error("Unrecognized endpoint parameter instruction: " + JSON.stringify(instruction)); } } if (Object.keys(instructions).length === 0) { Object.assign(endpointParams, clientConfig); } if (String(clientConfig.serviceId).toLowerCase() === "s3") { await resolveParamsForS3(endpointParams); } return endpointParams; }; function setFeature(context, feature, value) { if (!context.__smithy_context) { context.__smithy_context = { features: {} }; } else if (!context.__smithy_context.features) { context.__smithy_context.features = {}; } context.__smithy_context.features[feature] = value; } function bindEndpointMiddleware(getEndpointFromConfig) { const getEndpointFromInstructions = bindGetEndpointFromInstructions(getEndpointFromConfig); return ({ config, instructions, }) => { return (next, context) => async (args) => { if (config.isCustomEndpoint) { setFeature(context, "ENDPOINT_OVERRIDE", "N"); } const endpoint = await getEndpointFromInstructions(args.input, { getEndpointParameterInstructions() { return instructions; }, }, { ...config }, context); context.endpointV2 = endpoint; context.authSchemes = endpoint.properties?.authSchemes; const authScheme = context.authSchemes?.[0]; if (authScheme) { context["signing_region"] = authScheme.signingRegion; context["signing_service"] = authScheme.signingName; const smithyContext = client.getSmithyContext(context); const httpAuthOption = smithyContext?.selectedHttpAuthScheme?.httpAuthOption; if (httpAuthOption) { httpAuthOption.signingProperties = Object.assign(httpAuthOption.signingProperties || {}, { signing_region: authScheme.signingRegion, signingRegion: authScheme.signingRegion, signing_service: authScheme.signingName, signingName: authScheme.signingName, signingRegionSet: authScheme.signingRegionSet, }, authScheme.properties); } } return next({ ...args, }); }; }; } const serializerMiddlewareOption = { name: "serializerMiddleware"}; const endpointMiddlewareOptions = { step: "serialize", tags: ["ENDPOINT_PARAMETERS", "ENDPOINT_V2", "ENDPOINT"], name: "endpointV2Middleware", override: true, relation: "before", toMiddleware: serializerMiddlewareOption.name, }; function bindGetEndpointPlugin(getEndpointFromConfig) { const endpointMiddleware = bindEndpointMiddleware(getEndpointFromConfig); return (config, instructions) => ({ applyToStack: (clientStack) => { clientStack.addRelativeTo(endpointMiddleware({ config, instructions, }), endpointMiddlewareOptions); }, }); } function bindResolveEndpointConfig(getEndpointFromConfig) { return (input) => { const tls = input.tls ?? true; const { endpoint, useDualstackEndpoint, useFipsEndpoint } = input; const customEndpointProvider = endpoint != null ? async () => toEndpointV1(await client.normalizeProvider(endpoint)()) : undefined; const isCustomEndpoint = !!endpoint; const resolvedConfig = Object.assign(input, { endpoint: customEndpointProvider, tls, isCustomEndpoint, useDualstackEndpoint: client.normalizeProvider(useDualstackEndpoint ?? false), useFipsEndpoint: client.normalizeProvider(useFipsEndpoint ?? false), }); let configuredEndpointPromise = undefined; resolvedConfig.serviceConfiguredEndpoint = async () => { if (input.serviceId && !configuredEndpointPromise) { configuredEndpointPromise = getEndpointFromConfig(input.serviceId); } return configuredEndpointPromise; }; return resolvedConfig; }; } 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; }; const resolveEndpointRequiredConfig = (input) => { const { endpoint } = input; if (endpoint === undefined) { input.endpoint = async () => { throw new Error("@smithy/middleware-endpoint: (default endpointRuleSet) endpoint is not set - you must configure an endpoint."); }; } return input; }; const getEndpointFromInstructions = bindGetEndpointFromInstructions(getEndpointFromConfig); const resolveEndpointConfig = bindResolveEndpointConfig(getEndpointFromConfig); const endpointMiddleware = bindEndpointMiddleware(getEndpointFromConfig); const getEndpointPlugin = bindGetEndpointPlugin(getEndpointFromConfig); exports.BinaryDecisionDiagram = BinaryDecisionDiagram; exports.EndpointCache = EndpointCache; exports.EndpointError = EndpointError; exports.customEndpointFunctions = customEndpointFunctions; exports.decideEndpoint = decideEndpoint; exports.endpointMiddleware = endpointMiddleware; exports.endpointMiddlewareOptions = endpointMiddlewareOptions; exports.getEndpointFromInstructions = getEndpointFromInstructions; exports.getEndpointPlugin = getEndpointPlugin; exports.isIpAddress = isIpAddress; exports.isValidHostLabel = isValidHostLabel; exports.middlewareEndpointToEndpointV1 = toEndpointV1; exports.resolveEndpoint = resolveEndpoint; exports.resolveEndpointConfig = resolveEndpointConfig; exports.resolveEndpointRequiredConfig = resolveEndpointRequiredConfig; exports.resolveParams = resolveParams; exports.toEndpointV1 = toEndpointV1;