UEA-Prodem
This commit is contained in:
+344
@@ -0,0 +1,344 @@
|
||||
'use strict';
|
||||
|
||||
var transport = require('@smithy/core/transport');
|
||||
var protocols = require('@smithy/core/protocols');
|
||||
var types = require('@smithy/types');
|
||||
var client = require('@smithy/core/client');
|
||||
|
||||
const resolveAuthOptions = (candidateAuthOptions, authSchemePreference) => {
|
||||
if (!authSchemePreference || authSchemePreference.length === 0) {
|
||||
return candidateAuthOptions;
|
||||
}
|
||||
const preferredAuthOptions = [];
|
||||
for (const preferredSchemeName of authSchemePreference) {
|
||||
for (const candidateAuthOption of candidateAuthOptions) {
|
||||
const candidateAuthSchemeName = candidateAuthOption.schemeId.split("#")[1];
|
||||
if (candidateAuthSchemeName === preferredSchemeName) {
|
||||
preferredAuthOptions.push(candidateAuthOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const candidateAuthOption of candidateAuthOptions) {
|
||||
if (!preferredAuthOptions.find(({ schemeId }) => schemeId === candidateAuthOption.schemeId)) {
|
||||
preferredAuthOptions.push(candidateAuthOption);
|
||||
}
|
||||
}
|
||||
return preferredAuthOptions;
|
||||
};
|
||||
|
||||
function convertHttpAuthSchemesToMap(httpAuthSchemes) {
|
||||
const map = new Map();
|
||||
for (const scheme of httpAuthSchemes) {
|
||||
map.set(scheme.schemeId, scheme);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
const httpAuthSchemeMiddleware = (config, mwOptions) => (next, context) => async (args) => {
|
||||
const options = config.httpAuthSchemeProvider(await mwOptions.httpAuthSchemeParametersProvider(config, context, args.input));
|
||||
const authSchemePreference = config.authSchemePreference ? await config.authSchemePreference() : [];
|
||||
const resolvedOptions = resolveAuthOptions(options, authSchemePreference);
|
||||
const authSchemes = convertHttpAuthSchemesToMap(config.httpAuthSchemes);
|
||||
const smithyContext = client.getSmithyContext(context);
|
||||
const failureReasons = [];
|
||||
for (const option of resolvedOptions) {
|
||||
const scheme = authSchemes.get(option.schemeId);
|
||||
if (!scheme) {
|
||||
failureReasons.push(`HttpAuthScheme \`${option.schemeId}\` was not enabled for this service.`);
|
||||
continue;
|
||||
}
|
||||
const identityProvider = scheme.identityProvider(await mwOptions.identityProviderConfigProvider(config));
|
||||
if (!identityProvider) {
|
||||
failureReasons.push(`HttpAuthScheme \`${option.schemeId}\` did not have an IdentityProvider configured.`);
|
||||
continue;
|
||||
}
|
||||
const { identityProperties = {}, signingProperties = {} } = option.propertiesExtractor?.(config, context) || {};
|
||||
option.identityProperties = Object.assign(option.identityProperties || {}, identityProperties);
|
||||
option.signingProperties = Object.assign(option.signingProperties || {}, signingProperties);
|
||||
smithyContext.selectedHttpAuthScheme = {
|
||||
httpAuthOption: option,
|
||||
identity: await identityProvider(option.identityProperties),
|
||||
signer: scheme.signer,
|
||||
};
|
||||
break;
|
||||
}
|
||||
if (!smithyContext.selectedHttpAuthScheme) {
|
||||
throw new Error(failureReasons.join("\n"));
|
||||
}
|
||||
return next(args);
|
||||
};
|
||||
|
||||
const httpAuthSchemeEndpointRuleSetMiddlewareOptions = {
|
||||
step: "serialize",
|
||||
tags: ["HTTP_AUTH_SCHEME"],
|
||||
name: "httpAuthSchemeMiddleware",
|
||||
override: true,
|
||||
relation: "before",
|
||||
toMiddleware: "endpointV2Middleware",
|
||||
};
|
||||
const getHttpAuthSchemeEndpointRuleSetPlugin = (config, { httpAuthSchemeParametersProvider, identityProviderConfigProvider, }) => ({
|
||||
applyToStack: (clientStack) => {
|
||||
clientStack.addRelativeTo(httpAuthSchemeMiddleware(config, {
|
||||
httpAuthSchemeParametersProvider,
|
||||
identityProviderConfigProvider,
|
||||
}), httpAuthSchemeEndpointRuleSetMiddlewareOptions);
|
||||
},
|
||||
});
|
||||
|
||||
const httpAuthSchemeMiddlewareOptions = {
|
||||
step: "serialize",
|
||||
tags: ["HTTP_AUTH_SCHEME"],
|
||||
name: "httpAuthSchemeMiddleware",
|
||||
override: true,
|
||||
relation: "before",
|
||||
toMiddleware: "serializerMiddleware",
|
||||
};
|
||||
const getHttpAuthSchemePlugin = (config, { httpAuthSchemeParametersProvider, identityProviderConfigProvider, }) => ({
|
||||
applyToStack: (clientStack) => {
|
||||
clientStack.addRelativeTo(httpAuthSchemeMiddleware(config, {
|
||||
httpAuthSchemeParametersProvider,
|
||||
identityProviderConfigProvider,
|
||||
}), httpAuthSchemeMiddlewareOptions);
|
||||
},
|
||||
});
|
||||
|
||||
const defaultErrorHandler = (signingProperties) => (error) => {
|
||||
throw error;
|
||||
};
|
||||
const defaultSuccessHandler = (httpResponse, signingProperties) => { };
|
||||
const httpSigningMiddleware = (config) => (next, context) => async (args) => {
|
||||
if (!protocols.HttpRequest.isInstance(args.request)) {
|
||||
return next(args);
|
||||
}
|
||||
const smithyContext = client.getSmithyContext(context);
|
||||
const scheme = smithyContext.selectedHttpAuthScheme;
|
||||
if (!scheme) {
|
||||
throw new Error(`No HttpAuthScheme was selected: unable to sign request`);
|
||||
}
|
||||
const { httpAuthOption: { signingProperties = {} }, identity, signer, } = scheme;
|
||||
const output = await next({
|
||||
...args,
|
||||
request: await signer.sign(args.request, identity, signingProperties),
|
||||
}).catch((signer.errorHandler || defaultErrorHandler)(signingProperties));
|
||||
(signer.successHandler || defaultSuccessHandler)(output.response, signingProperties);
|
||||
return output;
|
||||
};
|
||||
|
||||
const httpSigningMiddlewareOptions = {
|
||||
step: "finalizeRequest",
|
||||
tags: ["HTTP_SIGNING"],
|
||||
name: "httpSigningMiddleware",
|
||||
aliases: ["apiKeyMiddleware", "tokenMiddleware", "awsAuthMiddleware"],
|
||||
override: true,
|
||||
relation: "after",
|
||||
toMiddleware: "retryMiddleware",
|
||||
};
|
||||
const getHttpSigningPlugin = (config) => ({
|
||||
applyToStack: (clientStack) => {
|
||||
clientStack.addRelativeTo(httpSigningMiddleware(), httpSigningMiddlewareOptions);
|
||||
},
|
||||
});
|
||||
|
||||
const normalizeProvider = (input) => {
|
||||
if (typeof input === "function")
|
||||
return input;
|
||||
const promisified = Promise.resolve(input);
|
||||
return () => promisified;
|
||||
};
|
||||
|
||||
const makePagedClientRequest = async (CommandCtor, client, input, withCommand = (_) => _, ...args) => {
|
||||
let command = new CommandCtor(input);
|
||||
command = withCommand(command) ?? command;
|
||||
return await client.send(command, ...args);
|
||||
};
|
||||
function createPaginator(ClientCtor, CommandCtor, inputTokenName, outputTokenName, pageSizeTokenName) {
|
||||
return async function* paginateOperation(config, input, ...additionalArguments) {
|
||||
const _input = input;
|
||||
let token = config.startingToken ?? _input[inputTokenName];
|
||||
let hasNext = true;
|
||||
let page;
|
||||
while (hasNext) {
|
||||
_input[inputTokenName] = token;
|
||||
if (pageSizeTokenName) {
|
||||
_input[pageSizeTokenName] = _input[pageSizeTokenName] ?? config.pageSize;
|
||||
}
|
||||
if (config.client instanceof ClientCtor) {
|
||||
page = await makePagedClientRequest(CommandCtor, config.client, input, config.withCommand, ...additionalArguments);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Invalid client, expected instance of ${ClientCtor.name}`);
|
||||
}
|
||||
yield page;
|
||||
const prevToken = token;
|
||||
token = get(page, outputTokenName);
|
||||
hasNext = !!(token && (!config.stopOnSameToken || token !== prevToken));
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
const get = (fromObject, path) => {
|
||||
let cursor = fromObject;
|
||||
const pathComponents = path.split(".");
|
||||
for (const step of pathComponents) {
|
||||
if (!cursor || typeof cursor !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
cursor = cursor[step];
|
||||
}
|
||||
return cursor;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
class DefaultIdentityProviderConfig {
|
||||
authSchemes = new Map();
|
||||
constructor(config) {
|
||||
for (const key in config) {
|
||||
const value = config[key];
|
||||
if (value !== undefined) {
|
||||
this.authSchemes.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
getIdentityProvider(schemeId) {
|
||||
return this.authSchemes.get(schemeId);
|
||||
}
|
||||
}
|
||||
|
||||
class HttpApiKeyAuthSigner {
|
||||
async sign(httpRequest, identity, signingProperties) {
|
||||
if (!signingProperties) {
|
||||
throw new Error("request could not be signed with `apiKey` since the `name` and `in` signer properties are missing");
|
||||
}
|
||||
if (!signingProperties.name) {
|
||||
throw new Error("request could not be signed with `apiKey` since the `name` signer property is missing");
|
||||
}
|
||||
if (!signingProperties.in) {
|
||||
throw new Error("request could not be signed with `apiKey` since the `in` signer property is missing");
|
||||
}
|
||||
if (!identity.apiKey) {
|
||||
throw new Error("request could not be signed with `apiKey` since the `apiKey` is not defined");
|
||||
}
|
||||
const clonedRequest = protocols.HttpRequest.clone(httpRequest);
|
||||
if (signingProperties.in === types.HttpApiKeyAuthLocation.QUERY) {
|
||||
clonedRequest.query[signingProperties.name] = identity.apiKey;
|
||||
}
|
||||
else if (signingProperties.in === types.HttpApiKeyAuthLocation.HEADER) {
|
||||
clonedRequest.headers[signingProperties.name] = signingProperties.scheme
|
||||
? `${signingProperties.scheme} ${identity.apiKey}`
|
||||
: identity.apiKey;
|
||||
}
|
||||
else {
|
||||
throw new Error("request can only be signed with `apiKey` locations `query` or `header`, " +
|
||||
"but found: `" +
|
||||
signingProperties.in +
|
||||
"`");
|
||||
}
|
||||
return clonedRequest;
|
||||
}
|
||||
}
|
||||
|
||||
class HttpBearerAuthSigner {
|
||||
async sign(httpRequest, identity, signingProperties) {
|
||||
const clonedRequest = protocols.HttpRequest.clone(httpRequest);
|
||||
if (!identity.token) {
|
||||
throw new Error("request could not be signed with `token` since the `token` is not defined");
|
||||
}
|
||||
clonedRequest.headers["Authorization"] = `Bearer ${identity.token}`;
|
||||
return clonedRequest;
|
||||
}
|
||||
}
|
||||
|
||||
class NoAuthSigner {
|
||||
async sign(httpRequest, identity, signingProperties) {
|
||||
return httpRequest;
|
||||
}
|
||||
}
|
||||
|
||||
const createIsIdentityExpiredFunction = (expirationMs) => function isIdentityExpired(identity) {
|
||||
return doesIdentityRequireRefresh(identity) && identity.expiration.getTime() - Date.now() < expirationMs;
|
||||
};
|
||||
const EXPIRATION_MS = 300_000;
|
||||
const isIdentityExpired = createIsIdentityExpiredFunction(EXPIRATION_MS);
|
||||
const doesIdentityRequireRefresh = (identity) => identity.expiration !== undefined;
|
||||
const memoizeIdentityProvider = (provider, isExpired, requiresRefresh) => {
|
||||
if (provider === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const normalizedProvider = typeof provider !== "function" ? async () => Promise.resolve(provider) : provider;
|
||||
let resolved;
|
||||
let pending;
|
||||
let hasResult;
|
||||
let isConstant = false;
|
||||
const coalesceProvider = async (options) => {
|
||||
if (!pending) {
|
||||
pending = normalizedProvider(options);
|
||||
}
|
||||
try {
|
||||
resolved = await pending;
|
||||
hasResult = true;
|
||||
isConstant = false;
|
||||
}
|
||||
finally {
|
||||
pending = undefined;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
if (isExpired === undefined) {
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider(options);
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
}
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider(options);
|
||||
}
|
||||
if (isConstant) {
|
||||
return resolved;
|
||||
}
|
||||
if (!requiresRefresh(resolved)) {
|
||||
isConstant = true;
|
||||
return resolved;
|
||||
}
|
||||
if (isExpired(resolved)) {
|
||||
await coalesceProvider(options);
|
||||
return resolved;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
};
|
||||
|
||||
exports.getSmithyContext = transport.getSmithyContext;
|
||||
exports.requestBuilder = protocols.requestBuilder;
|
||||
exports.DefaultIdentityProviderConfig = DefaultIdentityProviderConfig;
|
||||
exports.EXPIRATION_MS = EXPIRATION_MS;
|
||||
exports.HttpApiKeyAuthSigner = HttpApiKeyAuthSigner;
|
||||
exports.HttpBearerAuthSigner = HttpBearerAuthSigner;
|
||||
exports.NoAuthSigner = NoAuthSigner;
|
||||
exports.createIsIdentityExpiredFunction = createIsIdentityExpiredFunction;
|
||||
exports.createPaginator = createPaginator;
|
||||
exports.doesIdentityRequireRefresh = doesIdentityRequireRefresh;
|
||||
exports.getHttpAuthSchemeEndpointRuleSetPlugin = getHttpAuthSchemeEndpointRuleSetPlugin;
|
||||
exports.getHttpAuthSchemePlugin = getHttpAuthSchemePlugin;
|
||||
exports.getHttpSigningPlugin = getHttpSigningPlugin;
|
||||
exports.httpAuthSchemeEndpointRuleSetMiddlewareOptions = httpAuthSchemeEndpointRuleSetMiddlewareOptions;
|
||||
exports.httpAuthSchemeMiddleware = httpAuthSchemeMiddleware;
|
||||
exports.httpAuthSchemeMiddlewareOptions = httpAuthSchemeMiddlewareOptions;
|
||||
exports.httpSigningMiddleware = httpSigningMiddleware;
|
||||
exports.httpSigningMiddlewareOptions = httpSigningMiddlewareOptions;
|
||||
exports.isIdentityExpired = isIdentityExpired;
|
||||
exports.memoizeIdentityProvider = memoizeIdentityProvider;
|
||||
exports.normalizeProvider = normalizeProvider;
|
||||
exports.setFeature = setFeature;
|
||||
+1110
File diff suppressed because it is too large
Load Diff
+202
@@ -0,0 +1,202 @@
|
||||
'use strict';
|
||||
|
||||
var serde = require('@smithy/core/serde');
|
||||
|
||||
async function blobReader(blob, onChunk, chunkSize = 1024 * 1024) {
|
||||
const size = blob.size;
|
||||
let totalBytesRead = 0;
|
||||
while (totalBytesRead < size) {
|
||||
const slice = blob.slice(totalBytesRead, Math.min(size, totalBytesRead + chunkSize));
|
||||
onChunk(new Uint8Array(await slice.arrayBuffer()));
|
||||
totalBytesRead += slice.size;
|
||||
}
|
||||
}
|
||||
|
||||
const blobHasher = async function blobHasher(hashCtor, blob) {
|
||||
const hash = new hashCtor();
|
||||
await blobReader(blob, (chunk) => {
|
||||
hash.update(chunk);
|
||||
});
|
||||
return hash.digest();
|
||||
};
|
||||
|
||||
const BLOCK_SIZE = 64;
|
||||
const DIGEST_LENGTH = 16;
|
||||
const INIT = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
|
||||
|
||||
class Md5 {
|
||||
state;
|
||||
buffer;
|
||||
bufferLength;
|
||||
bytesHashed;
|
||||
finished;
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
update(sourceData) {
|
||||
if (isEmptyData(sourceData)) {
|
||||
return;
|
||||
}
|
||||
else if (this.finished) {
|
||||
throw new Error("Attempted to update an already finished hash.");
|
||||
}
|
||||
const data = convertToBuffer(sourceData);
|
||||
let position = 0;
|
||||
let { byteLength } = data;
|
||||
this.bytesHashed += byteLength;
|
||||
while (byteLength > 0) {
|
||||
this.buffer.setUint8(this.bufferLength++, data[position++]);
|
||||
byteLength--;
|
||||
if (this.bufferLength === BLOCK_SIZE) {
|
||||
this.hashBuffer();
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
async digest() {
|
||||
if (!this.finished) {
|
||||
const { buffer, bufferLength: undecoratedLength, bytesHashed } = this;
|
||||
const bitsHashed = bytesHashed * 8;
|
||||
buffer.setUint8(this.bufferLength++, 0b10000000);
|
||||
if (undecoratedLength % BLOCK_SIZE >= BLOCK_SIZE - 8) {
|
||||
for (let i = this.bufferLength; i < BLOCK_SIZE; i++) {
|
||||
buffer.setUint8(i, 0);
|
||||
}
|
||||
this.hashBuffer();
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
for (let i = this.bufferLength; i < BLOCK_SIZE - 8; i++) {
|
||||
buffer.setUint8(i, 0);
|
||||
}
|
||||
buffer.setUint32(BLOCK_SIZE - 8, bitsHashed >>> 0, true);
|
||||
buffer.setUint32(BLOCK_SIZE - 4, Math.floor(bitsHashed / 0x100000000), true);
|
||||
this.hashBuffer();
|
||||
this.finished = true;
|
||||
}
|
||||
const out = new DataView(new ArrayBuffer(DIGEST_LENGTH));
|
||||
for (let i = 0; i < 4; i++) {
|
||||
out.setUint32(i * 4, this.state[i], true);
|
||||
}
|
||||
return new Uint8Array(out.buffer, out.byteOffset, out.byteLength);
|
||||
}
|
||||
hashBuffer() {
|
||||
const { buffer, state } = this;
|
||||
let a = state[0], b = state[1], c = state[2], d = state[3];
|
||||
a = ff(a, b, c, d, buffer.getUint32(0, true), 7, 0xd76aa478);
|
||||
d = ff(d, a, b, c, buffer.getUint32(4, true), 12, 0xe8c7b756);
|
||||
c = ff(c, d, a, b, buffer.getUint32(8, true), 17, 0x242070db);
|
||||
b = ff(b, c, d, a, buffer.getUint32(12, true), 22, 0xc1bdceee);
|
||||
a = ff(a, b, c, d, buffer.getUint32(16, true), 7, 0xf57c0faf);
|
||||
d = ff(d, a, b, c, buffer.getUint32(20, true), 12, 0x4787c62a);
|
||||
c = ff(c, d, a, b, buffer.getUint32(24, true), 17, 0xa8304613);
|
||||
b = ff(b, c, d, a, buffer.getUint32(28, true), 22, 0xfd469501);
|
||||
a = ff(a, b, c, d, buffer.getUint32(32, true), 7, 0x698098d8);
|
||||
d = ff(d, a, b, c, buffer.getUint32(36, true), 12, 0x8b44f7af);
|
||||
c = ff(c, d, a, b, buffer.getUint32(40, true), 17, 0xffff5bb1);
|
||||
b = ff(b, c, d, a, buffer.getUint32(44, true), 22, 0x895cd7be);
|
||||
a = ff(a, b, c, d, buffer.getUint32(48, true), 7, 0x6b901122);
|
||||
d = ff(d, a, b, c, buffer.getUint32(52, true), 12, 0xfd987193);
|
||||
c = ff(c, d, a, b, buffer.getUint32(56, true), 17, 0xa679438e);
|
||||
b = ff(b, c, d, a, buffer.getUint32(60, true), 22, 0x49b40821);
|
||||
a = gg(a, b, c, d, buffer.getUint32(4, true), 5, 0xf61e2562);
|
||||
d = gg(d, a, b, c, buffer.getUint32(24, true), 9, 0xc040b340);
|
||||
c = gg(c, d, a, b, buffer.getUint32(44, true), 14, 0x265e5a51);
|
||||
b = gg(b, c, d, a, buffer.getUint32(0, true), 20, 0xe9b6c7aa);
|
||||
a = gg(a, b, c, d, buffer.getUint32(20, true), 5, 0xd62f105d);
|
||||
d = gg(d, a, b, c, buffer.getUint32(40, true), 9, 0x02441453);
|
||||
c = gg(c, d, a, b, buffer.getUint32(60, true), 14, 0xd8a1e681);
|
||||
b = gg(b, c, d, a, buffer.getUint32(16, true), 20, 0xe7d3fbc8);
|
||||
a = gg(a, b, c, d, buffer.getUint32(36, true), 5, 0x21e1cde6);
|
||||
d = gg(d, a, b, c, buffer.getUint32(56, true), 9, 0xc33707d6);
|
||||
c = gg(c, d, a, b, buffer.getUint32(12, true), 14, 0xf4d50d87);
|
||||
b = gg(b, c, d, a, buffer.getUint32(32, true), 20, 0x455a14ed);
|
||||
a = gg(a, b, c, d, buffer.getUint32(52, true), 5, 0xa9e3e905);
|
||||
d = gg(d, a, b, c, buffer.getUint32(8, true), 9, 0xfcefa3f8);
|
||||
c = gg(c, d, a, b, buffer.getUint32(28, true), 14, 0x676f02d9);
|
||||
b = gg(b, c, d, a, buffer.getUint32(48, true), 20, 0x8d2a4c8a);
|
||||
a = hh(a, b, c, d, buffer.getUint32(20, true), 4, 0xfffa3942);
|
||||
d = hh(d, a, b, c, buffer.getUint32(32, true), 11, 0x8771f681);
|
||||
c = hh(c, d, a, b, buffer.getUint32(44, true), 16, 0x6d9d6122);
|
||||
b = hh(b, c, d, a, buffer.getUint32(56, true), 23, 0xfde5380c);
|
||||
a = hh(a, b, c, d, buffer.getUint32(4, true), 4, 0xa4beea44);
|
||||
d = hh(d, a, b, c, buffer.getUint32(16, true), 11, 0x4bdecfa9);
|
||||
c = hh(c, d, a, b, buffer.getUint32(28, true), 16, 0xf6bb4b60);
|
||||
b = hh(b, c, d, a, buffer.getUint32(40, true), 23, 0xbebfbc70);
|
||||
a = hh(a, b, c, d, buffer.getUint32(52, true), 4, 0x289b7ec6);
|
||||
d = hh(d, a, b, c, buffer.getUint32(0, true), 11, 0xeaa127fa);
|
||||
c = hh(c, d, a, b, buffer.getUint32(12, true), 16, 0xd4ef3085);
|
||||
b = hh(b, c, d, a, buffer.getUint32(24, true), 23, 0x04881d05);
|
||||
a = hh(a, b, c, d, buffer.getUint32(36, true), 4, 0xd9d4d039);
|
||||
d = hh(d, a, b, c, buffer.getUint32(48, true), 11, 0xe6db99e5);
|
||||
c = hh(c, d, a, b, buffer.getUint32(60, true), 16, 0x1fa27cf8);
|
||||
b = hh(b, c, d, a, buffer.getUint32(8, true), 23, 0xc4ac5665);
|
||||
a = ii(a, b, c, d, buffer.getUint32(0, true), 6, 0xf4292244);
|
||||
d = ii(d, a, b, c, buffer.getUint32(28, true), 10, 0x432aff97);
|
||||
c = ii(c, d, a, b, buffer.getUint32(56, true), 15, 0xab9423a7);
|
||||
b = ii(b, c, d, a, buffer.getUint32(20, true), 21, 0xfc93a039);
|
||||
a = ii(a, b, c, d, buffer.getUint32(48, true), 6, 0x655b59c3);
|
||||
d = ii(d, a, b, c, buffer.getUint32(12, true), 10, 0x8f0ccc92);
|
||||
c = ii(c, d, a, b, buffer.getUint32(40, true), 15, 0xffeff47d);
|
||||
b = ii(b, c, d, a, buffer.getUint32(4, true), 21, 0x85845dd1);
|
||||
a = ii(a, b, c, d, buffer.getUint32(32, true), 6, 0x6fa87e4f);
|
||||
d = ii(d, a, b, c, buffer.getUint32(60, true), 10, 0xfe2ce6e0);
|
||||
c = ii(c, d, a, b, buffer.getUint32(24, true), 15, 0xa3014314);
|
||||
b = ii(b, c, d, a, buffer.getUint32(52, true), 21, 0x4e0811a1);
|
||||
a = ii(a, b, c, d, buffer.getUint32(16, true), 6, 0xf7537e82);
|
||||
d = ii(d, a, b, c, buffer.getUint32(44, true), 10, 0xbd3af235);
|
||||
c = ii(c, d, a, b, buffer.getUint32(8, true), 15, 0x2ad7d2bb);
|
||||
b = ii(b, c, d, a, buffer.getUint32(36, true), 21, 0xeb86d391);
|
||||
state[0] = (a + state[0]) & 0xffffffff;
|
||||
state[1] = (b + state[1]) & 0xffffffff;
|
||||
state[2] = (c + state[2]) & 0xffffffff;
|
||||
state[3] = (d + state[3]) & 0xffffffff;
|
||||
}
|
||||
reset() {
|
||||
this.state = Uint32Array.from(INIT);
|
||||
this.buffer = new DataView(new ArrayBuffer(BLOCK_SIZE));
|
||||
this.bufferLength = 0;
|
||||
this.bytesHashed = 0;
|
||||
this.finished = false;
|
||||
}
|
||||
}
|
||||
function cmn(q, a, b, x, s, t) {
|
||||
a = (((a + q) & 0xffffffff) + ((x + t) & 0xffffffff)) & 0xffffffff;
|
||||
return (((a << s) | (a >>> (32 - s))) + b) & 0xffffffff;
|
||||
}
|
||||
function ff(a, b, c, d, x, s, t) {
|
||||
return cmn((b & c) | (~b & d), a, b, x, s, t);
|
||||
}
|
||||
function gg(a, b, c, d, x, s, t) {
|
||||
return cmn((b & d) | (c & ~d), a, b, x, s, t);
|
||||
}
|
||||
function hh(a, b, c, d, x, s, t) {
|
||||
return cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function ii(a, b, c, d, x, s, t) {
|
||||
return cmn(c ^ (b | ~d), a, b, x, s, t);
|
||||
}
|
||||
function isEmptyData(data) {
|
||||
if (typeof data === "string") {
|
||||
return data.length === 0;
|
||||
}
|
||||
return data.byteLength === 0;
|
||||
}
|
||||
function convertToBuffer(data) {
|
||||
if (typeof data === "string") {
|
||||
return serde.fromUtf8(data);
|
||||
}
|
||||
if (ArrayBuffer.isView(data)) {
|
||||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength / Uint8Array.BYTES_PER_ELEMENT);
|
||||
}
|
||||
return new Uint8Array(data);
|
||||
}
|
||||
|
||||
const no = Symbol.for("node-only");
|
||||
const fileStreamHasher = no;
|
||||
const readableStreamHasher = no;
|
||||
|
||||
exports.Md5 = Md5;
|
||||
exports.blobHasher = blobHasher;
|
||||
exports.blobReader = blobReader;
|
||||
exports.fileStreamHasher = fileStreamHasher;
|
||||
exports.readableStreamHasher = readableStreamHasher;
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
'use strict';
|
||||
|
||||
var node_fs = require('node:fs');
|
||||
var node_stream = require('node:stream');
|
||||
var serde = require('@smithy/core/serde');
|
||||
|
||||
async function blobReader(blob, onChunk, chunkSize = 1024 * 1024) {
|
||||
const size = blob.size;
|
||||
let totalBytesRead = 0;
|
||||
while (totalBytesRead < size) {
|
||||
const slice = blob.slice(totalBytesRead, Math.min(size, totalBytesRead + chunkSize));
|
||||
onChunk(new Uint8Array(await slice.arrayBuffer()));
|
||||
totalBytesRead += slice.size;
|
||||
}
|
||||
}
|
||||
|
||||
const blobHasher = async function blobHasher(hashCtor, blob) {
|
||||
const hash = new hashCtor();
|
||||
await blobReader(blob, (chunk) => {
|
||||
hash.update(chunk);
|
||||
});
|
||||
return hash.digest();
|
||||
};
|
||||
|
||||
class HashCalculator extends node_stream.Writable {
|
||||
hash;
|
||||
constructor(hash, options) {
|
||||
super(options);
|
||||
this.hash = hash;
|
||||
}
|
||||
_write(chunk, encoding, callback) {
|
||||
try {
|
||||
this.hash.update(serde.toUint8Array(chunk));
|
||||
}
|
||||
catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
const fileStreamHasher = (hashCtor, fileStream) => new Promise((resolve, reject) => {
|
||||
if (!isReadStream(fileStream)) {
|
||||
reject(new Error("Unable to calculate hash for non-file streams."));
|
||||
return;
|
||||
}
|
||||
const fileStreamTee = node_fs.createReadStream(fileStream.path, {
|
||||
start: fileStream.start,
|
||||
end: fileStream.end,
|
||||
});
|
||||
const hash = new hashCtor();
|
||||
const hashCalculator = new HashCalculator(hash);
|
||||
fileStreamTee.pipe(hashCalculator);
|
||||
fileStreamTee.on("error", (err) => {
|
||||
hashCalculator.end();
|
||||
reject(err);
|
||||
});
|
||||
hashCalculator.on("error", reject);
|
||||
hashCalculator.on("finish", function () {
|
||||
hash.digest().then(resolve).catch(reject);
|
||||
});
|
||||
});
|
||||
const isReadStream = (stream) => typeof stream.path === "string";
|
||||
|
||||
const readableStreamHasher = (hashCtor, readableStream) => {
|
||||
if (readableStream.readableFlowing !== null) {
|
||||
throw new Error("Unable to calculate hash for flowing readable stream");
|
||||
}
|
||||
const hash = new hashCtor();
|
||||
const hashCalculator = new HashCalculator(hash);
|
||||
readableStream.pipe(hashCalculator);
|
||||
return new Promise((resolve, reject) => {
|
||||
readableStream.on("error", (err) => {
|
||||
hashCalculator.end();
|
||||
reject(err);
|
||||
});
|
||||
hashCalculator.on("error", reject);
|
||||
hashCalculator.on("finish", () => {
|
||||
hash.digest().then(resolve).catch(reject);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const BLOCK_SIZE = 64;
|
||||
const DIGEST_LENGTH = 16;
|
||||
const INIT = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
|
||||
|
||||
class Md5 {
|
||||
state;
|
||||
buffer;
|
||||
bufferLength;
|
||||
bytesHashed;
|
||||
finished;
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
update(sourceData) {
|
||||
if (isEmptyData(sourceData)) {
|
||||
return;
|
||||
}
|
||||
else if (this.finished) {
|
||||
throw new Error("Attempted to update an already finished hash.");
|
||||
}
|
||||
const data = convertToBuffer(sourceData);
|
||||
let position = 0;
|
||||
let { byteLength } = data;
|
||||
this.bytesHashed += byteLength;
|
||||
while (byteLength > 0) {
|
||||
this.buffer.setUint8(this.bufferLength++, data[position++]);
|
||||
byteLength--;
|
||||
if (this.bufferLength === BLOCK_SIZE) {
|
||||
this.hashBuffer();
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
async digest() {
|
||||
if (!this.finished) {
|
||||
const { buffer, bufferLength: undecoratedLength, bytesHashed } = this;
|
||||
const bitsHashed = bytesHashed * 8;
|
||||
buffer.setUint8(this.bufferLength++, 0b10000000);
|
||||
if (undecoratedLength % BLOCK_SIZE >= BLOCK_SIZE - 8) {
|
||||
for (let i = this.bufferLength; i < BLOCK_SIZE; i++) {
|
||||
buffer.setUint8(i, 0);
|
||||
}
|
||||
this.hashBuffer();
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
for (let i = this.bufferLength; i < BLOCK_SIZE - 8; i++) {
|
||||
buffer.setUint8(i, 0);
|
||||
}
|
||||
buffer.setUint32(BLOCK_SIZE - 8, bitsHashed >>> 0, true);
|
||||
buffer.setUint32(BLOCK_SIZE - 4, Math.floor(bitsHashed / 0x100000000), true);
|
||||
this.hashBuffer();
|
||||
this.finished = true;
|
||||
}
|
||||
const out = new DataView(new ArrayBuffer(DIGEST_LENGTH));
|
||||
for (let i = 0; i < 4; i++) {
|
||||
out.setUint32(i * 4, this.state[i], true);
|
||||
}
|
||||
return new Uint8Array(out.buffer, out.byteOffset, out.byteLength);
|
||||
}
|
||||
hashBuffer() {
|
||||
const { buffer, state } = this;
|
||||
let a = state[0], b = state[1], c = state[2], d = state[3];
|
||||
a = ff(a, b, c, d, buffer.getUint32(0, true), 7, 0xd76aa478);
|
||||
d = ff(d, a, b, c, buffer.getUint32(4, true), 12, 0xe8c7b756);
|
||||
c = ff(c, d, a, b, buffer.getUint32(8, true), 17, 0x242070db);
|
||||
b = ff(b, c, d, a, buffer.getUint32(12, true), 22, 0xc1bdceee);
|
||||
a = ff(a, b, c, d, buffer.getUint32(16, true), 7, 0xf57c0faf);
|
||||
d = ff(d, a, b, c, buffer.getUint32(20, true), 12, 0x4787c62a);
|
||||
c = ff(c, d, a, b, buffer.getUint32(24, true), 17, 0xa8304613);
|
||||
b = ff(b, c, d, a, buffer.getUint32(28, true), 22, 0xfd469501);
|
||||
a = ff(a, b, c, d, buffer.getUint32(32, true), 7, 0x698098d8);
|
||||
d = ff(d, a, b, c, buffer.getUint32(36, true), 12, 0x8b44f7af);
|
||||
c = ff(c, d, a, b, buffer.getUint32(40, true), 17, 0xffff5bb1);
|
||||
b = ff(b, c, d, a, buffer.getUint32(44, true), 22, 0x895cd7be);
|
||||
a = ff(a, b, c, d, buffer.getUint32(48, true), 7, 0x6b901122);
|
||||
d = ff(d, a, b, c, buffer.getUint32(52, true), 12, 0xfd987193);
|
||||
c = ff(c, d, a, b, buffer.getUint32(56, true), 17, 0xa679438e);
|
||||
b = ff(b, c, d, a, buffer.getUint32(60, true), 22, 0x49b40821);
|
||||
a = gg(a, b, c, d, buffer.getUint32(4, true), 5, 0xf61e2562);
|
||||
d = gg(d, a, b, c, buffer.getUint32(24, true), 9, 0xc040b340);
|
||||
c = gg(c, d, a, b, buffer.getUint32(44, true), 14, 0x265e5a51);
|
||||
b = gg(b, c, d, a, buffer.getUint32(0, true), 20, 0xe9b6c7aa);
|
||||
a = gg(a, b, c, d, buffer.getUint32(20, true), 5, 0xd62f105d);
|
||||
d = gg(d, a, b, c, buffer.getUint32(40, true), 9, 0x02441453);
|
||||
c = gg(c, d, a, b, buffer.getUint32(60, true), 14, 0xd8a1e681);
|
||||
b = gg(b, c, d, a, buffer.getUint32(16, true), 20, 0xe7d3fbc8);
|
||||
a = gg(a, b, c, d, buffer.getUint32(36, true), 5, 0x21e1cde6);
|
||||
d = gg(d, a, b, c, buffer.getUint32(56, true), 9, 0xc33707d6);
|
||||
c = gg(c, d, a, b, buffer.getUint32(12, true), 14, 0xf4d50d87);
|
||||
b = gg(b, c, d, a, buffer.getUint32(32, true), 20, 0x455a14ed);
|
||||
a = gg(a, b, c, d, buffer.getUint32(52, true), 5, 0xa9e3e905);
|
||||
d = gg(d, a, b, c, buffer.getUint32(8, true), 9, 0xfcefa3f8);
|
||||
c = gg(c, d, a, b, buffer.getUint32(28, true), 14, 0x676f02d9);
|
||||
b = gg(b, c, d, a, buffer.getUint32(48, true), 20, 0x8d2a4c8a);
|
||||
a = hh(a, b, c, d, buffer.getUint32(20, true), 4, 0xfffa3942);
|
||||
d = hh(d, a, b, c, buffer.getUint32(32, true), 11, 0x8771f681);
|
||||
c = hh(c, d, a, b, buffer.getUint32(44, true), 16, 0x6d9d6122);
|
||||
b = hh(b, c, d, a, buffer.getUint32(56, true), 23, 0xfde5380c);
|
||||
a = hh(a, b, c, d, buffer.getUint32(4, true), 4, 0xa4beea44);
|
||||
d = hh(d, a, b, c, buffer.getUint32(16, true), 11, 0x4bdecfa9);
|
||||
c = hh(c, d, a, b, buffer.getUint32(28, true), 16, 0xf6bb4b60);
|
||||
b = hh(b, c, d, a, buffer.getUint32(40, true), 23, 0xbebfbc70);
|
||||
a = hh(a, b, c, d, buffer.getUint32(52, true), 4, 0x289b7ec6);
|
||||
d = hh(d, a, b, c, buffer.getUint32(0, true), 11, 0xeaa127fa);
|
||||
c = hh(c, d, a, b, buffer.getUint32(12, true), 16, 0xd4ef3085);
|
||||
b = hh(b, c, d, a, buffer.getUint32(24, true), 23, 0x04881d05);
|
||||
a = hh(a, b, c, d, buffer.getUint32(36, true), 4, 0xd9d4d039);
|
||||
d = hh(d, a, b, c, buffer.getUint32(48, true), 11, 0xe6db99e5);
|
||||
c = hh(c, d, a, b, buffer.getUint32(60, true), 16, 0x1fa27cf8);
|
||||
b = hh(b, c, d, a, buffer.getUint32(8, true), 23, 0xc4ac5665);
|
||||
a = ii(a, b, c, d, buffer.getUint32(0, true), 6, 0xf4292244);
|
||||
d = ii(d, a, b, c, buffer.getUint32(28, true), 10, 0x432aff97);
|
||||
c = ii(c, d, a, b, buffer.getUint32(56, true), 15, 0xab9423a7);
|
||||
b = ii(b, c, d, a, buffer.getUint32(20, true), 21, 0xfc93a039);
|
||||
a = ii(a, b, c, d, buffer.getUint32(48, true), 6, 0x655b59c3);
|
||||
d = ii(d, a, b, c, buffer.getUint32(12, true), 10, 0x8f0ccc92);
|
||||
c = ii(c, d, a, b, buffer.getUint32(40, true), 15, 0xffeff47d);
|
||||
b = ii(b, c, d, a, buffer.getUint32(4, true), 21, 0x85845dd1);
|
||||
a = ii(a, b, c, d, buffer.getUint32(32, true), 6, 0x6fa87e4f);
|
||||
d = ii(d, a, b, c, buffer.getUint32(60, true), 10, 0xfe2ce6e0);
|
||||
c = ii(c, d, a, b, buffer.getUint32(24, true), 15, 0xa3014314);
|
||||
b = ii(b, c, d, a, buffer.getUint32(52, true), 21, 0x4e0811a1);
|
||||
a = ii(a, b, c, d, buffer.getUint32(16, true), 6, 0xf7537e82);
|
||||
d = ii(d, a, b, c, buffer.getUint32(44, true), 10, 0xbd3af235);
|
||||
c = ii(c, d, a, b, buffer.getUint32(8, true), 15, 0x2ad7d2bb);
|
||||
b = ii(b, c, d, a, buffer.getUint32(36, true), 21, 0xeb86d391);
|
||||
state[0] = (a + state[0]) & 0xffffffff;
|
||||
state[1] = (b + state[1]) & 0xffffffff;
|
||||
state[2] = (c + state[2]) & 0xffffffff;
|
||||
state[3] = (d + state[3]) & 0xffffffff;
|
||||
}
|
||||
reset() {
|
||||
this.state = Uint32Array.from(INIT);
|
||||
this.buffer = new DataView(new ArrayBuffer(BLOCK_SIZE));
|
||||
this.bufferLength = 0;
|
||||
this.bytesHashed = 0;
|
||||
this.finished = false;
|
||||
}
|
||||
}
|
||||
function cmn(q, a, b, x, s, t) {
|
||||
a = (((a + q) & 0xffffffff) + ((x + t) & 0xffffffff)) & 0xffffffff;
|
||||
return (((a << s) | (a >>> (32 - s))) + b) & 0xffffffff;
|
||||
}
|
||||
function ff(a, b, c, d, x, s, t) {
|
||||
return cmn((b & c) | (~b & d), a, b, x, s, t);
|
||||
}
|
||||
function gg(a, b, c, d, x, s, t) {
|
||||
return cmn((b & d) | (c & ~d), a, b, x, s, t);
|
||||
}
|
||||
function hh(a, b, c, d, x, s, t) {
|
||||
return cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function ii(a, b, c, d, x, s, t) {
|
||||
return cmn(c ^ (b | ~d), a, b, x, s, t);
|
||||
}
|
||||
function isEmptyData(data) {
|
||||
if (typeof data === "string") {
|
||||
return data.length === 0;
|
||||
}
|
||||
return data.byteLength === 0;
|
||||
}
|
||||
function convertToBuffer(data) {
|
||||
if (typeof data === "string") {
|
||||
return serde.fromUtf8(data);
|
||||
}
|
||||
if (ArrayBuffer.isView(data)) {
|
||||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength / Uint8Array.BYTES_PER_ELEMENT);
|
||||
}
|
||||
return new Uint8Array(data);
|
||||
}
|
||||
|
||||
exports.Md5 = Md5;
|
||||
exports.blobHasher = blobHasher;
|
||||
exports.blobReader = blobReader;
|
||||
exports.fileStreamHasher = fileStreamHasher;
|
||||
exports.readableStreamHasher = readableStreamHasher;
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
'use strict';
|
||||
|
||||
var serde = require('@smithy/core/serde');
|
||||
|
||||
async function blobReader$1(blob, onChunk, chunkSize = 1024 * 1024) {
|
||||
const size = blob.size;
|
||||
let totalBytesRead = 0;
|
||||
while (totalBytesRead < size) {
|
||||
const slice = blob.slice(totalBytesRead, Math.min(size, totalBytesRead + chunkSize));
|
||||
onChunk(new Uint8Array(await slice.arrayBuffer()));
|
||||
totalBytesRead += slice.size;
|
||||
}
|
||||
}
|
||||
|
||||
const blobHasher = async function blobHasher(hashCtor, blob) {
|
||||
const hash = new hashCtor();
|
||||
await blobReader$1(blob, (chunk) => {
|
||||
hash.update(chunk);
|
||||
});
|
||||
return hash.digest();
|
||||
};
|
||||
|
||||
const BLOCK_SIZE = 64;
|
||||
const DIGEST_LENGTH = 16;
|
||||
const INIT = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
|
||||
|
||||
class Md5 {
|
||||
state;
|
||||
buffer;
|
||||
bufferLength;
|
||||
bytesHashed;
|
||||
finished;
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
update(sourceData) {
|
||||
if (isEmptyData(sourceData)) {
|
||||
return;
|
||||
}
|
||||
else if (this.finished) {
|
||||
throw new Error("Attempted to update an already finished hash.");
|
||||
}
|
||||
const data = convertToBuffer(sourceData);
|
||||
let position = 0;
|
||||
let { byteLength } = data;
|
||||
this.bytesHashed += byteLength;
|
||||
while (byteLength > 0) {
|
||||
this.buffer.setUint8(this.bufferLength++, data[position++]);
|
||||
byteLength--;
|
||||
if (this.bufferLength === BLOCK_SIZE) {
|
||||
this.hashBuffer();
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
async digest() {
|
||||
if (!this.finished) {
|
||||
const { buffer, bufferLength: undecoratedLength, bytesHashed } = this;
|
||||
const bitsHashed = bytesHashed * 8;
|
||||
buffer.setUint8(this.bufferLength++, 0b10000000);
|
||||
if (undecoratedLength % BLOCK_SIZE >= BLOCK_SIZE - 8) {
|
||||
for (let i = this.bufferLength; i < BLOCK_SIZE; i++) {
|
||||
buffer.setUint8(i, 0);
|
||||
}
|
||||
this.hashBuffer();
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
for (let i = this.bufferLength; i < BLOCK_SIZE - 8; i++) {
|
||||
buffer.setUint8(i, 0);
|
||||
}
|
||||
buffer.setUint32(BLOCK_SIZE - 8, bitsHashed >>> 0, true);
|
||||
buffer.setUint32(BLOCK_SIZE - 4, Math.floor(bitsHashed / 0x100000000), true);
|
||||
this.hashBuffer();
|
||||
this.finished = true;
|
||||
}
|
||||
const out = new DataView(new ArrayBuffer(DIGEST_LENGTH));
|
||||
for (let i = 0; i < 4; i++) {
|
||||
out.setUint32(i * 4, this.state[i], true);
|
||||
}
|
||||
return new Uint8Array(out.buffer, out.byteOffset, out.byteLength);
|
||||
}
|
||||
hashBuffer() {
|
||||
const { buffer, state } = this;
|
||||
let a = state[0], b = state[1], c = state[2], d = state[3];
|
||||
a = ff(a, b, c, d, buffer.getUint32(0, true), 7, 0xd76aa478);
|
||||
d = ff(d, a, b, c, buffer.getUint32(4, true), 12, 0xe8c7b756);
|
||||
c = ff(c, d, a, b, buffer.getUint32(8, true), 17, 0x242070db);
|
||||
b = ff(b, c, d, a, buffer.getUint32(12, true), 22, 0xc1bdceee);
|
||||
a = ff(a, b, c, d, buffer.getUint32(16, true), 7, 0xf57c0faf);
|
||||
d = ff(d, a, b, c, buffer.getUint32(20, true), 12, 0x4787c62a);
|
||||
c = ff(c, d, a, b, buffer.getUint32(24, true), 17, 0xa8304613);
|
||||
b = ff(b, c, d, a, buffer.getUint32(28, true), 22, 0xfd469501);
|
||||
a = ff(a, b, c, d, buffer.getUint32(32, true), 7, 0x698098d8);
|
||||
d = ff(d, a, b, c, buffer.getUint32(36, true), 12, 0x8b44f7af);
|
||||
c = ff(c, d, a, b, buffer.getUint32(40, true), 17, 0xffff5bb1);
|
||||
b = ff(b, c, d, a, buffer.getUint32(44, true), 22, 0x895cd7be);
|
||||
a = ff(a, b, c, d, buffer.getUint32(48, true), 7, 0x6b901122);
|
||||
d = ff(d, a, b, c, buffer.getUint32(52, true), 12, 0xfd987193);
|
||||
c = ff(c, d, a, b, buffer.getUint32(56, true), 17, 0xa679438e);
|
||||
b = ff(b, c, d, a, buffer.getUint32(60, true), 22, 0x49b40821);
|
||||
a = gg(a, b, c, d, buffer.getUint32(4, true), 5, 0xf61e2562);
|
||||
d = gg(d, a, b, c, buffer.getUint32(24, true), 9, 0xc040b340);
|
||||
c = gg(c, d, a, b, buffer.getUint32(44, true), 14, 0x265e5a51);
|
||||
b = gg(b, c, d, a, buffer.getUint32(0, true), 20, 0xe9b6c7aa);
|
||||
a = gg(a, b, c, d, buffer.getUint32(20, true), 5, 0xd62f105d);
|
||||
d = gg(d, a, b, c, buffer.getUint32(40, true), 9, 0x02441453);
|
||||
c = gg(c, d, a, b, buffer.getUint32(60, true), 14, 0xd8a1e681);
|
||||
b = gg(b, c, d, a, buffer.getUint32(16, true), 20, 0xe7d3fbc8);
|
||||
a = gg(a, b, c, d, buffer.getUint32(36, true), 5, 0x21e1cde6);
|
||||
d = gg(d, a, b, c, buffer.getUint32(56, true), 9, 0xc33707d6);
|
||||
c = gg(c, d, a, b, buffer.getUint32(12, true), 14, 0xf4d50d87);
|
||||
b = gg(b, c, d, a, buffer.getUint32(32, true), 20, 0x455a14ed);
|
||||
a = gg(a, b, c, d, buffer.getUint32(52, true), 5, 0xa9e3e905);
|
||||
d = gg(d, a, b, c, buffer.getUint32(8, true), 9, 0xfcefa3f8);
|
||||
c = gg(c, d, a, b, buffer.getUint32(28, true), 14, 0x676f02d9);
|
||||
b = gg(b, c, d, a, buffer.getUint32(48, true), 20, 0x8d2a4c8a);
|
||||
a = hh(a, b, c, d, buffer.getUint32(20, true), 4, 0xfffa3942);
|
||||
d = hh(d, a, b, c, buffer.getUint32(32, true), 11, 0x8771f681);
|
||||
c = hh(c, d, a, b, buffer.getUint32(44, true), 16, 0x6d9d6122);
|
||||
b = hh(b, c, d, a, buffer.getUint32(56, true), 23, 0xfde5380c);
|
||||
a = hh(a, b, c, d, buffer.getUint32(4, true), 4, 0xa4beea44);
|
||||
d = hh(d, a, b, c, buffer.getUint32(16, true), 11, 0x4bdecfa9);
|
||||
c = hh(c, d, a, b, buffer.getUint32(28, true), 16, 0xf6bb4b60);
|
||||
b = hh(b, c, d, a, buffer.getUint32(40, true), 23, 0xbebfbc70);
|
||||
a = hh(a, b, c, d, buffer.getUint32(52, true), 4, 0x289b7ec6);
|
||||
d = hh(d, a, b, c, buffer.getUint32(0, true), 11, 0xeaa127fa);
|
||||
c = hh(c, d, a, b, buffer.getUint32(12, true), 16, 0xd4ef3085);
|
||||
b = hh(b, c, d, a, buffer.getUint32(24, true), 23, 0x04881d05);
|
||||
a = hh(a, b, c, d, buffer.getUint32(36, true), 4, 0xd9d4d039);
|
||||
d = hh(d, a, b, c, buffer.getUint32(48, true), 11, 0xe6db99e5);
|
||||
c = hh(c, d, a, b, buffer.getUint32(60, true), 16, 0x1fa27cf8);
|
||||
b = hh(b, c, d, a, buffer.getUint32(8, true), 23, 0xc4ac5665);
|
||||
a = ii(a, b, c, d, buffer.getUint32(0, true), 6, 0xf4292244);
|
||||
d = ii(d, a, b, c, buffer.getUint32(28, true), 10, 0x432aff97);
|
||||
c = ii(c, d, a, b, buffer.getUint32(56, true), 15, 0xab9423a7);
|
||||
b = ii(b, c, d, a, buffer.getUint32(20, true), 21, 0xfc93a039);
|
||||
a = ii(a, b, c, d, buffer.getUint32(48, true), 6, 0x655b59c3);
|
||||
d = ii(d, a, b, c, buffer.getUint32(12, true), 10, 0x8f0ccc92);
|
||||
c = ii(c, d, a, b, buffer.getUint32(40, true), 15, 0xffeff47d);
|
||||
b = ii(b, c, d, a, buffer.getUint32(4, true), 21, 0x85845dd1);
|
||||
a = ii(a, b, c, d, buffer.getUint32(32, true), 6, 0x6fa87e4f);
|
||||
d = ii(d, a, b, c, buffer.getUint32(60, true), 10, 0xfe2ce6e0);
|
||||
c = ii(c, d, a, b, buffer.getUint32(24, true), 15, 0xa3014314);
|
||||
b = ii(b, c, d, a, buffer.getUint32(52, true), 21, 0x4e0811a1);
|
||||
a = ii(a, b, c, d, buffer.getUint32(16, true), 6, 0xf7537e82);
|
||||
d = ii(d, a, b, c, buffer.getUint32(44, true), 10, 0xbd3af235);
|
||||
c = ii(c, d, a, b, buffer.getUint32(8, true), 15, 0x2ad7d2bb);
|
||||
b = ii(b, c, d, a, buffer.getUint32(36, true), 21, 0xeb86d391);
|
||||
state[0] = (a + state[0]) & 0xffffffff;
|
||||
state[1] = (b + state[1]) & 0xffffffff;
|
||||
state[2] = (c + state[2]) & 0xffffffff;
|
||||
state[3] = (d + state[3]) & 0xffffffff;
|
||||
}
|
||||
reset() {
|
||||
this.state = Uint32Array.from(INIT);
|
||||
this.buffer = new DataView(new ArrayBuffer(BLOCK_SIZE));
|
||||
this.bufferLength = 0;
|
||||
this.bytesHashed = 0;
|
||||
this.finished = false;
|
||||
}
|
||||
}
|
||||
function cmn(q, a, b, x, s, t) {
|
||||
a = (((a + q) & 0xffffffff) + ((x + t) & 0xffffffff)) & 0xffffffff;
|
||||
return (((a << s) | (a >>> (32 - s))) + b) & 0xffffffff;
|
||||
}
|
||||
function ff(a, b, c, d, x, s, t) {
|
||||
return cmn((b & c) | (~b & d), a, b, x, s, t);
|
||||
}
|
||||
function gg(a, b, c, d, x, s, t) {
|
||||
return cmn((b & d) | (c & ~d), a, b, x, s, t);
|
||||
}
|
||||
function hh(a, b, c, d, x, s, t) {
|
||||
return cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function ii(a, b, c, d, x, s, t) {
|
||||
return cmn(c ^ (b | ~d), a, b, x, s, t);
|
||||
}
|
||||
function isEmptyData(data) {
|
||||
if (typeof data === "string") {
|
||||
return data.length === 0;
|
||||
}
|
||||
return data.byteLength === 0;
|
||||
}
|
||||
function convertToBuffer(data) {
|
||||
if (typeof data === "string") {
|
||||
return serde.fromUtf8(data);
|
||||
}
|
||||
if (ArrayBuffer.isView(data)) {
|
||||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength / Uint8Array.BYTES_PER_ELEMENT);
|
||||
}
|
||||
return new Uint8Array(data);
|
||||
}
|
||||
|
||||
function blobReader(blob, onChunk, chunkSize = 1024 * 1024) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onerror = reject;
|
||||
fileReader.onabort = reject;
|
||||
const size = blob.size;
|
||||
let totalBytesRead = 0;
|
||||
const read = () => {
|
||||
if (totalBytesRead >= size) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
fileReader.readAsDataURL(blob.slice(totalBytesRead, Math.min(size, totalBytesRead + chunkSize)));
|
||||
};
|
||||
fileReader.onload = (event) => {
|
||||
const result = event.target.result;
|
||||
const dataOffset = result.indexOf(",") + 1;
|
||||
const data = result.substring(dataOffset);
|
||||
const decoded = serde.fromBase64(data);
|
||||
onChunk(decoded);
|
||||
totalBytesRead += decoded.byteLength;
|
||||
read();
|
||||
};
|
||||
read();
|
||||
});
|
||||
}
|
||||
|
||||
const no = Symbol.for("node-only");
|
||||
const fileStreamHasher = no;
|
||||
const readableStreamHasher = no;
|
||||
|
||||
exports.Md5 = Md5;
|
||||
exports.blobHasher = blobHasher;
|
||||
exports.blobReader = blobReader;
|
||||
exports.fileStreamHasher = fileStreamHasher;
|
||||
exports.readableStreamHasher = readableStreamHasher;
|
||||
+1128
File diff suppressed because it is too large
Load Diff
+375
@@ -0,0 +1,375 @@
|
||||
'use strict';
|
||||
|
||||
var client = require('@smithy/core/client');
|
||||
var transport = require('@smithy/core/transport');
|
||||
|
||||
class ProviderError extends Error {
|
||||
name = "ProviderError";
|
||||
tryNextLink;
|
||||
constructor(message, options = true) {
|
||||
let logger;
|
||||
let tryNextLink = true;
|
||||
if (typeof options === "boolean") {
|
||||
logger = undefined;
|
||||
tryNextLink = options;
|
||||
}
|
||||
else if (options != null && typeof options === "object") {
|
||||
logger = options.logger;
|
||||
tryNextLink = options.tryNextLink ?? true;
|
||||
}
|
||||
super(message);
|
||||
this.tryNextLink = tryNextLink;
|
||||
Object.setPrototypeOf(this, ProviderError.prototype);
|
||||
logger?.debug?.(`@smithy/property-provider ${tryNextLink ? "->" : "(!)"} ${message}`);
|
||||
}
|
||||
static from(error, options = true) {
|
||||
return Object.assign(new this(error.message, options), error);
|
||||
}
|
||||
}
|
||||
|
||||
class CredentialsProviderError extends ProviderError {
|
||||
name = "CredentialsProviderError";
|
||||
constructor(message, options = true) {
|
||||
super(message, options);
|
||||
Object.setPrototypeOf(this, CredentialsProviderError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
class TokenProviderError extends ProviderError {
|
||||
name = "TokenProviderError";
|
||||
constructor(message, options = true) {
|
||||
super(message, options);
|
||||
Object.setPrototypeOf(this, TokenProviderError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
const chain = (...providers) => async () => {
|
||||
if (providers.length === 0) {
|
||||
throw new ProviderError("No providers in chain");
|
||||
}
|
||||
let lastProviderError;
|
||||
for (const provider of providers) {
|
||||
try {
|
||||
const credentials = await provider();
|
||||
return credentials;
|
||||
}
|
||||
catch (err) {
|
||||
lastProviderError = err;
|
||||
if (err?.tryNextLink) {
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
throw lastProviderError;
|
||||
};
|
||||
|
||||
const fromValue = (staticValue) => () => Promise.resolve(staticValue);
|
||||
|
||||
const memoize = (provider, isExpired, requiresRefresh) => {
|
||||
let resolved;
|
||||
let pending;
|
||||
let hasResult;
|
||||
let isConstant = false;
|
||||
const coalesceProvider = async () => {
|
||||
if (!pending) {
|
||||
pending = provider();
|
||||
}
|
||||
try {
|
||||
resolved = await pending;
|
||||
hasResult = true;
|
||||
isConstant = false;
|
||||
}
|
||||
finally {
|
||||
pending = undefined;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
if (isExpired === undefined) {
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider();
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
}
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider();
|
||||
}
|
||||
if (isConstant) {
|
||||
return resolved;
|
||||
}
|
||||
if (requiresRefresh && !requiresRefresh(resolved)) {
|
||||
isConstant = true;
|
||||
return resolved;
|
||||
}
|
||||
if (isExpired(resolved)) {
|
||||
await coalesceProvider();
|
||||
return resolved;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
};
|
||||
|
||||
const booleanSelector = (obj, key, type) => {
|
||||
if (!(key in obj))
|
||||
return undefined;
|
||||
if (obj[key] === "true")
|
||||
return true;
|
||||
if (obj[key] === "false")
|
||||
return false;
|
||||
throw new Error(`Cannot load ${type} "${key}". Expected "true" or "false", got ${obj[key]}.`);
|
||||
};
|
||||
|
||||
const numberSelector = (obj, key, type) => {
|
||||
if (!(key in obj))
|
||||
return undefined;
|
||||
const numberValue = parseInt(obj[key], 10);
|
||||
if (Number.isNaN(numberValue)) {
|
||||
throw new TypeError(`Cannot load ${type} '${key}'. Expected number, got '${obj[key]}'.`);
|
||||
}
|
||||
return numberValue;
|
||||
};
|
||||
|
||||
exports.SelectorType = void 0;
|
||||
(function (SelectorType) {
|
||||
SelectorType["ENV"] = "env";
|
||||
SelectorType["CONFIG"] = "shared config entry";
|
||||
})(exports.SelectorType || (exports.SelectorType = {}));
|
||||
|
||||
const resolveCustomEndpointsConfig = (input) => {
|
||||
const { tls, endpoint, urlParser, useDualstackEndpoint } = input;
|
||||
return Object.assign(input, {
|
||||
tls: tls ?? true,
|
||||
endpoint: client.normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint),
|
||||
isCustomEndpoint: true,
|
||||
useDualstackEndpoint: client.normalizeProvider(useDualstackEndpoint ?? false),
|
||||
});
|
||||
};
|
||||
|
||||
const getEndpointFromRegion = async (input) => {
|
||||
const { tls = true } = input;
|
||||
const region = await input.region();
|
||||
const dnsHostRegex = new RegExp(/^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/);
|
||||
if (!dnsHostRegex.test(region)) {
|
||||
throw new Error("Invalid region in client config");
|
||||
}
|
||||
const useDualstackEndpoint = await input.useDualstackEndpoint();
|
||||
const useFipsEndpoint = await input.useFipsEndpoint();
|
||||
const { hostname } = (await input.regionInfoProvider(region, { useDualstackEndpoint, useFipsEndpoint })) ?? {};
|
||||
if (!hostname) {
|
||||
throw new Error("Cannot resolve hostname from client config");
|
||||
}
|
||||
return input.urlParser(`${tls ? "https:" : "http:"}//${hostname}`);
|
||||
};
|
||||
|
||||
const resolveEndpointsConfig = (input) => {
|
||||
const useDualstackEndpoint = client.normalizeProvider(input.useDualstackEndpoint ?? false);
|
||||
const { endpoint, useFipsEndpoint, urlParser, tls } = input;
|
||||
return Object.assign(input, {
|
||||
tls: tls ?? true,
|
||||
endpoint: endpoint
|
||||
? client.normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint)
|
||||
: () => getEndpointFromRegion({ ...input, useDualstackEndpoint, useFipsEndpoint }),
|
||||
isCustomEndpoint: !!endpoint,
|
||||
useDualstackEndpoint,
|
||||
});
|
||||
};
|
||||
|
||||
const validRegions = new Set();
|
||||
const checkRegion = (region, check = transport.isValidHostLabel) => {
|
||||
if (!validRegions.has(region) && !check(region)) {
|
||||
if (region === "*") {
|
||||
console.warn(`@smithy/config-resolver WARN - Please use the caller region instead of "*". See "sigv4a" in https://github.com/aws/aws-sdk-js-v3/blob/main/supplemental-docs/CLIENTS.md.`);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Region not accepted: region="${region}" is not a valid hostname component.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
validRegions.add(region);
|
||||
}
|
||||
};
|
||||
|
||||
const isFipsRegion = (region) => typeof region === "string" && (region.startsWith("fips-") || region.endsWith("-fips"));
|
||||
|
||||
const getRealRegion = (region) => isFipsRegion(region)
|
||||
? ["fips-aws-global", "aws-fips"].includes(region)
|
||||
? "us-east-1"
|
||||
: region.replace(/fips-(dkr-|prod-)?|-fips/, "")
|
||||
: region;
|
||||
|
||||
const resolveRegionConfig = (input) => {
|
||||
const { region, useFipsEndpoint } = input;
|
||||
if (!region) {
|
||||
throw new Error("Region is missing");
|
||||
}
|
||||
return Object.assign(input, {
|
||||
region: async () => {
|
||||
const providedRegion = typeof region === "function" ? await region() : region;
|
||||
const realRegion = getRealRegion(providedRegion);
|
||||
checkRegion(realRegion);
|
||||
return realRegion;
|
||||
},
|
||||
useFipsEndpoint: async () => {
|
||||
const providedRegion = typeof region === "string" ? region : await region();
|
||||
if (isFipsRegion(providedRegion)) {
|
||||
return true;
|
||||
}
|
||||
return typeof useFipsEndpoint !== "function" ? Promise.resolve(!!useFipsEndpoint) : useFipsEndpoint();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getHostnameFromVariants = (variants = [], { useFipsEndpoint, useDualstackEndpoint }) => variants.find(({ tags }) => useFipsEndpoint === tags.includes("fips") && useDualstackEndpoint === tags.includes("dualstack"))?.hostname;
|
||||
|
||||
const getResolvedHostname = (resolvedRegion, { regionHostname, partitionHostname }) => regionHostname
|
||||
? regionHostname
|
||||
: partitionHostname
|
||||
? partitionHostname.replace("{region}", resolvedRegion)
|
||||
: undefined;
|
||||
|
||||
const getResolvedPartition = (region, { partitionHash }) => Object.keys(partitionHash || {}).find((key) => partitionHash[key].regions.includes(region)) ?? "aws";
|
||||
|
||||
const getResolvedSigningRegion = (hostname, { signingRegion, regionRegex, useFipsEndpoint }) => {
|
||||
if (signingRegion) {
|
||||
return signingRegion;
|
||||
}
|
||||
else if (useFipsEndpoint) {
|
||||
const regionRegexJs = regionRegex.replace("\\\\", "\\").replace(/^\^/g, "\\.").replace(/\$$/g, "\\.");
|
||||
const regionRegexmatchArray = hostname.match(regionRegexJs);
|
||||
if (regionRegexmatchArray) {
|
||||
return regionRegexmatchArray[0].slice(1, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getRegionInfo = (region, { useFipsEndpoint = false, useDualstackEndpoint = false, signingService, regionHash, partitionHash, }) => {
|
||||
const partition = getResolvedPartition(region, { partitionHash });
|
||||
const resolvedRegion = region in regionHash ? region : partitionHash[partition]?.endpoint ?? region;
|
||||
const hostnameOptions = { useFipsEndpoint, useDualstackEndpoint };
|
||||
const regionHostname = getHostnameFromVariants(regionHash[resolvedRegion]?.variants, hostnameOptions);
|
||||
const partitionHostname = getHostnameFromVariants(partitionHash[partition]?.variants, hostnameOptions);
|
||||
const hostname = getResolvedHostname(resolvedRegion, { regionHostname, partitionHostname });
|
||||
if (hostname === undefined) {
|
||||
throw new Error(`Endpoint resolution failed for: ${{ resolvedRegion, useFipsEndpoint, useDualstackEndpoint }}`);
|
||||
}
|
||||
const signingRegion = getResolvedSigningRegion(hostname, {
|
||||
signingRegion: regionHash[resolvedRegion]?.signingRegion,
|
||||
regionRegex: partitionHash[partition].regionRegex,
|
||||
useFipsEndpoint,
|
||||
});
|
||||
return {
|
||||
partition,
|
||||
signingService,
|
||||
hostname,
|
||||
...(signingRegion && { signingRegion }),
|
||||
...(regionHash[resolvedRegion]?.signingService && {
|
||||
signingService: regionHash[resolvedRegion].signingService,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULTS_MODE_OPTIONS = ["in-region", "cross-region", "mobile", "standard", "legacy"];
|
||||
|
||||
const resolveDefaultsModeConfig = ({ defaultsMode, } = {}) => memoize(async () => {
|
||||
const mode = typeof defaultsMode === "function" ? await defaultsMode() : defaultsMode;
|
||||
switch (mode?.toLowerCase()) {
|
||||
case "auto":
|
||||
return Promise.resolve(useMobileConfiguration() ? "mobile" : "standard");
|
||||
case "mobile":
|
||||
case "in-region":
|
||||
case "cross-region":
|
||||
case "standard":
|
||||
case "legacy":
|
||||
return Promise.resolve(mode?.toLocaleLowerCase());
|
||||
case undefined:
|
||||
return Promise.resolve("legacy");
|
||||
default:
|
||||
throw new Error(`Invalid parameter for "defaultsMode", expect ${DEFAULTS_MODE_OPTIONS.join(", ")}, got ${mode}`);
|
||||
}
|
||||
});
|
||||
const useMobileConfiguration = () => {
|
||||
const navigator = window?.navigator;
|
||||
if (navigator?.connection) {
|
||||
const { effectiveType, rtt, downlink } = navigator?.connection;
|
||||
const slow = (typeof effectiveType === "string" && effectiveType !== "4g") || Number(rtt) > 100 || Number(downlink) < 10;
|
||||
if (slow) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return (navigator?.userAgentData?.mobile || (typeof navigator?.maxTouchPoints === "number" && navigator?.maxTouchPoints > 1));
|
||||
};
|
||||
|
||||
const no = Symbol.for("node-only");
|
||||
const getHomeDir = no;
|
||||
const ENV_PROFILE = no;
|
||||
const DEFAULT_PROFILE = "default";
|
||||
const getProfileName = no;
|
||||
const getSSOTokenFilepath = no;
|
||||
const getSSOTokenFromFile = no;
|
||||
const CONFIG_PREFIX_SEPARATOR = no;
|
||||
const loadSharedConfigFiles = no;
|
||||
const loadSsoSessionData = no;
|
||||
const parseKnownFiles = no;
|
||||
const externalDataInterceptor = no;
|
||||
const readFile = no;
|
||||
const loadConfig = no;
|
||||
const fromStatic = no;
|
||||
const ENV_USE_DUALSTACK_ENDPOINT = no;
|
||||
const CONFIG_USE_DUALSTACK_ENDPOINT = no;
|
||||
const DEFAULT_USE_DUALSTACK_ENDPOINT = false;
|
||||
const NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS = no;
|
||||
const nodeDualstackConfigSelectors = no;
|
||||
const ENV_USE_FIPS_ENDPOINT = no;
|
||||
const CONFIG_USE_FIPS_ENDPOINT = no;
|
||||
const DEFAULT_USE_FIPS_ENDPOINT = false;
|
||||
const NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS = no;
|
||||
const nodeFipsConfigSelectors = no;
|
||||
const REGION_ENV_NAME = no;
|
||||
const REGION_INI_NAME = no;
|
||||
const NODE_REGION_CONFIG_OPTIONS = no;
|
||||
const NODE_REGION_CONFIG_FILE_OPTIONS = no;
|
||||
|
||||
exports.CONFIG_PREFIX_SEPARATOR = CONFIG_PREFIX_SEPARATOR;
|
||||
exports.CONFIG_USE_DUALSTACK_ENDPOINT = CONFIG_USE_DUALSTACK_ENDPOINT;
|
||||
exports.CONFIG_USE_FIPS_ENDPOINT = CONFIG_USE_FIPS_ENDPOINT;
|
||||
exports.CredentialsProviderError = CredentialsProviderError;
|
||||
exports.DEFAULT_PROFILE = DEFAULT_PROFILE;
|
||||
exports.DEFAULT_USE_DUALSTACK_ENDPOINT = DEFAULT_USE_DUALSTACK_ENDPOINT;
|
||||
exports.DEFAULT_USE_FIPS_ENDPOINT = DEFAULT_USE_FIPS_ENDPOINT;
|
||||
exports.ENV_PROFILE = ENV_PROFILE;
|
||||
exports.ENV_USE_DUALSTACK_ENDPOINT = ENV_USE_DUALSTACK_ENDPOINT;
|
||||
exports.ENV_USE_FIPS_ENDPOINT = ENV_USE_FIPS_ENDPOINT;
|
||||
exports.NODE_REGION_CONFIG_FILE_OPTIONS = NODE_REGION_CONFIG_FILE_OPTIONS;
|
||||
exports.NODE_REGION_CONFIG_OPTIONS = NODE_REGION_CONFIG_OPTIONS;
|
||||
exports.NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS = NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS;
|
||||
exports.NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS = NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS;
|
||||
exports.ProviderError = ProviderError;
|
||||
exports.REGION_ENV_NAME = REGION_ENV_NAME;
|
||||
exports.REGION_INI_NAME = REGION_INI_NAME;
|
||||
exports.TokenProviderError = TokenProviderError;
|
||||
exports.booleanSelector = booleanSelector;
|
||||
exports.chain = chain;
|
||||
exports.externalDataInterceptor = externalDataInterceptor;
|
||||
exports.fromStatic = fromStatic;
|
||||
exports.fromValue = fromValue;
|
||||
exports.getHomeDir = getHomeDir;
|
||||
exports.getProfileName = getProfileName;
|
||||
exports.getRegionInfo = getRegionInfo;
|
||||
exports.getSSOTokenFilepath = getSSOTokenFilepath;
|
||||
exports.getSSOTokenFromFile = getSSOTokenFromFile;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.loadSharedConfigFiles = loadSharedConfigFiles;
|
||||
exports.loadSsoSessionData = loadSsoSessionData;
|
||||
exports.memoize = memoize;
|
||||
exports.nodeDualstackConfigSelectors = nodeDualstackConfigSelectors;
|
||||
exports.nodeFipsConfigSelectors = nodeFipsConfigSelectors;
|
||||
exports.numberSelector = numberSelector;
|
||||
exports.parseKnownFiles = parseKnownFiles;
|
||||
exports.readFile = readFile;
|
||||
exports.resolveCustomEndpointsConfig = resolveCustomEndpointsConfig;
|
||||
exports.resolveDefaultsModeConfig = resolveDefaultsModeConfig;
|
||||
exports.resolveEndpointsConfig = resolveEndpointsConfig;
|
||||
exports.resolveRegionConfig = resolveRegionConfig;
|
||||
+735
@@ -0,0 +1,735 @@
|
||||
'use strict';
|
||||
|
||||
var node_os = require('node:os');
|
||||
var node_path = require('node:path');
|
||||
var node_crypto = require('node:crypto');
|
||||
var promises = require('node:fs/promises');
|
||||
var types = require('@smithy/types');
|
||||
var client = require('@smithy/core/client');
|
||||
var transport = require('@smithy/core/transport');
|
||||
|
||||
class ProviderError extends Error {
|
||||
name = "ProviderError";
|
||||
tryNextLink;
|
||||
constructor(message, options = true) {
|
||||
let logger;
|
||||
let tryNextLink = true;
|
||||
if (typeof options === "boolean") {
|
||||
logger = undefined;
|
||||
tryNextLink = options;
|
||||
}
|
||||
else if (options != null && typeof options === "object") {
|
||||
logger = options.logger;
|
||||
tryNextLink = options.tryNextLink ?? true;
|
||||
}
|
||||
super(message);
|
||||
this.tryNextLink = tryNextLink;
|
||||
Object.setPrototypeOf(this, ProviderError.prototype);
|
||||
logger?.debug?.(`@smithy/property-provider ${tryNextLink ? "->" : "(!)"} ${message}`);
|
||||
}
|
||||
static from(error, options = true) {
|
||||
return Object.assign(new this(error.message, options), error);
|
||||
}
|
||||
}
|
||||
|
||||
class CredentialsProviderError extends ProviderError {
|
||||
name = "CredentialsProviderError";
|
||||
constructor(message, options = true) {
|
||||
super(message, options);
|
||||
Object.setPrototypeOf(this, CredentialsProviderError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
class TokenProviderError extends ProviderError {
|
||||
name = "TokenProviderError";
|
||||
constructor(message, options = true) {
|
||||
super(message, options);
|
||||
Object.setPrototypeOf(this, TokenProviderError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
const chain = (...providers) => async () => {
|
||||
if (providers.length === 0) {
|
||||
throw new ProviderError("No providers in chain");
|
||||
}
|
||||
let lastProviderError;
|
||||
for (const provider of providers) {
|
||||
try {
|
||||
const credentials = await provider();
|
||||
return credentials;
|
||||
}
|
||||
catch (err) {
|
||||
lastProviderError = err;
|
||||
if (err?.tryNextLink) {
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
throw lastProviderError;
|
||||
};
|
||||
|
||||
const fromValue = (staticValue) => () => Promise.resolve(staticValue);
|
||||
|
||||
const memoize = (provider, isExpired, requiresRefresh) => {
|
||||
let resolved;
|
||||
let pending;
|
||||
let hasResult;
|
||||
let isConstant = false;
|
||||
const coalesceProvider = async () => {
|
||||
if (!pending) {
|
||||
pending = provider();
|
||||
}
|
||||
try {
|
||||
resolved = await pending;
|
||||
hasResult = true;
|
||||
isConstant = false;
|
||||
}
|
||||
finally {
|
||||
pending = undefined;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
if (isExpired === undefined) {
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider();
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
}
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider();
|
||||
}
|
||||
if (isConstant) {
|
||||
return resolved;
|
||||
}
|
||||
if (requiresRefresh && !requiresRefresh(resolved)) {
|
||||
isConstant = true;
|
||||
return resolved;
|
||||
}
|
||||
if (isExpired(resolved)) {
|
||||
await coalesceProvider();
|
||||
return resolved;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
};
|
||||
|
||||
const booleanSelector = (obj, key, type) => {
|
||||
if (!(key in obj))
|
||||
return undefined;
|
||||
if (obj[key] === "true")
|
||||
return true;
|
||||
if (obj[key] === "false")
|
||||
return false;
|
||||
throw new Error(`Cannot load ${type} "${key}". Expected "true" or "false", got ${obj[key]}.`);
|
||||
};
|
||||
|
||||
const numberSelector = (obj, key, type) => {
|
||||
if (!(key in obj))
|
||||
return undefined;
|
||||
const numberValue = parseInt(obj[key], 10);
|
||||
if (Number.isNaN(numberValue)) {
|
||||
throw new TypeError(`Cannot load ${type} '${key}'. Expected number, got '${obj[key]}'.`);
|
||||
}
|
||||
return numberValue;
|
||||
};
|
||||
|
||||
exports.SelectorType = void 0;
|
||||
(function (SelectorType) {
|
||||
SelectorType["ENV"] = "env";
|
||||
SelectorType["CONFIG"] = "shared config entry";
|
||||
})(exports.SelectorType || (exports.SelectorType = {}));
|
||||
|
||||
const homeDirCache = {};
|
||||
const getHomeDirCacheKey = () => {
|
||||
if (process && process.geteuid) {
|
||||
return `${process.geteuid()}`;
|
||||
}
|
||||
return "DEFAULT";
|
||||
};
|
||||
const getHomeDir = () => {
|
||||
const { HOME, USERPROFILE, HOMEPATH, HOMEDRIVE = `C:${node_path.sep}` } = process.env;
|
||||
if (HOME)
|
||||
return HOME;
|
||||
if (USERPROFILE)
|
||||
return USERPROFILE;
|
||||
if (HOMEPATH)
|
||||
return `${HOMEDRIVE}${HOMEPATH}`;
|
||||
const homeDirCacheKey = getHomeDirCacheKey();
|
||||
if (!homeDirCache[homeDirCacheKey])
|
||||
homeDirCache[homeDirCacheKey] = node_os.homedir();
|
||||
return homeDirCache[homeDirCacheKey];
|
||||
};
|
||||
|
||||
const ENV_PROFILE = "AWS_PROFILE";
|
||||
const DEFAULT_PROFILE = "default";
|
||||
const getProfileName = (init) => init.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE;
|
||||
|
||||
const getSSOTokenFilepath = (id) => {
|
||||
const hasher = node_crypto.createHash("sha1");
|
||||
const cacheName = hasher.update(id).digest("hex");
|
||||
return node_path.join(getHomeDir(), ".aws", "sso", "cache", `${cacheName}.json`);
|
||||
};
|
||||
|
||||
const tokenIntercept = {};
|
||||
const getSSOTokenFromFile = async (id) => {
|
||||
if (tokenIntercept[id]) {
|
||||
return tokenIntercept[id];
|
||||
}
|
||||
const ssoTokenFilepath = getSSOTokenFilepath(id);
|
||||
const ssoTokenText = await promises.readFile(ssoTokenFilepath, "utf8");
|
||||
return JSON.parse(ssoTokenText);
|
||||
};
|
||||
|
||||
const CONFIG_PREFIX_SEPARATOR = ".";
|
||||
|
||||
const getConfigData = (data) => Object.entries(data)
|
||||
.filter(([key]) => {
|
||||
const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
|
||||
if (indexOfSeparator === -1) {
|
||||
return false;
|
||||
}
|
||||
return Object.values(types.IniSectionType).includes(key.substring(0, indexOfSeparator));
|
||||
})
|
||||
.reduce((acc, [key, value]) => {
|
||||
const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
|
||||
const updatedKey = key.substring(0, indexOfSeparator) === types.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
|
||||
acc[updatedKey] = value;
|
||||
return acc;
|
||||
}, {
|
||||
...(data.default && { default: data.default }),
|
||||
});
|
||||
|
||||
const ENV_CONFIG_PATH = "AWS_CONFIG_FILE";
|
||||
const getConfigFilepath = () => process.env[ENV_CONFIG_PATH] || node_path.join(getHomeDir(), ".aws", "config");
|
||||
|
||||
const ENV_CREDENTIALS_PATH = "AWS_SHARED_CREDENTIALS_FILE";
|
||||
const getCredentialsFilepath = () => process.env[ENV_CREDENTIALS_PATH] || node_path.join(getHomeDir(), ".aws", "credentials");
|
||||
|
||||
const prefixKeyRegex = /^([\w-]+)\s(["'])?([\w-@\+\.%:/]+)\2$/;
|
||||
const profileNameBlockList = ["__proto__", "profile __proto__"];
|
||||
const parseIni = (iniData) => {
|
||||
const map = {};
|
||||
let currentSection;
|
||||
let currentSubSection;
|
||||
for (const iniLine of iniData.split(/\r?\n/)) {
|
||||
const trimmedLine = iniLine.split(/(^|\s)[;#]/)[0].trim();
|
||||
const isSection = trimmedLine[0] === "[" && trimmedLine[trimmedLine.length - 1] === "]";
|
||||
if (isSection) {
|
||||
currentSection = undefined;
|
||||
currentSubSection = undefined;
|
||||
const sectionName = trimmedLine.substring(1, trimmedLine.length - 1);
|
||||
const matches = prefixKeyRegex.exec(sectionName);
|
||||
if (matches) {
|
||||
const [, prefix, , name] = matches;
|
||||
if (Object.values(types.IniSectionType).includes(prefix)) {
|
||||
currentSection = [prefix, name].join(CONFIG_PREFIX_SEPARATOR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
currentSection = sectionName;
|
||||
}
|
||||
if (profileNameBlockList.includes(sectionName)) {
|
||||
throw new Error(`Found invalid profile name "${sectionName}"`);
|
||||
}
|
||||
}
|
||||
else if (currentSection) {
|
||||
const indexOfEqualsSign = trimmedLine.indexOf("=");
|
||||
if (![0, -1].includes(indexOfEqualsSign)) {
|
||||
const [name, value] = [
|
||||
trimmedLine.substring(0, indexOfEqualsSign).trim(),
|
||||
trimmedLine.substring(indexOfEqualsSign + 1).trim(),
|
||||
];
|
||||
if (value === "") {
|
||||
currentSubSection = name;
|
||||
}
|
||||
else {
|
||||
if (currentSubSection && iniLine.trimStart() === iniLine) {
|
||||
currentSubSection = undefined;
|
||||
}
|
||||
map[currentSection] = map[currentSection] || {};
|
||||
const key = currentSubSection ? [currentSubSection, name].join(CONFIG_PREFIX_SEPARATOR) : name;
|
||||
map[currentSection][key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
};
|
||||
|
||||
const filePromises = {};
|
||||
const fileIntercept = {};
|
||||
const readFile = (path, options) => {
|
||||
if (fileIntercept[path] !== undefined) {
|
||||
return fileIntercept[path];
|
||||
}
|
||||
if (!filePromises[path] || options?.ignoreCache) {
|
||||
filePromises[path] = promises.readFile(path, "utf8");
|
||||
}
|
||||
return filePromises[path];
|
||||
};
|
||||
|
||||
const swallowError$1 = () => ({});
|
||||
const loadSharedConfigFiles = async (init = {}) => {
|
||||
const { filepath = getCredentialsFilepath(), configFilepath = getConfigFilepath() } = init;
|
||||
const homeDir = getHomeDir();
|
||||
const relativeHomeDirPrefix = "~/";
|
||||
let resolvedFilepath = filepath;
|
||||
if (filepath.startsWith(relativeHomeDirPrefix)) {
|
||||
resolvedFilepath = node_path.join(homeDir, filepath.slice(2));
|
||||
}
|
||||
let resolvedConfigFilepath = configFilepath;
|
||||
if (configFilepath.startsWith(relativeHomeDirPrefix)) {
|
||||
resolvedConfigFilepath = node_path.join(homeDir, configFilepath.slice(2));
|
||||
}
|
||||
const parsedFiles = await Promise.all([
|
||||
readFile(resolvedConfigFilepath, {
|
||||
ignoreCache: init.ignoreCache,
|
||||
})
|
||||
.then(parseIni)
|
||||
.then(getConfigData)
|
||||
.catch(swallowError$1),
|
||||
readFile(resolvedFilepath, {
|
||||
ignoreCache: init.ignoreCache,
|
||||
})
|
||||
.then(parseIni)
|
||||
.catch(swallowError$1),
|
||||
]);
|
||||
return {
|
||||
configFile: parsedFiles[0],
|
||||
credentialsFile: parsedFiles[1],
|
||||
};
|
||||
};
|
||||
|
||||
const getSsoSessionData = (data) => Object.entries(data)
|
||||
.filter(([key]) => key.startsWith(types.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR))
|
||||
.reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {});
|
||||
|
||||
const swallowError = () => ({});
|
||||
const loadSsoSessionData = async (init = {}) => readFile(init.configFilepath ?? getConfigFilepath())
|
||||
.then(parseIni)
|
||||
.then(getSsoSessionData)
|
||||
.catch(swallowError);
|
||||
|
||||
const mergeConfigFiles = (...files) => {
|
||||
const merged = {};
|
||||
for (const file of files) {
|
||||
for (const [key, values] of Object.entries(file)) {
|
||||
if (merged[key] !== undefined) {
|
||||
Object.assign(merged[key], values);
|
||||
}
|
||||
else {
|
||||
merged[key] = values;
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
};
|
||||
|
||||
const parseKnownFiles = async (init) => {
|
||||
const parsedFiles = await loadSharedConfigFiles(init);
|
||||
return mergeConfigFiles(parsedFiles.configFile, parsedFiles.credentialsFile);
|
||||
};
|
||||
|
||||
const externalDataInterceptor = {
|
||||
getFileRecord() {
|
||||
return fileIntercept;
|
||||
},
|
||||
interceptFile(path, contents) {
|
||||
fileIntercept[path] = Promise.resolve(contents);
|
||||
},
|
||||
getTokenRecord() {
|
||||
return tokenIntercept;
|
||||
},
|
||||
interceptToken(id, contents) {
|
||||
tokenIntercept[id] = contents;
|
||||
},
|
||||
};
|
||||
|
||||
function getSelectorName(functionString) {
|
||||
try {
|
||||
const constants = new Set(Array.from(functionString.match(/([A-Z_]){3,}/g) ?? []));
|
||||
constants.delete("CONFIG");
|
||||
constants.delete("CONFIG_PREFIX_SEPARATOR");
|
||||
constants.delete("ENV");
|
||||
return [...constants].join(", ");
|
||||
}
|
||||
catch (e) {
|
||||
return functionString;
|
||||
}
|
||||
}
|
||||
|
||||
const fromEnv = (envVarSelector, options) => async () => {
|
||||
try {
|
||||
const config = envVarSelector(process.env, options);
|
||||
if (config === undefined) {
|
||||
throw new Error();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
catch (e) {
|
||||
throw new CredentialsProviderError(e.message || `Not found in ENV: ${getSelectorName(envVarSelector.toString())}`, { logger: options?.logger });
|
||||
}
|
||||
};
|
||||
|
||||
const fromSharedConfigFiles = (configSelector, { preferredFile = "config", ...init } = {}) => async () => {
|
||||
const profile = getProfileName(init);
|
||||
const { configFile, credentialsFile } = await loadSharedConfigFiles(init);
|
||||
const profileFromCredentials = credentialsFile[profile] || {};
|
||||
const profileFromConfig = configFile[profile] || {};
|
||||
const mergedProfile = preferredFile === "config"
|
||||
? { ...profileFromCredentials, ...profileFromConfig }
|
||||
: { ...profileFromConfig, ...profileFromCredentials };
|
||||
try {
|
||||
const cfgFile = preferredFile === "config" ? configFile : credentialsFile;
|
||||
const configValue = configSelector(mergedProfile, cfgFile);
|
||||
if (configValue === undefined) {
|
||||
throw new Error();
|
||||
}
|
||||
return configValue;
|
||||
}
|
||||
catch (e) {
|
||||
throw new CredentialsProviderError(e.message || `Not found in config files w/ profile [${profile}]: ${getSelectorName(configSelector.toString())}`, { logger: init.logger });
|
||||
}
|
||||
};
|
||||
|
||||
const isFunction = (func) => typeof func === "function";
|
||||
const fromStatic = (defaultValue) => isFunction(defaultValue) ? async () => await defaultValue() : fromValue(defaultValue);
|
||||
|
||||
const loadConfig = ({ environmentVariableSelector, configFileSelector, default: defaultValue }, configuration = {}) => {
|
||||
const { signingName, logger } = configuration;
|
||||
const envOptions = { signingName, logger };
|
||||
return memoize(chain(fromEnv(environmentVariableSelector, envOptions), fromSharedConfigFiles(configFileSelector, configuration), fromStatic(defaultValue)));
|
||||
};
|
||||
|
||||
const ENV_USE_DUALSTACK_ENDPOINT = "AWS_USE_DUALSTACK_ENDPOINT";
|
||||
const CONFIG_USE_DUALSTACK_ENDPOINT = "use_dualstack_endpoint";
|
||||
const DEFAULT_USE_DUALSTACK_ENDPOINT = false;
|
||||
const NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS = {
|
||||
environmentVariableSelector: (env) => booleanSelector(env, ENV_USE_DUALSTACK_ENDPOINT, exports.SelectorType.ENV),
|
||||
configFileSelector: (profile) => booleanSelector(profile, CONFIG_USE_DUALSTACK_ENDPOINT, exports.SelectorType.CONFIG),
|
||||
default: false,
|
||||
};
|
||||
const nodeDualstackConfigSelectors = {
|
||||
environmentVariableSelector: (env) => booleanSelector(env, ENV_USE_DUALSTACK_ENDPOINT, exports.SelectorType.ENV),
|
||||
configFileSelector: (profile) => booleanSelector(profile, CONFIG_USE_DUALSTACK_ENDPOINT, exports.SelectorType.CONFIG),
|
||||
default: undefined,
|
||||
};
|
||||
|
||||
const ENV_USE_FIPS_ENDPOINT = "AWS_USE_FIPS_ENDPOINT";
|
||||
const CONFIG_USE_FIPS_ENDPOINT = "use_fips_endpoint";
|
||||
const DEFAULT_USE_FIPS_ENDPOINT = false;
|
||||
const NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS = {
|
||||
environmentVariableSelector: (env) => booleanSelector(env, ENV_USE_FIPS_ENDPOINT, exports.SelectorType.ENV),
|
||||
configFileSelector: (profile) => booleanSelector(profile, CONFIG_USE_FIPS_ENDPOINT, exports.SelectorType.CONFIG),
|
||||
default: false,
|
||||
};
|
||||
const nodeFipsConfigSelectors = {
|
||||
environmentVariableSelector: (env) => booleanSelector(env, ENV_USE_FIPS_ENDPOINT, exports.SelectorType.ENV),
|
||||
configFileSelector: (profile) => booleanSelector(profile, CONFIG_USE_FIPS_ENDPOINT, exports.SelectorType.CONFIG),
|
||||
default: undefined,
|
||||
};
|
||||
|
||||
const resolveCustomEndpointsConfig = (input) => {
|
||||
const { tls, endpoint, urlParser, useDualstackEndpoint } = input;
|
||||
return Object.assign(input, {
|
||||
tls: tls ?? true,
|
||||
endpoint: client.normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint),
|
||||
isCustomEndpoint: true,
|
||||
useDualstackEndpoint: client.normalizeProvider(useDualstackEndpoint ?? false),
|
||||
});
|
||||
};
|
||||
|
||||
const getEndpointFromRegion = async (input) => {
|
||||
const { tls = true } = input;
|
||||
const region = await input.region();
|
||||
const dnsHostRegex = new RegExp(/^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/);
|
||||
if (!dnsHostRegex.test(region)) {
|
||||
throw new Error("Invalid region in client config");
|
||||
}
|
||||
const useDualstackEndpoint = await input.useDualstackEndpoint();
|
||||
const useFipsEndpoint = await input.useFipsEndpoint();
|
||||
const { hostname } = (await input.regionInfoProvider(region, { useDualstackEndpoint, useFipsEndpoint })) ?? {};
|
||||
if (!hostname) {
|
||||
throw new Error("Cannot resolve hostname from client config");
|
||||
}
|
||||
return input.urlParser(`${tls ? "https:" : "http:"}//${hostname}`);
|
||||
};
|
||||
|
||||
const resolveEndpointsConfig = (input) => {
|
||||
const useDualstackEndpoint = client.normalizeProvider(input.useDualstackEndpoint ?? false);
|
||||
const { endpoint, useFipsEndpoint, urlParser, tls } = input;
|
||||
return Object.assign(input, {
|
||||
tls: tls ?? true,
|
||||
endpoint: endpoint
|
||||
? client.normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint)
|
||||
: () => getEndpointFromRegion({ ...input, useDualstackEndpoint, useFipsEndpoint }),
|
||||
isCustomEndpoint: !!endpoint,
|
||||
useDualstackEndpoint,
|
||||
});
|
||||
};
|
||||
|
||||
const REGION_ENV_NAME = "AWS_REGION";
|
||||
const REGION_INI_NAME = "region";
|
||||
const NODE_REGION_CONFIG_OPTIONS = {
|
||||
environmentVariableSelector: (env) => env[REGION_ENV_NAME],
|
||||
configFileSelector: (profile) => profile[REGION_INI_NAME],
|
||||
default: () => {
|
||||
throw new Error("Region is missing");
|
||||
},
|
||||
};
|
||||
const NODE_REGION_CONFIG_FILE_OPTIONS = {
|
||||
preferredFile: "credentials",
|
||||
};
|
||||
|
||||
const validRegions = new Set();
|
||||
const checkRegion = (region, check = transport.isValidHostLabel) => {
|
||||
if (!validRegions.has(region) && !check(region)) {
|
||||
if (region === "*") {
|
||||
console.warn(`@smithy/config-resolver WARN - Please use the caller region instead of "*". See "sigv4a" in https://github.com/aws/aws-sdk-js-v3/blob/main/supplemental-docs/CLIENTS.md.`);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Region not accepted: region="${region}" is not a valid hostname component.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
validRegions.add(region);
|
||||
}
|
||||
};
|
||||
|
||||
const isFipsRegion = (region) => typeof region === "string" && (region.startsWith("fips-") || region.endsWith("-fips"));
|
||||
|
||||
const getRealRegion = (region) => isFipsRegion(region)
|
||||
? ["fips-aws-global", "aws-fips"].includes(region)
|
||||
? "us-east-1"
|
||||
: region.replace(/fips-(dkr-|prod-)?|-fips/, "")
|
||||
: region;
|
||||
|
||||
const resolveRegionConfig = (input) => {
|
||||
const { region, useFipsEndpoint } = input;
|
||||
if (!region) {
|
||||
throw new Error("Region is missing");
|
||||
}
|
||||
return Object.assign(input, {
|
||||
region: async () => {
|
||||
const providedRegion = typeof region === "function" ? await region() : region;
|
||||
const realRegion = getRealRegion(providedRegion);
|
||||
checkRegion(realRegion);
|
||||
return realRegion;
|
||||
},
|
||||
useFipsEndpoint: async () => {
|
||||
const providedRegion = typeof region === "string" ? region : await region();
|
||||
if (isFipsRegion(providedRegion)) {
|
||||
return true;
|
||||
}
|
||||
return typeof useFipsEndpoint !== "function" ? Promise.resolve(!!useFipsEndpoint) : useFipsEndpoint();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getHostnameFromVariants = (variants = [], { useFipsEndpoint, useDualstackEndpoint }) => variants.find(({ tags }) => useFipsEndpoint === tags.includes("fips") && useDualstackEndpoint === tags.includes("dualstack"))?.hostname;
|
||||
|
||||
const getResolvedHostname = (resolvedRegion, { regionHostname, partitionHostname }) => regionHostname
|
||||
? regionHostname
|
||||
: partitionHostname
|
||||
? partitionHostname.replace("{region}", resolvedRegion)
|
||||
: undefined;
|
||||
|
||||
const getResolvedPartition = (region, { partitionHash }) => Object.keys(partitionHash || {}).find((key) => partitionHash[key].regions.includes(region)) ?? "aws";
|
||||
|
||||
const getResolvedSigningRegion = (hostname, { signingRegion, regionRegex, useFipsEndpoint }) => {
|
||||
if (signingRegion) {
|
||||
return signingRegion;
|
||||
}
|
||||
else if (useFipsEndpoint) {
|
||||
const regionRegexJs = regionRegex.replace("\\\\", "\\").replace(/^\^/g, "\\.").replace(/\$$/g, "\\.");
|
||||
const regionRegexmatchArray = hostname.match(regionRegexJs);
|
||||
if (regionRegexmatchArray) {
|
||||
return regionRegexmatchArray[0].slice(1, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getRegionInfo = (region, { useFipsEndpoint = false, useDualstackEndpoint = false, signingService, regionHash, partitionHash, }) => {
|
||||
const partition = getResolvedPartition(region, { partitionHash });
|
||||
const resolvedRegion = region in regionHash ? region : partitionHash[partition]?.endpoint ?? region;
|
||||
const hostnameOptions = { useFipsEndpoint, useDualstackEndpoint };
|
||||
const regionHostname = getHostnameFromVariants(regionHash[resolvedRegion]?.variants, hostnameOptions);
|
||||
const partitionHostname = getHostnameFromVariants(partitionHash[partition]?.variants, hostnameOptions);
|
||||
const hostname = getResolvedHostname(resolvedRegion, { regionHostname, partitionHostname });
|
||||
if (hostname === undefined) {
|
||||
throw new Error(`Endpoint resolution failed for: ${{ resolvedRegion, useFipsEndpoint, useDualstackEndpoint }}`);
|
||||
}
|
||||
const signingRegion = getResolvedSigningRegion(hostname, {
|
||||
signingRegion: regionHash[resolvedRegion]?.signingRegion,
|
||||
regionRegex: partitionHash[partition].regionRegex,
|
||||
useFipsEndpoint,
|
||||
});
|
||||
return {
|
||||
partition,
|
||||
signingService,
|
||||
hostname,
|
||||
...(signingRegion && { signingRegion }),
|
||||
...(regionHash[resolvedRegion]?.signingService && {
|
||||
signingService: regionHash[resolvedRegion].signingService,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
const AWS_EXECUTION_ENV = "AWS_EXECUTION_ENV";
|
||||
const AWS_REGION_ENV = "AWS_REGION";
|
||||
const AWS_DEFAULT_REGION_ENV = "AWS_DEFAULT_REGION";
|
||||
const ENV_IMDS_DISABLED = "AWS_EC2_METADATA_DISABLED";
|
||||
const DEFAULTS_MODE_OPTIONS = ["in-region", "cross-region", "mobile", "standard", "legacy"];
|
||||
const IMDS_REGION_PATH = "/latest/meta-data/placement/region";
|
||||
|
||||
const AWS_DEFAULTS_MODE_ENV = "AWS_DEFAULTS_MODE";
|
||||
const AWS_DEFAULTS_MODE_CONFIG = "defaults_mode";
|
||||
const NODE_DEFAULTS_MODE_CONFIG_OPTIONS = {
|
||||
environmentVariableSelector: (env) => {
|
||||
return env[AWS_DEFAULTS_MODE_ENV];
|
||||
},
|
||||
configFileSelector: (profile) => {
|
||||
return profile[AWS_DEFAULTS_MODE_CONFIG];
|
||||
},
|
||||
default: "legacy",
|
||||
};
|
||||
|
||||
const resolveDefaultsModeConfig = ({ region = loadConfig(NODE_REGION_CONFIG_OPTIONS), defaultsMode = loadConfig(NODE_DEFAULTS_MODE_CONFIG_OPTIONS), } = {}) => memoize(async () => {
|
||||
const mode = typeof defaultsMode === "function" ? await defaultsMode() : defaultsMode;
|
||||
switch (mode?.toLowerCase()) {
|
||||
case "auto":
|
||||
return resolveNodeDefaultsModeAuto(region);
|
||||
case "in-region":
|
||||
case "cross-region":
|
||||
case "mobile":
|
||||
case "standard":
|
||||
case "legacy":
|
||||
return Promise.resolve(mode?.toLocaleLowerCase());
|
||||
case undefined:
|
||||
return Promise.resolve("legacy");
|
||||
default:
|
||||
throw new Error(`Invalid parameter for "defaultsMode", expect ${DEFAULTS_MODE_OPTIONS.join(", ")}, got ${mode}`);
|
||||
}
|
||||
});
|
||||
const resolveNodeDefaultsModeAuto = async (clientRegion) => {
|
||||
if (clientRegion) {
|
||||
const resolvedRegion = typeof clientRegion === "function" ? await clientRegion() : clientRegion;
|
||||
const inferredRegion = await inferPhysicalRegion();
|
||||
if (!inferredRegion) {
|
||||
return "standard";
|
||||
}
|
||||
if (resolvedRegion === inferredRegion) {
|
||||
return "in-region";
|
||||
}
|
||||
else {
|
||||
return "cross-region";
|
||||
}
|
||||
}
|
||||
return "standard";
|
||||
};
|
||||
const inferPhysicalRegion = async () => {
|
||||
if (process.env[AWS_EXECUTION_ENV] && (process.env[AWS_REGION_ENV] || process.env[AWS_DEFAULT_REGION_ENV])) {
|
||||
return process.env[AWS_REGION_ENV] ?? process.env[AWS_DEFAULT_REGION_ENV];
|
||||
}
|
||||
if (!process.env[ENV_IMDS_DISABLED]) {
|
||||
try {
|
||||
const endpoint = await getImdsEndpoint();
|
||||
return (await imdsHttpGet({ hostname: endpoint.hostname, path: IMDS_REGION_PATH })).toString();
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
const getImdsEndpoint = async () => {
|
||||
const envEndpoint = process.env.AWS_EC2_METADATA_SERVICE_ENDPOINT;
|
||||
if (envEndpoint) {
|
||||
const url = new URL(envEndpoint);
|
||||
return { hostname: url.hostname, path: url.pathname };
|
||||
}
|
||||
const envMode = process.env.AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE;
|
||||
if (envMode === "IPv6") {
|
||||
return { hostname: "fd00:ec2::254", path: "/" };
|
||||
}
|
||||
return { hostname: "169.254.169.254", path: "/" };
|
||||
};
|
||||
const imdsHttpGet = async ({ hostname, path }) => {
|
||||
const { request } = await import('node:http');
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = request({
|
||||
method: "GET",
|
||||
hostname: hostname.replace(/^\[(.+)]$/, "$1"),
|
||||
path,
|
||||
timeout: 1000,
|
||||
signal: AbortSignal.timeout(1000),
|
||||
});
|
||||
req.on("error", (err) => {
|
||||
reject(err);
|
||||
req.destroy();
|
||||
});
|
||||
req.on("timeout", () => {
|
||||
reject(new Error("TimeoutError from instance metadata service"));
|
||||
req.destroy();
|
||||
});
|
||||
req.on("response", (res) => {
|
||||
const { statusCode = 400 } = res;
|
||||
if (statusCode < 200 || 300 <= statusCode) {
|
||||
reject(Object.assign(new Error("Error response received from instance metadata service"), { statusCode }));
|
||||
req.destroy();
|
||||
return;
|
||||
}
|
||||
const chunks = [];
|
||||
res.on("data", (chunk) => chunks.push(chunk));
|
||||
res.on("end", () => {
|
||||
resolve(Buffer.concat(chunks));
|
||||
req.destroy();
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
};
|
||||
|
||||
exports.CONFIG_PREFIX_SEPARATOR = CONFIG_PREFIX_SEPARATOR;
|
||||
exports.CONFIG_USE_DUALSTACK_ENDPOINT = CONFIG_USE_DUALSTACK_ENDPOINT;
|
||||
exports.CONFIG_USE_FIPS_ENDPOINT = CONFIG_USE_FIPS_ENDPOINT;
|
||||
exports.CredentialsProviderError = CredentialsProviderError;
|
||||
exports.DEFAULT_PROFILE = DEFAULT_PROFILE;
|
||||
exports.DEFAULT_USE_DUALSTACK_ENDPOINT = DEFAULT_USE_DUALSTACK_ENDPOINT;
|
||||
exports.DEFAULT_USE_FIPS_ENDPOINT = DEFAULT_USE_FIPS_ENDPOINT;
|
||||
exports.ENV_PROFILE = ENV_PROFILE;
|
||||
exports.ENV_USE_DUALSTACK_ENDPOINT = ENV_USE_DUALSTACK_ENDPOINT;
|
||||
exports.ENV_USE_FIPS_ENDPOINT = ENV_USE_FIPS_ENDPOINT;
|
||||
exports.NODE_REGION_CONFIG_FILE_OPTIONS = NODE_REGION_CONFIG_FILE_OPTIONS;
|
||||
exports.NODE_REGION_CONFIG_OPTIONS = NODE_REGION_CONFIG_OPTIONS;
|
||||
exports.NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS = NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS;
|
||||
exports.NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS = NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS;
|
||||
exports.ProviderError = ProviderError;
|
||||
exports.REGION_ENV_NAME = REGION_ENV_NAME;
|
||||
exports.REGION_INI_NAME = REGION_INI_NAME;
|
||||
exports.TokenProviderError = TokenProviderError;
|
||||
exports.booleanSelector = booleanSelector;
|
||||
exports.chain = chain;
|
||||
exports.externalDataInterceptor = externalDataInterceptor;
|
||||
exports.fromStatic = fromStatic;
|
||||
exports.fromValue = fromValue;
|
||||
exports.getHomeDir = getHomeDir;
|
||||
exports.getProfileName = getProfileName;
|
||||
exports.getRegionInfo = getRegionInfo;
|
||||
exports.getSSOTokenFilepath = getSSOTokenFilepath;
|
||||
exports.getSSOTokenFromFile = getSSOTokenFromFile;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.loadSharedConfigFiles = loadSharedConfigFiles;
|
||||
exports.loadSsoSessionData = loadSsoSessionData;
|
||||
exports.memoize = memoize;
|
||||
exports.nodeDualstackConfigSelectors = nodeDualstackConfigSelectors;
|
||||
exports.nodeFipsConfigSelectors = nodeFipsConfigSelectors;
|
||||
exports.numberSelector = numberSelector;
|
||||
exports.parseKnownFiles = parseKnownFiles;
|
||||
exports.readFile = readFile;
|
||||
exports.resolveCustomEndpointsConfig = resolveCustomEndpointsConfig;
|
||||
exports.resolveDefaultsModeConfig = resolveDefaultsModeConfig;
|
||||
exports.resolveEndpointsConfig = resolveEndpointsConfig;
|
||||
exports.resolveRegionConfig = resolveRegionConfig;
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
'use strict';
|
||||
|
||||
var client = require('@smithy/core/client');
|
||||
var transport = require('@smithy/core/transport');
|
||||
|
||||
class ProviderError extends Error {
|
||||
name = "ProviderError";
|
||||
tryNextLink;
|
||||
constructor(message, options = true) {
|
||||
let logger;
|
||||
let tryNextLink = true;
|
||||
if (typeof options === "boolean") {
|
||||
logger = undefined;
|
||||
tryNextLink = options;
|
||||
}
|
||||
else if (options != null && typeof options === "object") {
|
||||
logger = options.logger;
|
||||
tryNextLink = options.tryNextLink ?? true;
|
||||
}
|
||||
super(message);
|
||||
this.tryNextLink = tryNextLink;
|
||||
Object.setPrototypeOf(this, ProviderError.prototype);
|
||||
logger?.debug?.(`@smithy/property-provider ${tryNextLink ? "->" : "(!)"} ${message}`);
|
||||
}
|
||||
static from(error, options = true) {
|
||||
return Object.assign(new this(error.message, options), error);
|
||||
}
|
||||
}
|
||||
|
||||
class CredentialsProviderError extends ProviderError {
|
||||
name = "CredentialsProviderError";
|
||||
constructor(message, options = true) {
|
||||
super(message, options);
|
||||
Object.setPrototypeOf(this, CredentialsProviderError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
class TokenProviderError extends ProviderError {
|
||||
name = "TokenProviderError";
|
||||
constructor(message, options = true) {
|
||||
super(message, options);
|
||||
Object.setPrototypeOf(this, TokenProviderError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
const chain = (...providers) => async () => {
|
||||
if (providers.length === 0) {
|
||||
throw new ProviderError("No providers in chain");
|
||||
}
|
||||
let lastProviderError;
|
||||
for (const provider of providers) {
|
||||
try {
|
||||
const credentials = await provider();
|
||||
return credentials;
|
||||
}
|
||||
catch (err) {
|
||||
lastProviderError = err;
|
||||
if (err?.tryNextLink) {
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
throw lastProviderError;
|
||||
};
|
||||
|
||||
const fromValue = (staticValue) => () => Promise.resolve(staticValue);
|
||||
|
||||
const memoize = (provider, isExpired, requiresRefresh) => {
|
||||
let resolved;
|
||||
let pending;
|
||||
let hasResult;
|
||||
let isConstant = false;
|
||||
const coalesceProvider = async () => {
|
||||
if (!pending) {
|
||||
pending = provider();
|
||||
}
|
||||
try {
|
||||
resolved = await pending;
|
||||
hasResult = true;
|
||||
isConstant = false;
|
||||
}
|
||||
finally {
|
||||
pending = undefined;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
if (isExpired === undefined) {
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider();
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
}
|
||||
return async (options) => {
|
||||
if (!hasResult || options?.forceRefresh) {
|
||||
resolved = await coalesceProvider();
|
||||
}
|
||||
if (isConstant) {
|
||||
return resolved;
|
||||
}
|
||||
if (requiresRefresh && !requiresRefresh(resolved)) {
|
||||
isConstant = true;
|
||||
return resolved;
|
||||
}
|
||||
if (isExpired(resolved)) {
|
||||
await coalesceProvider();
|
||||
return resolved;
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
};
|
||||
|
||||
const booleanSelector = (obj, key, type) => {
|
||||
if (!(key in obj))
|
||||
return undefined;
|
||||
if (obj[key] === "true")
|
||||
return true;
|
||||
if (obj[key] === "false")
|
||||
return false;
|
||||
throw new Error(`Cannot load ${type} "${key}". Expected "true" or "false", got ${obj[key]}.`);
|
||||
};
|
||||
|
||||
const numberSelector = (obj, key, type) => {
|
||||
if (!(key in obj))
|
||||
return undefined;
|
||||
const numberValue = parseInt(obj[key], 10);
|
||||
if (Number.isNaN(numberValue)) {
|
||||
throw new TypeError(`Cannot load ${type} '${key}'. Expected number, got '${obj[key]}'.`);
|
||||
}
|
||||
return numberValue;
|
||||
};
|
||||
|
||||
exports.SelectorType = void 0;
|
||||
(function (SelectorType) {
|
||||
SelectorType["ENV"] = "env";
|
||||
SelectorType["CONFIG"] = "shared config entry";
|
||||
})(exports.SelectorType || (exports.SelectorType = {}));
|
||||
|
||||
const resolveCustomEndpointsConfig = (input) => {
|
||||
const { tls, endpoint, urlParser, useDualstackEndpoint } = input;
|
||||
return Object.assign(input, {
|
||||
tls: tls ?? true,
|
||||
endpoint: client.normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint),
|
||||
isCustomEndpoint: true,
|
||||
useDualstackEndpoint: client.normalizeProvider(useDualstackEndpoint ?? false),
|
||||
});
|
||||
};
|
||||
|
||||
const getEndpointFromRegion = async (input) => {
|
||||
const { tls = true } = input;
|
||||
const region = await input.region();
|
||||
const dnsHostRegex = new RegExp(/^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/);
|
||||
if (!dnsHostRegex.test(region)) {
|
||||
throw new Error("Invalid region in client config");
|
||||
}
|
||||
const useDualstackEndpoint = await input.useDualstackEndpoint();
|
||||
const useFipsEndpoint = await input.useFipsEndpoint();
|
||||
const { hostname } = (await input.regionInfoProvider(region, { useDualstackEndpoint, useFipsEndpoint })) ?? {};
|
||||
if (!hostname) {
|
||||
throw new Error("Cannot resolve hostname from client config");
|
||||
}
|
||||
return input.urlParser(`${tls ? "https:" : "http:"}//${hostname}`);
|
||||
};
|
||||
|
||||
const resolveEndpointsConfig = (input) => {
|
||||
const useDualstackEndpoint = client.normalizeProvider(input.useDualstackEndpoint ?? false);
|
||||
const { endpoint, useFipsEndpoint, urlParser, tls } = input;
|
||||
return Object.assign(input, {
|
||||
tls: tls ?? true,
|
||||
endpoint: endpoint
|
||||
? client.normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint)
|
||||
: () => getEndpointFromRegion({ ...input, useDualstackEndpoint, useFipsEndpoint }),
|
||||
isCustomEndpoint: !!endpoint,
|
||||
useDualstackEndpoint,
|
||||
});
|
||||
};
|
||||
|
||||
const validRegions = new Set();
|
||||
const checkRegion = (region, check = transport.isValidHostLabel) => {
|
||||
if (!validRegions.has(region) && !check(region)) {
|
||||
if (region === "*") {
|
||||
console.warn(`@smithy/config-resolver WARN - Please use the caller region instead of "*". See "sigv4a" in https://github.com/aws/aws-sdk-js-v3/blob/main/supplemental-docs/CLIENTS.md.`);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Region not accepted: region="${region}" is not a valid hostname component.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
validRegions.add(region);
|
||||
}
|
||||
};
|
||||
|
||||
const isFipsRegion = (region) => typeof region === "string" && (region.startsWith("fips-") || region.endsWith("-fips"));
|
||||
|
||||
const getRealRegion = (region) => isFipsRegion(region)
|
||||
? ["fips-aws-global", "aws-fips"].includes(region)
|
||||
? "us-east-1"
|
||||
: region.replace(/fips-(dkr-|prod-)?|-fips/, "")
|
||||
: region;
|
||||
|
||||
const resolveRegionConfig = (input) => {
|
||||
const { region, useFipsEndpoint } = input;
|
||||
if (!region) {
|
||||
throw new Error("Region is missing");
|
||||
}
|
||||
return Object.assign(input, {
|
||||
region: async () => {
|
||||
const providedRegion = typeof region === "function" ? await region() : region;
|
||||
const realRegion = getRealRegion(providedRegion);
|
||||
checkRegion(realRegion);
|
||||
return realRegion;
|
||||
},
|
||||
useFipsEndpoint: async () => {
|
||||
const providedRegion = typeof region === "string" ? region : await region();
|
||||
if (isFipsRegion(providedRegion)) {
|
||||
return true;
|
||||
}
|
||||
return typeof useFipsEndpoint !== "function" ? Promise.resolve(!!useFipsEndpoint) : useFipsEndpoint();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getHostnameFromVariants = (variants = [], { useFipsEndpoint, useDualstackEndpoint }) => variants.find(({ tags }) => useFipsEndpoint === tags.includes("fips") && useDualstackEndpoint === tags.includes("dualstack"))?.hostname;
|
||||
|
||||
const getResolvedHostname = (resolvedRegion, { regionHostname, partitionHostname }) => regionHostname
|
||||
? regionHostname
|
||||
: partitionHostname
|
||||
? partitionHostname.replace("{region}", resolvedRegion)
|
||||
: undefined;
|
||||
|
||||
const getResolvedPartition = (region, { partitionHash }) => Object.keys(partitionHash || {}).find((key) => partitionHash[key].regions.includes(region)) ?? "aws";
|
||||
|
||||
const getResolvedSigningRegion = (hostname, { signingRegion, regionRegex, useFipsEndpoint }) => {
|
||||
if (signingRegion) {
|
||||
return signingRegion;
|
||||
}
|
||||
else if (useFipsEndpoint) {
|
||||
const regionRegexJs = regionRegex.replace("\\\\", "\\").replace(/^\^/g, "\\.").replace(/\$$/g, "\\.");
|
||||
const regionRegexmatchArray = hostname.match(regionRegexJs);
|
||||
if (regionRegexmatchArray) {
|
||||
return regionRegexmatchArray[0].slice(1, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getRegionInfo = (region, { useFipsEndpoint = false, useDualstackEndpoint = false, signingService, regionHash, partitionHash, }) => {
|
||||
const partition = getResolvedPartition(region, { partitionHash });
|
||||
const resolvedRegion = region in regionHash ? region : partitionHash[partition]?.endpoint ?? region;
|
||||
const hostnameOptions = { useFipsEndpoint, useDualstackEndpoint };
|
||||
const regionHostname = getHostnameFromVariants(regionHash[resolvedRegion]?.variants, hostnameOptions);
|
||||
const partitionHostname = getHostnameFromVariants(partitionHash[partition]?.variants, hostnameOptions);
|
||||
const hostname = getResolvedHostname(resolvedRegion, { regionHostname, partitionHostname });
|
||||
if (hostname === undefined) {
|
||||
throw new Error(`Endpoint resolution failed for: ${{ resolvedRegion, useFipsEndpoint, useDualstackEndpoint }}`);
|
||||
}
|
||||
const signingRegion = getResolvedSigningRegion(hostname, {
|
||||
signingRegion: regionHash[resolvedRegion]?.signingRegion,
|
||||
regionRegex: partitionHash[partition].regionRegex,
|
||||
useFipsEndpoint,
|
||||
});
|
||||
return {
|
||||
partition,
|
||||
signingService,
|
||||
hostname,
|
||||
...(signingRegion && { signingRegion }),
|
||||
...(regionHash[resolvedRegion]?.signingService && {
|
||||
signingService: regionHash[resolvedRegion].signingService,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULTS_MODE_OPTIONS = ["in-region", "cross-region", "mobile", "standard", "legacy"];
|
||||
|
||||
const resolveDefaultsModeConfig = ({ defaultsMode, } = {}) => memoize(async () => {
|
||||
const mode = typeof defaultsMode === "function" ? await defaultsMode() : defaultsMode;
|
||||
switch (mode?.toLowerCase()) {
|
||||
case "auto":
|
||||
return Promise.resolve("mobile");
|
||||
case "mobile":
|
||||
case "in-region":
|
||||
case "cross-region":
|
||||
case "standard":
|
||||
case "legacy":
|
||||
return Promise.resolve(mode?.toLocaleLowerCase());
|
||||
case undefined:
|
||||
return Promise.resolve("legacy");
|
||||
default:
|
||||
throw new Error(`Invalid parameter for "defaultsMode", expect ${DEFAULTS_MODE_OPTIONS.join(", ")}, got ${mode}`);
|
||||
}
|
||||
});
|
||||
|
||||
const no = Symbol.for("node-only");
|
||||
const getHomeDir = no;
|
||||
const ENV_PROFILE = no;
|
||||
const DEFAULT_PROFILE = "default";
|
||||
const getProfileName = no;
|
||||
const getSSOTokenFilepath = no;
|
||||
const getSSOTokenFromFile = no;
|
||||
const CONFIG_PREFIX_SEPARATOR = no;
|
||||
const loadSharedConfigFiles = no;
|
||||
const loadSsoSessionData = no;
|
||||
const parseKnownFiles = no;
|
||||
const externalDataInterceptor = no;
|
||||
const readFile = no;
|
||||
const loadConfig = no;
|
||||
const fromStatic = no;
|
||||
const ENV_USE_DUALSTACK_ENDPOINT = no;
|
||||
const CONFIG_USE_DUALSTACK_ENDPOINT = no;
|
||||
const DEFAULT_USE_DUALSTACK_ENDPOINT = false;
|
||||
const NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS = no;
|
||||
const nodeDualstackConfigSelectors = no;
|
||||
const ENV_USE_FIPS_ENDPOINT = no;
|
||||
const CONFIG_USE_FIPS_ENDPOINT = no;
|
||||
const DEFAULT_USE_FIPS_ENDPOINT = false;
|
||||
const NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS = no;
|
||||
const nodeFipsConfigSelectors = no;
|
||||
const REGION_ENV_NAME = no;
|
||||
const REGION_INI_NAME = no;
|
||||
const NODE_REGION_CONFIG_OPTIONS = no;
|
||||
const NODE_REGION_CONFIG_FILE_OPTIONS = no;
|
||||
|
||||
exports.CONFIG_PREFIX_SEPARATOR = CONFIG_PREFIX_SEPARATOR;
|
||||
exports.CONFIG_USE_DUALSTACK_ENDPOINT = CONFIG_USE_DUALSTACK_ENDPOINT;
|
||||
exports.CONFIG_USE_FIPS_ENDPOINT = CONFIG_USE_FIPS_ENDPOINT;
|
||||
exports.CredentialsProviderError = CredentialsProviderError;
|
||||
exports.DEFAULT_PROFILE = DEFAULT_PROFILE;
|
||||
exports.DEFAULT_USE_DUALSTACK_ENDPOINT = DEFAULT_USE_DUALSTACK_ENDPOINT;
|
||||
exports.DEFAULT_USE_FIPS_ENDPOINT = DEFAULT_USE_FIPS_ENDPOINT;
|
||||
exports.ENV_PROFILE = ENV_PROFILE;
|
||||
exports.ENV_USE_DUALSTACK_ENDPOINT = ENV_USE_DUALSTACK_ENDPOINT;
|
||||
exports.ENV_USE_FIPS_ENDPOINT = ENV_USE_FIPS_ENDPOINT;
|
||||
exports.NODE_REGION_CONFIG_FILE_OPTIONS = NODE_REGION_CONFIG_FILE_OPTIONS;
|
||||
exports.NODE_REGION_CONFIG_OPTIONS = NODE_REGION_CONFIG_OPTIONS;
|
||||
exports.NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS = NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS;
|
||||
exports.NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS = NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS;
|
||||
exports.ProviderError = ProviderError;
|
||||
exports.REGION_ENV_NAME = REGION_ENV_NAME;
|
||||
exports.REGION_INI_NAME = REGION_INI_NAME;
|
||||
exports.TokenProviderError = TokenProviderError;
|
||||
exports.booleanSelector = booleanSelector;
|
||||
exports.chain = chain;
|
||||
exports.externalDataInterceptor = externalDataInterceptor;
|
||||
exports.fromStatic = fromStatic;
|
||||
exports.fromValue = fromValue;
|
||||
exports.getHomeDir = getHomeDir;
|
||||
exports.getProfileName = getProfileName;
|
||||
exports.getRegionInfo = getRegionInfo;
|
||||
exports.getSSOTokenFilepath = getSSOTokenFilepath;
|
||||
exports.getSSOTokenFromFile = getSSOTokenFromFile;
|
||||
exports.loadConfig = loadConfig;
|
||||
exports.loadSharedConfigFiles = loadSharedConfigFiles;
|
||||
exports.loadSsoSessionData = loadSsoSessionData;
|
||||
exports.memoize = memoize;
|
||||
exports.nodeDualstackConfigSelectors = nodeDualstackConfigSelectors;
|
||||
exports.nodeFipsConfigSelectors = nodeFipsConfigSelectors;
|
||||
exports.numberSelector = numberSelector;
|
||||
exports.parseKnownFiles = parseKnownFiles;
|
||||
exports.readFile = readFile;
|
||||
exports.resolveCustomEndpointsConfig = resolveCustomEndpointsConfig;
|
||||
exports.resolveDefaultsModeConfig = resolveDefaultsModeConfig;
|
||||
exports.resolveEndpointsConfig = resolveEndpointsConfig;
|
||||
exports.resolveRegionConfig = resolveRegionConfig;
|
||||
+829
@@ -0,0 +1,829 @@
|
||||
'use strict';
|
||||
|
||||
var transport = require('@smithy/core/transport');
|
||||
var client = require('@smithy/core/client');
|
||||
var types = require('@smithy/types');
|
||||
|
||||
const getEndpointFromConfig = async (serviceId) => undefined;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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(transport.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 () => transport.toEndpointV1(await transport.normalizeProvider(endpoint)()) : undefined;
|
||||
const isCustomEndpoint = !!endpoint;
|
||||
const resolvedConfig = Object.assign(input, {
|
||||
endpoint: customEndpointProvider,
|
||||
tls,
|
||||
isCustomEndpoint,
|
||||
useDualstackEndpoint: transport.normalizeProvider(useDualstackEndpoint ?? false),
|
||||
useFipsEndpoint: transport.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;
|
||||
|
||||
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: transport.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.isValidHostLabel = transport.isValidHostLabel;
|
||||
exports.middlewareEndpointToEndpointV1 = transport.toEndpointV1;
|
||||
exports.toEndpointV1 = transport.toEndpointV1;
|
||||
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.resolveEndpoint = resolveEndpoint;
|
||||
exports.resolveEndpointConfig = resolveEndpointConfig;
|
||||
exports.resolveEndpointRequiredConfig = resolveEndpointRequiredConfig;
|
||||
exports.resolveParams = resolveParams;
|
||||
+861
@@ -0,0 +1,861 @@
|
||||
'use strict';
|
||||
|
||||
var config = require('@smithy/core/config');
|
||||
var transport = require('@smithy/core/transport');
|
||||
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;
|
||||
};
|
||||
|
||||
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(transport.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 () => transport.toEndpointV1(await transport.normalizeProvider(endpoint)()) : undefined;
|
||||
const isCustomEndpoint = !!endpoint;
|
||||
const resolvedConfig = Object.assign(input, {
|
||||
endpoint: customEndpointProvider,
|
||||
tls,
|
||||
isCustomEndpoint,
|
||||
useDualstackEndpoint: transport.normalizeProvider(useDualstackEndpoint ?? false),
|
||||
useFipsEndpoint: transport.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;
|
||||
|
||||
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: transport.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.isValidHostLabel = transport.isValidHostLabel;
|
||||
exports.middlewareEndpointToEndpointV1 = transport.toEndpointV1;
|
||||
exports.toEndpointV1 = transport.toEndpointV1;
|
||||
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.resolveEndpoint = resolveEndpoint;
|
||||
exports.resolveEndpointConfig = resolveEndpointConfig;
|
||||
exports.resolveEndpointRequiredConfig = resolveEndpointRequiredConfig;
|
||||
exports.resolveParams = resolveParams;
|
||||
+866
@@ -0,0 +1,866 @@
|
||||
'use strict';
|
||||
|
||||
var crc32 = require('@aws-crypto/crc32');
|
||||
var serde = require('@smithy/core/serde');
|
||||
|
||||
class Int64 {
|
||||
bytes;
|
||||
constructor(bytes) {
|
||||
this.bytes = bytes;
|
||||
if (bytes.byteLength !== 8) {
|
||||
throw new Error("Int64 buffers must be exactly 8 bytes");
|
||||
}
|
||||
}
|
||||
static fromNumber(number) {
|
||||
if (number > 9_223_372_036_854_775_807 || number < -9223372036854776e3) {
|
||||
throw new Error(`${number} is too large (or, if negative, too small) to represent as an Int64`);
|
||||
}
|
||||
const bytes = new Uint8Array(8);
|
||||
for (let i = 7, remaining = Math.abs(Math.round(number)); i > -1 && remaining > 0; i--, remaining /= 256) {
|
||||
bytes[i] = remaining;
|
||||
}
|
||||
if (number < 0) {
|
||||
negate(bytes);
|
||||
}
|
||||
return new Int64(bytes);
|
||||
}
|
||||
valueOf() {
|
||||
const bytes = this.bytes.slice(0);
|
||||
const negative = bytes[0] & 0b10000000;
|
||||
if (negative) {
|
||||
negate(bytes);
|
||||
}
|
||||
return parseInt(serde.toHex(bytes), 16) * (negative ? -1 : 1);
|
||||
}
|
||||
toString() {
|
||||
return String(this.valueOf());
|
||||
}
|
||||
}
|
||||
function negate(bytes) {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
bytes[i] ^= 0xff;
|
||||
}
|
||||
for (let i = 7; i > -1; i--) {
|
||||
bytes[i]++;
|
||||
if (bytes[i] !== 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderMarshaller {
|
||||
toUtf8;
|
||||
fromUtf8;
|
||||
constructor(toUtf8, fromUtf8) {
|
||||
this.toUtf8 = toUtf8;
|
||||
this.fromUtf8 = fromUtf8;
|
||||
}
|
||||
format(headers) {
|
||||
const chunks = [];
|
||||
for (const headerName of Object.keys(headers)) {
|
||||
const bytes = this.fromUtf8(headerName);
|
||||
chunks.push(Uint8Array.from([bytes.byteLength]), bytes, this.formatHeaderValue(headers[headerName]));
|
||||
}
|
||||
const out = new Uint8Array(chunks.reduce((carry, bytes) => carry + bytes.byteLength, 0));
|
||||
let position = 0;
|
||||
for (const chunk of chunks) {
|
||||
out.set(chunk, position);
|
||||
position += chunk.byteLength;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
formatHeaderValue(header) {
|
||||
switch (header.type) {
|
||||
case "boolean":
|
||||
return Uint8Array.from([header.value ? 0 : 1]);
|
||||
case "byte":
|
||||
return Uint8Array.from([2, header.value]);
|
||||
case "short":
|
||||
const shortView = new DataView(new ArrayBuffer(3));
|
||||
shortView.setUint8(0, 3);
|
||||
shortView.setInt16(1, header.value, false);
|
||||
return new Uint8Array(shortView.buffer);
|
||||
case "integer":
|
||||
const intView = new DataView(new ArrayBuffer(5));
|
||||
intView.setUint8(0, 4);
|
||||
intView.setInt32(1, header.value, false);
|
||||
return new Uint8Array(intView.buffer);
|
||||
case "long":
|
||||
const longBytes = new Uint8Array(9);
|
||||
longBytes[0] = 5;
|
||||
longBytes.set(header.value.bytes, 1);
|
||||
return longBytes;
|
||||
case "binary":
|
||||
const binView = new DataView(new ArrayBuffer(3 + header.value.byteLength));
|
||||
binView.setUint8(0, 6);
|
||||
binView.setUint16(1, header.value.byteLength, false);
|
||||
const binBytes = new Uint8Array(binView.buffer);
|
||||
binBytes.set(header.value, 3);
|
||||
return binBytes;
|
||||
case "string":
|
||||
const utf8Bytes = this.fromUtf8(header.value);
|
||||
const strView = new DataView(new ArrayBuffer(3 + utf8Bytes.byteLength));
|
||||
strView.setUint8(0, 7);
|
||||
strView.setUint16(1, utf8Bytes.byteLength, false);
|
||||
const strBytes = new Uint8Array(strView.buffer);
|
||||
strBytes.set(utf8Bytes, 3);
|
||||
return strBytes;
|
||||
case "timestamp":
|
||||
const tsBytes = new Uint8Array(9);
|
||||
tsBytes[0] = 8;
|
||||
tsBytes.set(Int64.fromNumber(header.value.valueOf()).bytes, 1);
|
||||
return tsBytes;
|
||||
case "uuid":
|
||||
if (!UUID_PATTERN.test(header.value)) {
|
||||
throw new Error(`Invalid UUID received: ${header.value}`);
|
||||
}
|
||||
const uuidBytes = new Uint8Array(17);
|
||||
uuidBytes[0] = 9;
|
||||
uuidBytes.set(serde.fromHex(header.value.replace(/\-/g, "")), 1);
|
||||
return uuidBytes;
|
||||
}
|
||||
}
|
||||
parse(headers) {
|
||||
const out = {};
|
||||
let position = 0;
|
||||
while (position < headers.byteLength) {
|
||||
const nameLength = headers.getUint8(position++);
|
||||
const name = this.toUtf8(new Uint8Array(headers.buffer, headers.byteOffset + position, nameLength));
|
||||
position += nameLength;
|
||||
switch (headers.getUint8(position++)) {
|
||||
case 0:
|
||||
out[name] = {
|
||||
type: BOOLEAN_TAG,
|
||||
value: true,
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
out[name] = {
|
||||
type: BOOLEAN_TAG,
|
||||
value: false,
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
out[name] = {
|
||||
type: BYTE_TAG,
|
||||
value: headers.getInt8(position++),
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
out[name] = {
|
||||
type: SHORT_TAG,
|
||||
value: headers.getInt16(position, false),
|
||||
};
|
||||
position += 2;
|
||||
break;
|
||||
case 4:
|
||||
out[name] = {
|
||||
type: INT_TAG,
|
||||
value: headers.getInt32(position, false),
|
||||
};
|
||||
position += 4;
|
||||
break;
|
||||
case 5:
|
||||
out[name] = {
|
||||
type: LONG_TAG,
|
||||
value: new Int64(new Uint8Array(headers.buffer, headers.byteOffset + position, 8)),
|
||||
};
|
||||
position += 8;
|
||||
break;
|
||||
case 6:
|
||||
const binaryLength = headers.getUint16(position, false);
|
||||
position += 2;
|
||||
out[name] = {
|
||||
type: BINARY_TAG,
|
||||
value: new Uint8Array(headers.buffer, headers.byteOffset + position, binaryLength),
|
||||
};
|
||||
position += binaryLength;
|
||||
break;
|
||||
case 7:
|
||||
const stringLength = headers.getUint16(position, false);
|
||||
position += 2;
|
||||
out[name] = {
|
||||
type: STRING_TAG,
|
||||
value: this.toUtf8(new Uint8Array(headers.buffer, headers.byteOffset + position, stringLength)),
|
||||
};
|
||||
position += stringLength;
|
||||
break;
|
||||
case 8:
|
||||
out[name] = {
|
||||
type: TIMESTAMP_TAG,
|
||||
value: new Date(new Int64(new Uint8Array(headers.buffer, headers.byteOffset + position, 8)).valueOf()),
|
||||
};
|
||||
position += 8;
|
||||
break;
|
||||
case 9:
|
||||
const uuidBytes = new Uint8Array(headers.buffer, headers.byteOffset + position, 16);
|
||||
position += 16;
|
||||
out[name] = {
|
||||
type: UUID_TAG,
|
||||
value: `${serde.toHex(uuidBytes.subarray(0, 4))}-${serde.toHex(uuidBytes.subarray(4, 6))}-${serde.toHex(uuidBytes.subarray(6, 8))}-${serde.toHex(uuidBytes.subarray(8, 10))}-${serde.toHex(uuidBytes.subarray(10))}`,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unrecognized header type tag`);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
var HEADER_VALUE_TYPE;
|
||||
(function (HEADER_VALUE_TYPE) {
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["boolTrue"] = 0] = "boolTrue";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["boolFalse"] = 1] = "boolFalse";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["byte"] = 2] = "byte";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["short"] = 3] = "short";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["integer"] = 4] = "integer";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["long"] = 5] = "long";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["byteArray"] = 6] = "byteArray";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["string"] = 7] = "string";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["timestamp"] = 8] = "timestamp";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["uuid"] = 9] = "uuid";
|
||||
})(HEADER_VALUE_TYPE || (HEADER_VALUE_TYPE = {}));
|
||||
const BOOLEAN_TAG = "boolean";
|
||||
const BYTE_TAG = "byte";
|
||||
const SHORT_TAG = "short";
|
||||
const INT_TAG = "integer";
|
||||
const LONG_TAG = "long";
|
||||
const BINARY_TAG = "binary";
|
||||
const STRING_TAG = "string";
|
||||
const TIMESTAMP_TAG = "timestamp";
|
||||
const UUID_TAG = "uuid";
|
||||
const UUID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
||||
|
||||
const PRELUDE_MEMBER_LENGTH = 4;
|
||||
const PRELUDE_LENGTH = PRELUDE_MEMBER_LENGTH * 2;
|
||||
const CHECKSUM_LENGTH = 4;
|
||||
const MINIMUM_MESSAGE_LENGTH = PRELUDE_LENGTH + CHECKSUM_LENGTH * 2;
|
||||
function splitMessage({ byteLength, byteOffset, buffer }) {
|
||||
if (byteLength < MINIMUM_MESSAGE_LENGTH) {
|
||||
throw new Error("Provided message too short to accommodate event stream message overhead");
|
||||
}
|
||||
const view = new DataView(buffer, byteOffset, byteLength);
|
||||
const messageLength = view.getUint32(0, false);
|
||||
if (byteLength !== messageLength) {
|
||||
throw new Error("Reported message length does not match received message length");
|
||||
}
|
||||
const headerLength = view.getUint32(PRELUDE_MEMBER_LENGTH, false);
|
||||
const expectedPreludeChecksum = view.getUint32(PRELUDE_LENGTH, false);
|
||||
const expectedMessageChecksum = view.getUint32(byteLength - CHECKSUM_LENGTH, false);
|
||||
const checksummer = new crc32.Crc32().update(new Uint8Array(buffer, byteOffset, PRELUDE_LENGTH));
|
||||
if (expectedPreludeChecksum !== checksummer.digest()) {
|
||||
throw new Error(`The prelude checksum specified in the message (${expectedPreludeChecksum}) does not match the calculated CRC32 checksum (${checksummer.digest()})`);
|
||||
}
|
||||
checksummer.update(new Uint8Array(buffer, byteOffset + PRELUDE_LENGTH, byteLength - (PRELUDE_LENGTH + CHECKSUM_LENGTH)));
|
||||
if (expectedMessageChecksum !== checksummer.digest()) {
|
||||
throw new Error(`The message checksum (${checksummer.digest()}) did not match the expected value of ${expectedMessageChecksum}`);
|
||||
}
|
||||
return {
|
||||
headers: new DataView(buffer, byteOffset + PRELUDE_LENGTH + CHECKSUM_LENGTH, headerLength),
|
||||
body: new Uint8Array(buffer, byteOffset + PRELUDE_LENGTH + CHECKSUM_LENGTH + headerLength, messageLength - headerLength - (PRELUDE_LENGTH + CHECKSUM_LENGTH + CHECKSUM_LENGTH)),
|
||||
};
|
||||
}
|
||||
|
||||
class EventStreamCodec {
|
||||
headerMarshaller;
|
||||
messageBuffer;
|
||||
isEndOfStream;
|
||||
constructor(toUtf8, fromUtf8) {
|
||||
this.headerMarshaller = new HeaderMarshaller(toUtf8, fromUtf8);
|
||||
this.messageBuffer = [];
|
||||
this.isEndOfStream = false;
|
||||
}
|
||||
feed(message) {
|
||||
this.messageBuffer.push(this.decode(message));
|
||||
}
|
||||
endOfStream() {
|
||||
this.isEndOfStream = true;
|
||||
}
|
||||
getMessage() {
|
||||
const message = this.messageBuffer.pop();
|
||||
const isEndOfStream = this.isEndOfStream;
|
||||
return {
|
||||
getMessage() {
|
||||
return message;
|
||||
},
|
||||
isEndOfStream() {
|
||||
return isEndOfStream;
|
||||
},
|
||||
};
|
||||
}
|
||||
getAvailableMessages() {
|
||||
const messages = this.messageBuffer;
|
||||
this.messageBuffer = [];
|
||||
const isEndOfStream = this.isEndOfStream;
|
||||
return {
|
||||
getMessages() {
|
||||
return messages;
|
||||
},
|
||||
isEndOfStream() {
|
||||
return isEndOfStream;
|
||||
},
|
||||
};
|
||||
}
|
||||
encode({ headers: rawHeaders, body }) {
|
||||
const headers = this.headerMarshaller.format(rawHeaders);
|
||||
const length = headers.byteLength + body.byteLength + 16;
|
||||
const out = new Uint8Array(length);
|
||||
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
||||
const checksum = new crc32.Crc32();
|
||||
view.setUint32(0, length, false);
|
||||
view.setUint32(4, headers.byteLength, false);
|
||||
view.setUint32(8, checksum.update(out.subarray(0, 8)).digest(), false);
|
||||
out.set(headers, 12);
|
||||
out.set(body, headers.byteLength + 12);
|
||||
view.setUint32(length - 4, checksum.update(out.subarray(8, length - 4)).digest(), false);
|
||||
return out;
|
||||
}
|
||||
decode(message) {
|
||||
const { headers, body } = splitMessage(message);
|
||||
return { headers: this.headerMarshaller.parse(headers), body };
|
||||
}
|
||||
formatHeaders(rawHeaders) {
|
||||
return this.headerMarshaller.format(rawHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
class MessageDecoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const bytes of this.options.inputStream) {
|
||||
const decoded = this.options.decoder.decode(bytes);
|
||||
yield decoded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MessageEncoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const msg of this.options.messageStream) {
|
||||
const encoded = this.options.encoder.encode(msg);
|
||||
yield encoded;
|
||||
}
|
||||
if (this.options.includeEndFrame) {
|
||||
yield new Uint8Array(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SmithyMessageDecoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const message of this.options.messageStream) {
|
||||
const deserialized = await this.options.deserializer(message);
|
||||
if (deserialized === undefined)
|
||||
continue;
|
||||
yield deserialized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SmithyMessageEncoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const chunk of this.options.inputStream) {
|
||||
const payloadBuf = this.options.serializer(chunk);
|
||||
yield payloadBuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getChunkedStream(source) {
|
||||
let currentMessageTotalLength = 0;
|
||||
let currentMessagePendingLength = 0;
|
||||
let currentMessage = null;
|
||||
let messageLengthBuffer = null;
|
||||
const allocateMessage = (size) => {
|
||||
if (typeof size !== "number") {
|
||||
throw new Error("Attempted to allocate an event message where size was not a number: " + size);
|
||||
}
|
||||
currentMessageTotalLength = size;
|
||||
currentMessagePendingLength = 4;
|
||||
currentMessage = new Uint8Array(size);
|
||||
const currentMessageView = new DataView(currentMessage.buffer);
|
||||
currentMessageView.setUint32(0, size, false);
|
||||
};
|
||||
const iterator = async function* () {
|
||||
const sourceIterator = source[Symbol.asyncIterator]();
|
||||
while (true) {
|
||||
const { value, done } = await sourceIterator.next();
|
||||
if (done) {
|
||||
if (!currentMessageTotalLength) {
|
||||
return;
|
||||
}
|
||||
else if (currentMessageTotalLength === currentMessagePendingLength) {
|
||||
yield currentMessage;
|
||||
}
|
||||
else {
|
||||
throw new Error("Truncated event message received.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const chunkLength = value.length;
|
||||
let currentOffset = 0;
|
||||
while (currentOffset < chunkLength) {
|
||||
if (!currentMessage) {
|
||||
const bytesRemaining = chunkLength - currentOffset;
|
||||
if (!messageLengthBuffer) {
|
||||
messageLengthBuffer = new Uint8Array(4);
|
||||
}
|
||||
const numBytesForTotal = Math.min(4 - currentMessagePendingLength, bytesRemaining);
|
||||
messageLengthBuffer.set(value.slice(currentOffset, currentOffset + numBytesForTotal), currentMessagePendingLength);
|
||||
currentMessagePendingLength += numBytesForTotal;
|
||||
currentOffset += numBytesForTotal;
|
||||
if (currentMessagePendingLength < 4) {
|
||||
break;
|
||||
}
|
||||
allocateMessage(new DataView(messageLengthBuffer.buffer).getUint32(0, false));
|
||||
messageLengthBuffer = null;
|
||||
}
|
||||
const numBytesToWrite = Math.min(currentMessageTotalLength - currentMessagePendingLength, chunkLength - currentOffset);
|
||||
currentMessage.set(value.slice(currentOffset, currentOffset + numBytesToWrite), currentMessagePendingLength);
|
||||
currentMessagePendingLength += numBytesToWrite;
|
||||
currentOffset += numBytesToWrite;
|
||||
if (currentMessageTotalLength && currentMessageTotalLength === currentMessagePendingLength) {
|
||||
yield currentMessage;
|
||||
currentMessage = null;
|
||||
currentMessageTotalLength = 0;
|
||||
currentMessagePendingLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
[Symbol.asyncIterator]: iterator,
|
||||
};
|
||||
}
|
||||
|
||||
function getUnmarshalledStream(source, options) {
|
||||
const messageUnmarshaller = getMessageUnmarshaller(options.deserializer, options.toUtf8);
|
||||
return {
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
for await (const chunk of source) {
|
||||
const message = options.eventStreamCodec.decode(chunk);
|
||||
const type = await messageUnmarshaller(message);
|
||||
if (type === undefined)
|
||||
continue;
|
||||
yield type;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
function getMessageUnmarshaller(deserializer, toUtf8) {
|
||||
return async function (message) {
|
||||
const { value: messageType } = message.headers[":message-type"];
|
||||
if (messageType === "error") {
|
||||
const unmodeledError = new Error(message.headers[":error-message"].value || "UnknownError");
|
||||
unmodeledError.name = message.headers[":error-code"].value;
|
||||
throw unmodeledError;
|
||||
}
|
||||
else if (messageType === "exception") {
|
||||
const code = message.headers[":exception-type"].value;
|
||||
const exception = { [code]: message };
|
||||
const deserializedException = await deserializer(exception);
|
||||
if (deserializedException.$unknown) {
|
||||
const error = new Error(toUtf8(message.body));
|
||||
error.name = code;
|
||||
throw error;
|
||||
}
|
||||
throw deserializedException[code];
|
||||
}
|
||||
else if (messageType === "event") {
|
||||
const event = {
|
||||
[message.headers[":event-type"].value]: message,
|
||||
};
|
||||
const deserialized = await deserializer(event);
|
||||
if (deserialized.$unknown)
|
||||
return;
|
||||
return deserialized;
|
||||
}
|
||||
else {
|
||||
throw Error(`Unrecognizable event type: ${message.headers[":event-type"].value}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let EventStreamMarshaller$1 = class EventStreamMarshaller {
|
||||
eventStreamCodec;
|
||||
utfEncoder;
|
||||
constructor({ utf8Encoder, utf8Decoder }) {
|
||||
this.eventStreamCodec = new EventStreamCodec(utf8Encoder, utf8Decoder);
|
||||
this.utfEncoder = utf8Encoder;
|
||||
}
|
||||
deserialize(body, deserializer) {
|
||||
const inputStream = getChunkedStream(body);
|
||||
return new SmithyMessageDecoderStream({
|
||||
messageStream: new MessageDecoderStream({ inputStream, decoder: this.eventStreamCodec }),
|
||||
deserializer: getMessageUnmarshaller(deserializer, this.utfEncoder),
|
||||
});
|
||||
}
|
||||
serialize(inputStream, serializer) {
|
||||
return new MessageEncoderStream({
|
||||
messageStream: new SmithyMessageEncoderStream({ inputStream, serializer }),
|
||||
encoder: this.eventStreamCodec,
|
||||
includeEndFrame: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
const eventStreamSerdeProvider$1 = (options) => new EventStreamMarshaller$1(options);
|
||||
|
||||
const readableStreamToIterable = (readableStream) => ({
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
const reader = readableStream.getReader();
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done)
|
||||
return;
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
},
|
||||
});
|
||||
const iterableToReadableStream = (asyncIterable) => {
|
||||
const iterator = asyncIterable[Symbol.asyncIterator]();
|
||||
return new ReadableStream({
|
||||
async pull(controller) {
|
||||
const { done, value } = await iterator.next();
|
||||
if (done) {
|
||||
return controller.close();
|
||||
}
|
||||
controller.enqueue(value);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
class EventStreamMarshaller {
|
||||
universalMarshaller;
|
||||
constructor({ utf8Encoder, utf8Decoder }) {
|
||||
this.universalMarshaller = new EventStreamMarshaller$1({
|
||||
utf8Decoder,
|
||||
utf8Encoder,
|
||||
});
|
||||
}
|
||||
deserialize(body, deserializer) {
|
||||
const bodyIterable = isReadableStream(body) ? readableStreamToIterable(body) : body;
|
||||
return this.universalMarshaller.deserialize(bodyIterable, deserializer);
|
||||
}
|
||||
serialize(input, serializer) {
|
||||
const serializedIterable = this.universalMarshaller.serialize(input, serializer);
|
||||
return typeof ReadableStream === "function" ? iterableToReadableStream(serializedIterable) : serializedIterable;
|
||||
}
|
||||
}
|
||||
const isReadableStream = (body) => typeof ReadableStream === "function" && body instanceof ReadableStream;
|
||||
const eventStreamSerdeProvider = (options) => new EventStreamMarshaller(options);
|
||||
|
||||
const resolveEventStreamSerdeConfig = (input) => Object.assign(input, {
|
||||
eventStreamMarshaller: input.eventStreamSerdeProvider(input),
|
||||
});
|
||||
|
||||
class EventStreamSerde {
|
||||
marshaller;
|
||||
serializer;
|
||||
deserializer;
|
||||
serdeContext;
|
||||
defaultContentType;
|
||||
constructor({ marshaller, serializer, deserializer, serdeContext, defaultContentType, }) {
|
||||
this.marshaller = marshaller;
|
||||
this.serializer = serializer;
|
||||
this.deserializer = deserializer;
|
||||
this.serdeContext = serdeContext;
|
||||
this.defaultContentType = defaultContentType;
|
||||
}
|
||||
async serializeEventStream({ eventStream, requestSchema, initialRequest, }) {
|
||||
const marshaller = this.marshaller;
|
||||
const eventStreamMember = requestSchema.getEventStreamMember();
|
||||
const unionSchema = requestSchema.getMemberSchema(eventStreamMember);
|
||||
const serializer = this.serializer;
|
||||
const defaultContentType = this.defaultContentType;
|
||||
const initialRequestMarker = Symbol("initialRequestMarker");
|
||||
const eventStreamIterable = {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
if (initialRequest) {
|
||||
const headers = {
|
||||
":event-type": { type: "string", value: "initial-request" },
|
||||
":message-type": { type: "string", value: "event" },
|
||||
":content-type": { type: "string", value: defaultContentType },
|
||||
};
|
||||
serializer.write(requestSchema, initialRequest);
|
||||
const body = serializer.flush();
|
||||
yield {
|
||||
[initialRequestMarker]: true,
|
||||
headers,
|
||||
body,
|
||||
};
|
||||
}
|
||||
for await (const page of eventStream) {
|
||||
yield page;
|
||||
}
|
||||
},
|
||||
};
|
||||
return marshaller.serialize(eventStreamIterable, (event) => {
|
||||
if (event[initialRequestMarker]) {
|
||||
return {
|
||||
headers: event.headers,
|
||||
body: event.body,
|
||||
};
|
||||
}
|
||||
let unionMember = "";
|
||||
for (const key in event) {
|
||||
if (key !== "__type") {
|
||||
unionMember = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const { additionalHeaders, body, eventType, explicitPayloadContentType } = this.writeEventBody(unionMember, unionSchema, event);
|
||||
const headers = {
|
||||
":event-type": { type: "string", value: eventType },
|
||||
":message-type": { type: "string", value: "event" },
|
||||
":content-type": { type: "string", value: explicitPayloadContentType ?? defaultContentType },
|
||||
...additionalHeaders,
|
||||
};
|
||||
return {
|
||||
headers,
|
||||
body,
|
||||
};
|
||||
});
|
||||
}
|
||||
async deserializeEventStream({ response, responseSchema, initialResponseContainer, }) {
|
||||
const marshaller = this.marshaller;
|
||||
const eventStreamMember = responseSchema.getEventStreamMember();
|
||||
const unionSchema = responseSchema.getMemberSchema(eventStreamMember);
|
||||
const memberSchemas = unionSchema.getMemberSchemas();
|
||||
const initialResponseMarker = Symbol("initialResponseMarker");
|
||||
const asyncIterable = marshaller.deserialize(response.body, async (event) => {
|
||||
let unionMember = "";
|
||||
for (const key in event) {
|
||||
if (key !== "__type") {
|
||||
unionMember = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const body = event[unionMember].body;
|
||||
if (unionMember === "initial-response") {
|
||||
const dataObject = await this.deserializer.read(responseSchema, body);
|
||||
delete dataObject[eventStreamMember];
|
||||
return {
|
||||
[initialResponseMarker]: true,
|
||||
...dataObject,
|
||||
};
|
||||
}
|
||||
else if (unionMember in memberSchemas) {
|
||||
const eventStreamSchema = memberSchemas[unionMember];
|
||||
if (eventStreamSchema.isStructSchema()) {
|
||||
const out = {};
|
||||
let hasBindings = false;
|
||||
for (const [name, member] of eventStreamSchema.structIterator()) {
|
||||
const { eventHeader, eventPayload } = member.getMergedTraits();
|
||||
hasBindings = hasBindings || Boolean(eventHeader || eventPayload);
|
||||
if (eventPayload) {
|
||||
if (member.isBlobSchema()) {
|
||||
out[name] = body;
|
||||
}
|
||||
else if (member.isStringSchema()) {
|
||||
out[name] = (this.serdeContext?.utf8Encoder ?? serde.toUtf8)(body);
|
||||
}
|
||||
else if (member.isStructSchema()) {
|
||||
out[name] = await this.deserializer.read(member, body);
|
||||
}
|
||||
}
|
||||
else if (eventHeader) {
|
||||
const value = event[unionMember].headers[name]?.value;
|
||||
if (value != null) {
|
||||
if (member.isNumericSchema()) {
|
||||
if (value && typeof value === "object" && "bytes" in value) {
|
||||
out[name] = BigInt(value.toString());
|
||||
}
|
||||
else {
|
||||
out[name] = Number(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasBindings) {
|
||||
return {
|
||||
[unionMember]: out,
|
||||
};
|
||||
}
|
||||
if (body.byteLength === 0) {
|
||||
return {
|
||||
[unionMember]: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
[unionMember]: await this.deserializer.read(eventStreamSchema, body),
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
$unknown: event,
|
||||
};
|
||||
}
|
||||
});
|
||||
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
|
||||
const firstEvent = await asyncIterator.next();
|
||||
if (firstEvent.done) {
|
||||
return asyncIterable;
|
||||
}
|
||||
if (firstEvent.value?.[initialResponseMarker]) {
|
||||
if (!responseSchema) {
|
||||
throw new Error("@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.");
|
||||
}
|
||||
for (const key in firstEvent.value) {
|
||||
initialResponseContainer[key] = firstEvent.value[key];
|
||||
}
|
||||
}
|
||||
return {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
if (!firstEvent?.value?.[initialResponseMarker]) {
|
||||
yield firstEvent.value;
|
||||
}
|
||||
while (true) {
|
||||
const { done, value } = await asyncIterator.next();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
yield value;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
writeEventBody(unionMember, unionSchema, event) {
|
||||
const serializer = this.serializer;
|
||||
let eventType = unionMember;
|
||||
let explicitPayloadMember = null;
|
||||
let explicitPayloadContentType;
|
||||
const isKnownSchema = (() => {
|
||||
const struct = unionSchema.getSchema();
|
||||
return struct[4].includes(unionMember);
|
||||
})();
|
||||
const additionalHeaders = {};
|
||||
if (!isKnownSchema) {
|
||||
const [type, value] = event[unionMember];
|
||||
eventType = type;
|
||||
serializer.write(15, value);
|
||||
}
|
||||
else {
|
||||
const eventSchema = unionSchema.getMemberSchema(unionMember);
|
||||
if (eventSchema.isStructSchema()) {
|
||||
for (const [memberName, memberSchema] of eventSchema.structIterator()) {
|
||||
const { eventHeader, eventPayload } = memberSchema.getMergedTraits();
|
||||
if (eventPayload) {
|
||||
explicitPayloadMember = memberName;
|
||||
}
|
||||
else if (eventHeader) {
|
||||
const value = event[unionMember][memberName];
|
||||
let type = "binary";
|
||||
if (memberSchema.isNumericSchema()) {
|
||||
if ((-2) ** 31 <= value && value <= 2 ** 31 - 1) {
|
||||
type = "integer";
|
||||
}
|
||||
else {
|
||||
type = "long";
|
||||
}
|
||||
}
|
||||
else if (memberSchema.isTimestampSchema()) {
|
||||
type = "timestamp";
|
||||
}
|
||||
else if (memberSchema.isStringSchema()) {
|
||||
type = "string";
|
||||
}
|
||||
else if (memberSchema.isBooleanSchema()) {
|
||||
type = "boolean";
|
||||
}
|
||||
if (value != null) {
|
||||
additionalHeaders[memberName] = {
|
||||
type,
|
||||
value,
|
||||
};
|
||||
delete event[unionMember][memberName];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (explicitPayloadMember !== null) {
|
||||
const payloadSchema = eventSchema.getMemberSchema(explicitPayloadMember);
|
||||
if (payloadSchema.isBlobSchema()) {
|
||||
explicitPayloadContentType = "application/octet-stream";
|
||||
}
|
||||
else if (payloadSchema.isStringSchema()) {
|
||||
explicitPayloadContentType = "text/plain";
|
||||
}
|
||||
serializer.write(payloadSchema, event[unionMember][explicitPayloadMember]);
|
||||
}
|
||||
else {
|
||||
serializer.write(eventSchema, event[unionMember]);
|
||||
}
|
||||
}
|
||||
else if (eventSchema.isUnitSchema()) {
|
||||
serializer.write(eventSchema, {});
|
||||
}
|
||||
else {
|
||||
throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.");
|
||||
}
|
||||
}
|
||||
const messageSerialization = serializer.flush() ?? new Uint8Array();
|
||||
const body = typeof messageSerialization === "string"
|
||||
? (this.serdeContext?.utf8Decoder ?? serde.fromUtf8)(messageSerialization)
|
||||
: messageSerialization;
|
||||
return {
|
||||
body,
|
||||
eventType,
|
||||
explicitPayloadContentType,
|
||||
additionalHeaders,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
exports.EventStreamCodec = EventStreamCodec;
|
||||
exports.EventStreamMarshaller = EventStreamMarshaller;
|
||||
exports.EventStreamSerde = EventStreamSerde;
|
||||
exports.HeaderMarshaller = HeaderMarshaller;
|
||||
exports.Int64 = Int64;
|
||||
exports.MessageDecoderStream = MessageDecoderStream;
|
||||
exports.MessageEncoderStream = MessageEncoderStream;
|
||||
exports.SmithyMessageDecoderStream = SmithyMessageDecoderStream;
|
||||
exports.SmithyMessageEncoderStream = SmithyMessageEncoderStream;
|
||||
exports.UniversalEventStreamMarshaller = EventStreamMarshaller$1;
|
||||
exports.eventStreamSerdeProvider = eventStreamSerdeProvider;
|
||||
exports.getChunkedStream = getChunkedStream;
|
||||
exports.getMessageUnmarshaller = getMessageUnmarshaller;
|
||||
exports.getUnmarshalledStream = getUnmarshalledStream;
|
||||
exports.iterableToReadableStream = iterableToReadableStream;
|
||||
exports.readableStreamToIterable = readableStreamToIterable;
|
||||
exports.resolveEventStreamSerdeConfig = resolveEventStreamSerdeConfig;
|
||||
exports.universalEventStreamSerdeProvider = eventStreamSerdeProvider$1;
|
||||
+891
@@ -0,0 +1,891 @@
|
||||
'use strict';
|
||||
|
||||
var crc32 = require('@aws-crypto/crc32');
|
||||
var serde = require('@smithy/core/serde');
|
||||
var node_stream = require('node:stream');
|
||||
|
||||
class Int64 {
|
||||
bytes;
|
||||
constructor(bytes) {
|
||||
this.bytes = bytes;
|
||||
if (bytes.byteLength !== 8) {
|
||||
throw new Error("Int64 buffers must be exactly 8 bytes");
|
||||
}
|
||||
}
|
||||
static fromNumber(number) {
|
||||
if (number > 9_223_372_036_854_775_807 || number < -9223372036854776e3) {
|
||||
throw new Error(`${number} is too large (or, if negative, too small) to represent as an Int64`);
|
||||
}
|
||||
const bytes = new Uint8Array(8);
|
||||
for (let i = 7, remaining = Math.abs(Math.round(number)); i > -1 && remaining > 0; i--, remaining /= 256) {
|
||||
bytes[i] = remaining;
|
||||
}
|
||||
if (number < 0) {
|
||||
negate(bytes);
|
||||
}
|
||||
return new Int64(bytes);
|
||||
}
|
||||
valueOf() {
|
||||
const bytes = this.bytes.slice(0);
|
||||
const negative = bytes[0] & 0b10000000;
|
||||
if (negative) {
|
||||
negate(bytes);
|
||||
}
|
||||
return parseInt(serde.toHex(bytes), 16) * (negative ? -1 : 1);
|
||||
}
|
||||
toString() {
|
||||
return String(this.valueOf());
|
||||
}
|
||||
}
|
||||
function negate(bytes) {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
bytes[i] ^= 0xff;
|
||||
}
|
||||
for (let i = 7; i > -1; i--) {
|
||||
bytes[i]++;
|
||||
if (bytes[i] !== 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderMarshaller {
|
||||
toUtf8;
|
||||
fromUtf8;
|
||||
constructor(toUtf8, fromUtf8) {
|
||||
this.toUtf8 = toUtf8;
|
||||
this.fromUtf8 = fromUtf8;
|
||||
}
|
||||
format(headers) {
|
||||
const chunks = [];
|
||||
for (const headerName of Object.keys(headers)) {
|
||||
const bytes = this.fromUtf8(headerName);
|
||||
chunks.push(Uint8Array.from([bytes.byteLength]), bytes, this.formatHeaderValue(headers[headerName]));
|
||||
}
|
||||
const out = new Uint8Array(chunks.reduce((carry, bytes) => carry + bytes.byteLength, 0));
|
||||
let position = 0;
|
||||
for (const chunk of chunks) {
|
||||
out.set(chunk, position);
|
||||
position += chunk.byteLength;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
formatHeaderValue(header) {
|
||||
switch (header.type) {
|
||||
case "boolean":
|
||||
return Uint8Array.from([header.value ? 0 : 1]);
|
||||
case "byte":
|
||||
return Uint8Array.from([2, header.value]);
|
||||
case "short":
|
||||
const shortView = new DataView(new ArrayBuffer(3));
|
||||
shortView.setUint8(0, 3);
|
||||
shortView.setInt16(1, header.value, false);
|
||||
return new Uint8Array(shortView.buffer);
|
||||
case "integer":
|
||||
const intView = new DataView(new ArrayBuffer(5));
|
||||
intView.setUint8(0, 4);
|
||||
intView.setInt32(1, header.value, false);
|
||||
return new Uint8Array(intView.buffer);
|
||||
case "long":
|
||||
const longBytes = new Uint8Array(9);
|
||||
longBytes[0] = 5;
|
||||
longBytes.set(header.value.bytes, 1);
|
||||
return longBytes;
|
||||
case "binary":
|
||||
const binView = new DataView(new ArrayBuffer(3 + header.value.byteLength));
|
||||
binView.setUint8(0, 6);
|
||||
binView.setUint16(1, header.value.byteLength, false);
|
||||
const binBytes = new Uint8Array(binView.buffer);
|
||||
binBytes.set(header.value, 3);
|
||||
return binBytes;
|
||||
case "string":
|
||||
const utf8Bytes = this.fromUtf8(header.value);
|
||||
const strView = new DataView(new ArrayBuffer(3 + utf8Bytes.byteLength));
|
||||
strView.setUint8(0, 7);
|
||||
strView.setUint16(1, utf8Bytes.byteLength, false);
|
||||
const strBytes = new Uint8Array(strView.buffer);
|
||||
strBytes.set(utf8Bytes, 3);
|
||||
return strBytes;
|
||||
case "timestamp":
|
||||
const tsBytes = new Uint8Array(9);
|
||||
tsBytes[0] = 8;
|
||||
tsBytes.set(Int64.fromNumber(header.value.valueOf()).bytes, 1);
|
||||
return tsBytes;
|
||||
case "uuid":
|
||||
if (!UUID_PATTERN.test(header.value)) {
|
||||
throw new Error(`Invalid UUID received: ${header.value}`);
|
||||
}
|
||||
const uuidBytes = new Uint8Array(17);
|
||||
uuidBytes[0] = 9;
|
||||
uuidBytes.set(serde.fromHex(header.value.replace(/\-/g, "")), 1);
|
||||
return uuidBytes;
|
||||
}
|
||||
}
|
||||
parse(headers) {
|
||||
const out = {};
|
||||
let position = 0;
|
||||
while (position < headers.byteLength) {
|
||||
const nameLength = headers.getUint8(position++);
|
||||
const name = this.toUtf8(new Uint8Array(headers.buffer, headers.byteOffset + position, nameLength));
|
||||
position += nameLength;
|
||||
switch (headers.getUint8(position++)) {
|
||||
case 0:
|
||||
out[name] = {
|
||||
type: BOOLEAN_TAG,
|
||||
value: true,
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
out[name] = {
|
||||
type: BOOLEAN_TAG,
|
||||
value: false,
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
out[name] = {
|
||||
type: BYTE_TAG,
|
||||
value: headers.getInt8(position++),
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
out[name] = {
|
||||
type: SHORT_TAG,
|
||||
value: headers.getInt16(position, false),
|
||||
};
|
||||
position += 2;
|
||||
break;
|
||||
case 4:
|
||||
out[name] = {
|
||||
type: INT_TAG,
|
||||
value: headers.getInt32(position, false),
|
||||
};
|
||||
position += 4;
|
||||
break;
|
||||
case 5:
|
||||
out[name] = {
|
||||
type: LONG_TAG,
|
||||
value: new Int64(new Uint8Array(headers.buffer, headers.byteOffset + position, 8)),
|
||||
};
|
||||
position += 8;
|
||||
break;
|
||||
case 6:
|
||||
const binaryLength = headers.getUint16(position, false);
|
||||
position += 2;
|
||||
out[name] = {
|
||||
type: BINARY_TAG,
|
||||
value: new Uint8Array(headers.buffer, headers.byteOffset + position, binaryLength),
|
||||
};
|
||||
position += binaryLength;
|
||||
break;
|
||||
case 7:
|
||||
const stringLength = headers.getUint16(position, false);
|
||||
position += 2;
|
||||
out[name] = {
|
||||
type: STRING_TAG,
|
||||
value: this.toUtf8(new Uint8Array(headers.buffer, headers.byteOffset + position, stringLength)),
|
||||
};
|
||||
position += stringLength;
|
||||
break;
|
||||
case 8:
|
||||
out[name] = {
|
||||
type: TIMESTAMP_TAG,
|
||||
value: new Date(new Int64(new Uint8Array(headers.buffer, headers.byteOffset + position, 8)).valueOf()),
|
||||
};
|
||||
position += 8;
|
||||
break;
|
||||
case 9:
|
||||
const uuidBytes = new Uint8Array(headers.buffer, headers.byteOffset + position, 16);
|
||||
position += 16;
|
||||
out[name] = {
|
||||
type: UUID_TAG,
|
||||
value: `${serde.toHex(uuidBytes.subarray(0, 4))}-${serde.toHex(uuidBytes.subarray(4, 6))}-${serde.toHex(uuidBytes.subarray(6, 8))}-${serde.toHex(uuidBytes.subarray(8, 10))}-${serde.toHex(uuidBytes.subarray(10))}`,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unrecognized header type tag`);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
var HEADER_VALUE_TYPE;
|
||||
(function (HEADER_VALUE_TYPE) {
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["boolTrue"] = 0] = "boolTrue";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["boolFalse"] = 1] = "boolFalse";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["byte"] = 2] = "byte";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["short"] = 3] = "short";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["integer"] = 4] = "integer";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["long"] = 5] = "long";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["byteArray"] = 6] = "byteArray";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["string"] = 7] = "string";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["timestamp"] = 8] = "timestamp";
|
||||
HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["uuid"] = 9] = "uuid";
|
||||
})(HEADER_VALUE_TYPE || (HEADER_VALUE_TYPE = {}));
|
||||
const BOOLEAN_TAG = "boolean";
|
||||
const BYTE_TAG = "byte";
|
||||
const SHORT_TAG = "short";
|
||||
const INT_TAG = "integer";
|
||||
const LONG_TAG = "long";
|
||||
const BINARY_TAG = "binary";
|
||||
const STRING_TAG = "string";
|
||||
const TIMESTAMP_TAG = "timestamp";
|
||||
const UUID_TAG = "uuid";
|
||||
const UUID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
||||
|
||||
const PRELUDE_MEMBER_LENGTH = 4;
|
||||
const PRELUDE_LENGTH = PRELUDE_MEMBER_LENGTH * 2;
|
||||
const CHECKSUM_LENGTH = 4;
|
||||
const MINIMUM_MESSAGE_LENGTH = PRELUDE_LENGTH + CHECKSUM_LENGTH * 2;
|
||||
function splitMessage({ byteLength, byteOffset, buffer }) {
|
||||
if (byteLength < MINIMUM_MESSAGE_LENGTH) {
|
||||
throw new Error("Provided message too short to accommodate event stream message overhead");
|
||||
}
|
||||
const view = new DataView(buffer, byteOffset, byteLength);
|
||||
const messageLength = view.getUint32(0, false);
|
||||
if (byteLength !== messageLength) {
|
||||
throw new Error("Reported message length does not match received message length");
|
||||
}
|
||||
const headerLength = view.getUint32(PRELUDE_MEMBER_LENGTH, false);
|
||||
const expectedPreludeChecksum = view.getUint32(PRELUDE_LENGTH, false);
|
||||
const expectedMessageChecksum = view.getUint32(byteLength - CHECKSUM_LENGTH, false);
|
||||
const checksummer = new crc32.Crc32().update(new Uint8Array(buffer, byteOffset, PRELUDE_LENGTH));
|
||||
if (expectedPreludeChecksum !== checksummer.digest()) {
|
||||
throw new Error(`The prelude checksum specified in the message (${expectedPreludeChecksum}) does not match the calculated CRC32 checksum (${checksummer.digest()})`);
|
||||
}
|
||||
checksummer.update(new Uint8Array(buffer, byteOffset + PRELUDE_LENGTH, byteLength - (PRELUDE_LENGTH + CHECKSUM_LENGTH)));
|
||||
if (expectedMessageChecksum !== checksummer.digest()) {
|
||||
throw new Error(`The message checksum (${checksummer.digest()}) did not match the expected value of ${expectedMessageChecksum}`);
|
||||
}
|
||||
return {
|
||||
headers: new DataView(buffer, byteOffset + PRELUDE_LENGTH + CHECKSUM_LENGTH, headerLength),
|
||||
body: new Uint8Array(buffer, byteOffset + PRELUDE_LENGTH + CHECKSUM_LENGTH + headerLength, messageLength - headerLength - (PRELUDE_LENGTH + CHECKSUM_LENGTH + CHECKSUM_LENGTH)),
|
||||
};
|
||||
}
|
||||
|
||||
class EventStreamCodec {
|
||||
headerMarshaller;
|
||||
messageBuffer;
|
||||
isEndOfStream;
|
||||
constructor(toUtf8, fromUtf8) {
|
||||
this.headerMarshaller = new HeaderMarshaller(toUtf8, fromUtf8);
|
||||
this.messageBuffer = [];
|
||||
this.isEndOfStream = false;
|
||||
}
|
||||
feed(message) {
|
||||
this.messageBuffer.push(this.decode(message));
|
||||
}
|
||||
endOfStream() {
|
||||
this.isEndOfStream = true;
|
||||
}
|
||||
getMessage() {
|
||||
const message = this.messageBuffer.pop();
|
||||
const isEndOfStream = this.isEndOfStream;
|
||||
return {
|
||||
getMessage() {
|
||||
return message;
|
||||
},
|
||||
isEndOfStream() {
|
||||
return isEndOfStream;
|
||||
},
|
||||
};
|
||||
}
|
||||
getAvailableMessages() {
|
||||
const messages = this.messageBuffer;
|
||||
this.messageBuffer = [];
|
||||
const isEndOfStream = this.isEndOfStream;
|
||||
return {
|
||||
getMessages() {
|
||||
return messages;
|
||||
},
|
||||
isEndOfStream() {
|
||||
return isEndOfStream;
|
||||
},
|
||||
};
|
||||
}
|
||||
encode({ headers: rawHeaders, body }) {
|
||||
const headers = this.headerMarshaller.format(rawHeaders);
|
||||
const length = headers.byteLength + body.byteLength + 16;
|
||||
const out = new Uint8Array(length);
|
||||
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
||||
const checksum = new crc32.Crc32();
|
||||
view.setUint32(0, length, false);
|
||||
view.setUint32(4, headers.byteLength, false);
|
||||
view.setUint32(8, checksum.update(out.subarray(0, 8)).digest(), false);
|
||||
out.set(headers, 12);
|
||||
out.set(body, headers.byteLength + 12);
|
||||
view.setUint32(length - 4, checksum.update(out.subarray(8, length - 4)).digest(), false);
|
||||
return out;
|
||||
}
|
||||
decode(message) {
|
||||
const { headers, body } = splitMessage(message);
|
||||
return { headers: this.headerMarshaller.parse(headers), body };
|
||||
}
|
||||
formatHeaders(rawHeaders) {
|
||||
return this.headerMarshaller.format(rawHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
class MessageDecoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const bytes of this.options.inputStream) {
|
||||
const decoded = this.options.decoder.decode(bytes);
|
||||
yield decoded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MessageEncoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const msg of this.options.messageStream) {
|
||||
const encoded = this.options.encoder.encode(msg);
|
||||
yield encoded;
|
||||
}
|
||||
if (this.options.includeEndFrame) {
|
||||
yield new Uint8Array(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SmithyMessageDecoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const message of this.options.messageStream) {
|
||||
const deserialized = await this.options.deserializer(message);
|
||||
if (deserialized === undefined)
|
||||
continue;
|
||||
yield deserialized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SmithyMessageEncoderStream {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
[Symbol.asyncIterator]() {
|
||||
return this.asyncIterator();
|
||||
}
|
||||
async *asyncIterator() {
|
||||
for await (const chunk of this.options.inputStream) {
|
||||
const payloadBuf = this.options.serializer(chunk);
|
||||
yield payloadBuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getChunkedStream(source) {
|
||||
let currentMessageTotalLength = 0;
|
||||
let currentMessagePendingLength = 0;
|
||||
let currentMessage = null;
|
||||
let messageLengthBuffer = null;
|
||||
const allocateMessage = (size) => {
|
||||
if (typeof size !== "number") {
|
||||
throw new Error("Attempted to allocate an event message where size was not a number: " + size);
|
||||
}
|
||||
currentMessageTotalLength = size;
|
||||
currentMessagePendingLength = 4;
|
||||
currentMessage = new Uint8Array(size);
|
||||
const currentMessageView = new DataView(currentMessage.buffer);
|
||||
currentMessageView.setUint32(0, size, false);
|
||||
};
|
||||
const iterator = async function* () {
|
||||
const sourceIterator = source[Symbol.asyncIterator]();
|
||||
while (true) {
|
||||
const { value, done } = await sourceIterator.next();
|
||||
if (done) {
|
||||
if (!currentMessageTotalLength) {
|
||||
return;
|
||||
}
|
||||
else if (currentMessageTotalLength === currentMessagePendingLength) {
|
||||
yield currentMessage;
|
||||
}
|
||||
else {
|
||||
throw new Error("Truncated event message received.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const chunkLength = value.length;
|
||||
let currentOffset = 0;
|
||||
while (currentOffset < chunkLength) {
|
||||
if (!currentMessage) {
|
||||
const bytesRemaining = chunkLength - currentOffset;
|
||||
if (!messageLengthBuffer) {
|
||||
messageLengthBuffer = new Uint8Array(4);
|
||||
}
|
||||
const numBytesForTotal = Math.min(4 - currentMessagePendingLength, bytesRemaining);
|
||||
messageLengthBuffer.set(value.slice(currentOffset, currentOffset + numBytesForTotal), currentMessagePendingLength);
|
||||
currentMessagePendingLength += numBytesForTotal;
|
||||
currentOffset += numBytesForTotal;
|
||||
if (currentMessagePendingLength < 4) {
|
||||
break;
|
||||
}
|
||||
allocateMessage(new DataView(messageLengthBuffer.buffer).getUint32(0, false));
|
||||
messageLengthBuffer = null;
|
||||
}
|
||||
const numBytesToWrite = Math.min(currentMessageTotalLength - currentMessagePendingLength, chunkLength - currentOffset);
|
||||
currentMessage.set(value.slice(currentOffset, currentOffset + numBytesToWrite), currentMessagePendingLength);
|
||||
currentMessagePendingLength += numBytesToWrite;
|
||||
currentOffset += numBytesToWrite;
|
||||
if (currentMessageTotalLength && currentMessageTotalLength === currentMessagePendingLength) {
|
||||
yield currentMessage;
|
||||
currentMessage = null;
|
||||
currentMessageTotalLength = 0;
|
||||
currentMessagePendingLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
[Symbol.asyncIterator]: iterator,
|
||||
};
|
||||
}
|
||||
|
||||
function getUnmarshalledStream(source, options) {
|
||||
const messageUnmarshaller = getMessageUnmarshaller(options.deserializer, options.toUtf8);
|
||||
return {
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
for await (const chunk of source) {
|
||||
const message = options.eventStreamCodec.decode(chunk);
|
||||
const type = await messageUnmarshaller(message);
|
||||
if (type === undefined)
|
||||
continue;
|
||||
yield type;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
function getMessageUnmarshaller(deserializer, toUtf8) {
|
||||
return async function (message) {
|
||||
const { value: messageType } = message.headers[":message-type"];
|
||||
if (messageType === "error") {
|
||||
const unmodeledError = new Error(message.headers[":error-message"].value || "UnknownError");
|
||||
unmodeledError.name = message.headers[":error-code"].value;
|
||||
throw unmodeledError;
|
||||
}
|
||||
else if (messageType === "exception") {
|
||||
const code = message.headers[":exception-type"].value;
|
||||
const exception = { [code]: message };
|
||||
const deserializedException = await deserializer(exception);
|
||||
if (deserializedException.$unknown) {
|
||||
const error = new Error(toUtf8(message.body));
|
||||
error.name = code;
|
||||
throw error;
|
||||
}
|
||||
throw deserializedException[code];
|
||||
}
|
||||
else if (messageType === "event") {
|
||||
const event = {
|
||||
[message.headers[":event-type"].value]: message,
|
||||
};
|
||||
const deserialized = await deserializer(event);
|
||||
if (deserialized.$unknown)
|
||||
return;
|
||||
return deserialized;
|
||||
}
|
||||
else {
|
||||
throw Error(`Unrecognizable event type: ${message.headers[":event-type"].value}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let EventStreamMarshaller$1 = class EventStreamMarshaller {
|
||||
eventStreamCodec;
|
||||
utfEncoder;
|
||||
constructor({ utf8Encoder, utf8Decoder }) {
|
||||
this.eventStreamCodec = new EventStreamCodec(utf8Encoder, utf8Decoder);
|
||||
this.utfEncoder = utf8Encoder;
|
||||
}
|
||||
deserialize(body, deserializer) {
|
||||
const inputStream = getChunkedStream(body);
|
||||
return new SmithyMessageDecoderStream({
|
||||
messageStream: new MessageDecoderStream({ inputStream, decoder: this.eventStreamCodec }),
|
||||
deserializer: getMessageUnmarshaller(deserializer, this.utfEncoder),
|
||||
});
|
||||
}
|
||||
serialize(inputStream, serializer) {
|
||||
return new MessageEncoderStream({
|
||||
messageStream: new SmithyMessageEncoderStream({ inputStream, serializer }),
|
||||
encoder: this.eventStreamCodec,
|
||||
includeEndFrame: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
const eventStreamSerdeProvider$1 = (options) => new EventStreamMarshaller$1(options);
|
||||
|
||||
class EventStreamMarshaller {
|
||||
universalMarshaller;
|
||||
constructor({ utf8Encoder, utf8Decoder }) {
|
||||
this.universalMarshaller = new EventStreamMarshaller$1({
|
||||
utf8Decoder,
|
||||
utf8Encoder,
|
||||
});
|
||||
}
|
||||
deserialize(body, deserializer) {
|
||||
const bodyIterable = typeof body[Symbol.asyncIterator] === "function" ? body : readableToIterable(body);
|
||||
return this.universalMarshaller.deserialize(bodyIterable, deserializer);
|
||||
}
|
||||
serialize(input, serializer) {
|
||||
return node_stream.Readable.from(this.universalMarshaller.serialize(input, serializer));
|
||||
}
|
||||
}
|
||||
const eventStreamSerdeProvider = (options) => new EventStreamMarshaller(options);
|
||||
async function* readableToIterable(readStream) {
|
||||
let streamEnded = false;
|
||||
let generationEnded = false;
|
||||
const records = new Array();
|
||||
readStream.on("error", (err) => {
|
||||
if (!streamEnded) {
|
||||
streamEnded = true;
|
||||
}
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
readStream.on("data", (data) => {
|
||||
records.push(data);
|
||||
});
|
||||
readStream.on("end", () => {
|
||||
streamEnded = true;
|
||||
});
|
||||
while (!generationEnded) {
|
||||
const value = await new Promise((resolve) => setTimeout(() => resolve(records.shift()), 0));
|
||||
if (value) {
|
||||
yield value;
|
||||
}
|
||||
generationEnded = streamEnded && records.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
const readableStreamToIterable = (readableStream) => ({
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
const reader = readableStream.getReader();
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done)
|
||||
return;
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
},
|
||||
});
|
||||
const iterableToReadableStream = (asyncIterable) => {
|
||||
const iterator = asyncIterable[Symbol.asyncIterator]();
|
||||
return new ReadableStream({
|
||||
async pull(controller) {
|
||||
const { done, value } = await iterator.next();
|
||||
if (done) {
|
||||
return controller.close();
|
||||
}
|
||||
controller.enqueue(value);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const resolveEventStreamSerdeConfig = (input) => Object.assign(input, {
|
||||
eventStreamMarshaller: input.eventStreamSerdeProvider(input),
|
||||
});
|
||||
|
||||
class EventStreamSerde {
|
||||
marshaller;
|
||||
serializer;
|
||||
deserializer;
|
||||
serdeContext;
|
||||
defaultContentType;
|
||||
constructor({ marshaller, serializer, deserializer, serdeContext, defaultContentType, }) {
|
||||
this.marshaller = marshaller;
|
||||
this.serializer = serializer;
|
||||
this.deserializer = deserializer;
|
||||
this.serdeContext = serdeContext;
|
||||
this.defaultContentType = defaultContentType;
|
||||
}
|
||||
async serializeEventStream({ eventStream, requestSchema, initialRequest, }) {
|
||||
const marshaller = this.marshaller;
|
||||
const eventStreamMember = requestSchema.getEventStreamMember();
|
||||
const unionSchema = requestSchema.getMemberSchema(eventStreamMember);
|
||||
const serializer = this.serializer;
|
||||
const defaultContentType = this.defaultContentType;
|
||||
const initialRequestMarker = Symbol("initialRequestMarker");
|
||||
const eventStreamIterable = {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
if (initialRequest) {
|
||||
const headers = {
|
||||
":event-type": { type: "string", value: "initial-request" },
|
||||
":message-type": { type: "string", value: "event" },
|
||||
":content-type": { type: "string", value: defaultContentType },
|
||||
};
|
||||
serializer.write(requestSchema, initialRequest);
|
||||
const body = serializer.flush();
|
||||
yield {
|
||||
[initialRequestMarker]: true,
|
||||
headers,
|
||||
body,
|
||||
};
|
||||
}
|
||||
for await (const page of eventStream) {
|
||||
yield page;
|
||||
}
|
||||
},
|
||||
};
|
||||
return marshaller.serialize(eventStreamIterable, (event) => {
|
||||
if (event[initialRequestMarker]) {
|
||||
return {
|
||||
headers: event.headers,
|
||||
body: event.body,
|
||||
};
|
||||
}
|
||||
let unionMember = "";
|
||||
for (const key in event) {
|
||||
if (key !== "__type") {
|
||||
unionMember = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const { additionalHeaders, body, eventType, explicitPayloadContentType } = this.writeEventBody(unionMember, unionSchema, event);
|
||||
const headers = {
|
||||
":event-type": { type: "string", value: eventType },
|
||||
":message-type": { type: "string", value: "event" },
|
||||
":content-type": { type: "string", value: explicitPayloadContentType ?? defaultContentType },
|
||||
...additionalHeaders,
|
||||
};
|
||||
return {
|
||||
headers,
|
||||
body,
|
||||
};
|
||||
});
|
||||
}
|
||||
async deserializeEventStream({ response, responseSchema, initialResponseContainer, }) {
|
||||
const marshaller = this.marshaller;
|
||||
const eventStreamMember = responseSchema.getEventStreamMember();
|
||||
const unionSchema = responseSchema.getMemberSchema(eventStreamMember);
|
||||
const memberSchemas = unionSchema.getMemberSchemas();
|
||||
const initialResponseMarker = Symbol("initialResponseMarker");
|
||||
const asyncIterable = marshaller.deserialize(response.body, async (event) => {
|
||||
let unionMember = "";
|
||||
for (const key in event) {
|
||||
if (key !== "__type") {
|
||||
unionMember = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const body = event[unionMember].body;
|
||||
if (unionMember === "initial-response") {
|
||||
const dataObject = await this.deserializer.read(responseSchema, body);
|
||||
delete dataObject[eventStreamMember];
|
||||
return {
|
||||
[initialResponseMarker]: true,
|
||||
...dataObject,
|
||||
};
|
||||
}
|
||||
else if (unionMember in memberSchemas) {
|
||||
const eventStreamSchema = memberSchemas[unionMember];
|
||||
if (eventStreamSchema.isStructSchema()) {
|
||||
const out = {};
|
||||
let hasBindings = false;
|
||||
for (const [name, member] of eventStreamSchema.structIterator()) {
|
||||
const { eventHeader, eventPayload } = member.getMergedTraits();
|
||||
hasBindings = hasBindings || Boolean(eventHeader || eventPayload);
|
||||
if (eventPayload) {
|
||||
if (member.isBlobSchema()) {
|
||||
out[name] = body;
|
||||
}
|
||||
else if (member.isStringSchema()) {
|
||||
out[name] = (this.serdeContext?.utf8Encoder ?? serde.toUtf8)(body);
|
||||
}
|
||||
else if (member.isStructSchema()) {
|
||||
out[name] = await this.deserializer.read(member, body);
|
||||
}
|
||||
}
|
||||
else if (eventHeader) {
|
||||
const value = event[unionMember].headers[name]?.value;
|
||||
if (value != null) {
|
||||
if (member.isNumericSchema()) {
|
||||
if (value && typeof value === "object" && "bytes" in value) {
|
||||
out[name] = BigInt(value.toString());
|
||||
}
|
||||
else {
|
||||
out[name] = Number(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasBindings) {
|
||||
return {
|
||||
[unionMember]: out,
|
||||
};
|
||||
}
|
||||
if (body.byteLength === 0) {
|
||||
return {
|
||||
[unionMember]: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
[unionMember]: await this.deserializer.read(eventStreamSchema, body),
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
$unknown: event,
|
||||
};
|
||||
}
|
||||
});
|
||||
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
|
||||
const firstEvent = await asyncIterator.next();
|
||||
if (firstEvent.done) {
|
||||
return asyncIterable;
|
||||
}
|
||||
if (firstEvent.value?.[initialResponseMarker]) {
|
||||
if (!responseSchema) {
|
||||
throw new Error("@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.");
|
||||
}
|
||||
for (const key in firstEvent.value) {
|
||||
initialResponseContainer[key] = firstEvent.value[key];
|
||||
}
|
||||
}
|
||||
return {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
if (!firstEvent?.value?.[initialResponseMarker]) {
|
||||
yield firstEvent.value;
|
||||
}
|
||||
while (true) {
|
||||
const { done, value } = await asyncIterator.next();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
yield value;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
writeEventBody(unionMember, unionSchema, event) {
|
||||
const serializer = this.serializer;
|
||||
let eventType = unionMember;
|
||||
let explicitPayloadMember = null;
|
||||
let explicitPayloadContentType;
|
||||
const isKnownSchema = (() => {
|
||||
const struct = unionSchema.getSchema();
|
||||
return struct[4].includes(unionMember);
|
||||
})();
|
||||
const additionalHeaders = {};
|
||||
if (!isKnownSchema) {
|
||||
const [type, value] = event[unionMember];
|
||||
eventType = type;
|
||||
serializer.write(15, value);
|
||||
}
|
||||
else {
|
||||
const eventSchema = unionSchema.getMemberSchema(unionMember);
|
||||
if (eventSchema.isStructSchema()) {
|
||||
for (const [memberName, memberSchema] of eventSchema.structIterator()) {
|
||||
const { eventHeader, eventPayload } = memberSchema.getMergedTraits();
|
||||
if (eventPayload) {
|
||||
explicitPayloadMember = memberName;
|
||||
}
|
||||
else if (eventHeader) {
|
||||
const value = event[unionMember][memberName];
|
||||
let type = "binary";
|
||||
if (memberSchema.isNumericSchema()) {
|
||||
if ((-2) ** 31 <= value && value <= 2 ** 31 - 1) {
|
||||
type = "integer";
|
||||
}
|
||||
else {
|
||||
type = "long";
|
||||
}
|
||||
}
|
||||
else if (memberSchema.isTimestampSchema()) {
|
||||
type = "timestamp";
|
||||
}
|
||||
else if (memberSchema.isStringSchema()) {
|
||||
type = "string";
|
||||
}
|
||||
else if (memberSchema.isBooleanSchema()) {
|
||||
type = "boolean";
|
||||
}
|
||||
if (value != null) {
|
||||
additionalHeaders[memberName] = {
|
||||
type,
|
||||
value,
|
||||
};
|
||||
delete event[unionMember][memberName];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (explicitPayloadMember !== null) {
|
||||
const payloadSchema = eventSchema.getMemberSchema(explicitPayloadMember);
|
||||
if (payloadSchema.isBlobSchema()) {
|
||||
explicitPayloadContentType = "application/octet-stream";
|
||||
}
|
||||
else if (payloadSchema.isStringSchema()) {
|
||||
explicitPayloadContentType = "text/plain";
|
||||
}
|
||||
serializer.write(payloadSchema, event[unionMember][explicitPayloadMember]);
|
||||
}
|
||||
else {
|
||||
serializer.write(eventSchema, event[unionMember]);
|
||||
}
|
||||
}
|
||||
else if (eventSchema.isUnitSchema()) {
|
||||
serializer.write(eventSchema, {});
|
||||
}
|
||||
else {
|
||||
throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.");
|
||||
}
|
||||
}
|
||||
const messageSerialization = serializer.flush() ?? new Uint8Array();
|
||||
const body = typeof messageSerialization === "string"
|
||||
? (this.serdeContext?.utf8Decoder ?? serde.fromUtf8)(messageSerialization)
|
||||
: messageSerialization;
|
||||
return {
|
||||
body,
|
||||
eventType,
|
||||
explicitPayloadContentType,
|
||||
additionalHeaders,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
exports.EventStreamCodec = EventStreamCodec;
|
||||
exports.EventStreamMarshaller = EventStreamMarshaller;
|
||||
exports.EventStreamSerde = EventStreamSerde;
|
||||
exports.HeaderMarshaller = HeaderMarshaller;
|
||||
exports.Int64 = Int64;
|
||||
exports.MessageDecoderStream = MessageDecoderStream;
|
||||
exports.MessageEncoderStream = MessageEncoderStream;
|
||||
exports.SmithyMessageDecoderStream = SmithyMessageDecoderStream;
|
||||
exports.SmithyMessageEncoderStream = SmithyMessageEncoderStream;
|
||||
exports.UniversalEventStreamMarshaller = EventStreamMarshaller$1;
|
||||
exports.eventStreamSerdeProvider = eventStreamSerdeProvider;
|
||||
exports.getChunkedStream = getChunkedStream;
|
||||
exports.getMessageUnmarshaller = getMessageUnmarshaller;
|
||||
exports.getUnmarshalledStream = getUnmarshalledStream;
|
||||
exports.iterableToReadableStream = iterableToReadableStream;
|
||||
exports.readableStreamToIterable = readableStreamToIterable;
|
||||
exports.resolveEventStreamSerdeConfig = resolveEventStreamSerdeConfig;
|
||||
exports.universalEventStreamSerdeProvider = eventStreamSerdeProvider$1;
|
||||
+1029
File diff suppressed because it is too large
Load Diff
+817
@@ -0,0 +1,817 @@
|
||||
'use strict';
|
||||
|
||||
var client = require('@smithy/core/client');
|
||||
var protocols = require('@smithy/core/protocols');
|
||||
var serde = require('@smithy/core/serde');
|
||||
|
||||
const isStreamingPayload = (request) => request?.body instanceof ReadableStream;
|
||||
|
||||
const CLOCK_SKEW_ERROR_CODES = [
|
||||
"AuthFailure",
|
||||
"InvalidSignatureException",
|
||||
"RequestExpired",
|
||||
"RequestInTheFuture",
|
||||
"RequestTimeTooSkewed",
|
||||
"SignatureDoesNotMatch",
|
||||
];
|
||||
const THROTTLING_ERROR_CODES = [
|
||||
"BandwidthLimitExceeded",
|
||||
"EC2ThrottledException",
|
||||
"LimitExceededException",
|
||||
"PriorRequestNotComplete",
|
||||
"ProvisionedThroughputExceededException",
|
||||
"RequestLimitExceeded",
|
||||
"RequestThrottled",
|
||||
"RequestThrottledException",
|
||||
"SlowDown",
|
||||
"ThrottledException",
|
||||
"Throttling",
|
||||
"ThrottlingException",
|
||||
"TooManyRequestsException",
|
||||
"TransactionInProgressException",
|
||||
];
|
||||
const TRANSIENT_ERROR_CODES = ["TimeoutError", "RequestTimeout", "RequestTimeoutException"];
|
||||
const TRANSIENT_ERROR_STATUS_CODES = [500, 502, 503, 504];
|
||||
const NODEJS_TIMEOUT_ERROR_CODES = ["ECONNRESET", "ECONNREFUSED", "EPIPE", "ETIMEDOUT"];
|
||||
const NODEJS_NETWORK_ERROR_CODES = ["EHOSTUNREACH", "ENETUNREACH", "ENOTFOUND", "EAI_AGAIN"];
|
||||
|
||||
const isRetryableByTrait = (error) => error?.$retryable !== undefined;
|
||||
const isClockSkewError = (error) => CLOCK_SKEW_ERROR_CODES.includes(error.name);
|
||||
const isClockSkewCorrectedError = (error) => error.$metadata?.clockSkewCorrected;
|
||||
const isBrowserNetworkError = (error) => {
|
||||
const errorMessages = new Set([
|
||||
"Failed to fetch",
|
||||
"NetworkError when attempting to fetch resource",
|
||||
"The Internet connection appears to be offline",
|
||||
"Load failed",
|
||||
"Network request failed",
|
||||
]);
|
||||
const isValid = error && error instanceof TypeError;
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
return errorMessages.has(error.message);
|
||||
};
|
||||
const isThrottlingError = (error) => error.$metadata?.httpStatusCode === 429 ||
|
||||
THROTTLING_ERROR_CODES.includes(error.name) ||
|
||||
error.$retryable?.throttling == true;
|
||||
const isTransientError = (error, depth = 0) => isRetryableByTrait(error) ||
|
||||
isClockSkewCorrectedError(error) ||
|
||||
(error.name === "InvalidSignatureException" && error.message?.includes("Signature expired")) ||
|
||||
TRANSIENT_ERROR_CODES.includes(error.name) ||
|
||||
NODEJS_TIMEOUT_ERROR_CODES.includes(error?.code || "") ||
|
||||
NODEJS_NETWORK_ERROR_CODES.includes(error?.code || "") ||
|
||||
TRANSIENT_ERROR_STATUS_CODES.includes(error.$metadata?.httpStatusCode || 0) ||
|
||||
isBrowserNetworkError(error) ||
|
||||
isNodeJsHttp2TransientError(error) ||
|
||||
(error.cause !== undefined && depth <= 10 && isTransientError(error.cause, depth + 1));
|
||||
const isServerError = (error) => {
|
||||
if (error.$metadata?.httpStatusCode !== undefined) {
|
||||
const statusCode = error.$metadata.httpStatusCode;
|
||||
if (500 <= statusCode && statusCode <= 599 && !isTransientError(error)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
function isNodeJsHttp2TransientError(error) {
|
||||
return error.code === "ERR_HTTP2_STREAM_ERROR" && error.message.includes("NGHTTP2_REFUSED_STREAM");
|
||||
}
|
||||
|
||||
const DEFAULT_RETRY_DELAY_BASE = 100;
|
||||
const MAXIMUM_RETRY_DELAY = 20 * 1000;
|
||||
const THROTTLING_RETRY_DELAY_BASE = 500;
|
||||
const INITIAL_RETRY_TOKENS = 500;
|
||||
const RETRY_COST = 5;
|
||||
const TIMEOUT_RETRY_COST = 10;
|
||||
const NO_RETRY_INCREMENT = 1;
|
||||
const INVOCATION_ID_HEADER = "amz-sdk-invocation-id";
|
||||
const REQUEST_HEADER = "amz-sdk-request";
|
||||
|
||||
function parseRetryAfterHeader(response, logger) {
|
||||
if (!protocols.HttpResponse.isInstance(response)) {
|
||||
return;
|
||||
}
|
||||
for (const header of Object.keys(response.headers)) {
|
||||
const h = header.toLowerCase();
|
||||
if (h === "retry-after") {
|
||||
const retryAfter = response.headers[header];
|
||||
let retryAfterSeconds = NaN;
|
||||
if (retryAfter.endsWith("GMT")) {
|
||||
try {
|
||||
const date = serde.parseRfc7231DateTime(retryAfter);
|
||||
retryAfterSeconds = (date.getTime() - Date.now()) / 1000;
|
||||
}
|
||||
catch (e) {
|
||||
logger?.trace?.("Failed to parse retry-after header");
|
||||
logger?.trace?.(e);
|
||||
}
|
||||
}
|
||||
else if (retryAfter.match(/ GMT, ((\d+)|(\d+\.\d+))$/)) {
|
||||
retryAfterSeconds = Number(retryAfter.match(/ GMT, ([\d.]+)$/)?.[1]);
|
||||
}
|
||||
else if (retryAfter.match(/^((\d+)|(\d+\.\d+))$/)) {
|
||||
retryAfterSeconds = Number(retryAfter);
|
||||
}
|
||||
else if (Date.parse(retryAfter) >= Date.now()) {
|
||||
retryAfterSeconds = (Date.parse(retryAfter) - Date.now()) / 1000;
|
||||
}
|
||||
if (isNaN(retryAfterSeconds)) {
|
||||
return;
|
||||
}
|
||||
return new Date(Date.now() + retryAfterSeconds * 1000);
|
||||
}
|
||||
else if (h === "x-amz-retry-after") {
|
||||
const v = response.headers[header];
|
||||
const backoffMilliseconds = Number(v);
|
||||
if (isNaN(backoffMilliseconds)) {
|
||||
logger?.trace?.(`Failed to parse x-amz-retry-after=${v}`);
|
||||
return;
|
||||
}
|
||||
return new Date(Date.now() + backoffMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
function getRetryAfterHint(response, logger) {
|
||||
return parseRetryAfterHeader(response, logger);
|
||||
}
|
||||
|
||||
const asSdkError = (error) => {
|
||||
if (error instanceof Error)
|
||||
return error;
|
||||
if (error instanceof Object)
|
||||
return Object.assign(new Error(), error);
|
||||
if (typeof error === "string")
|
||||
return new Error(error);
|
||||
return new Error(`AWS SDK error wrapper for ${error}`);
|
||||
};
|
||||
|
||||
function bindRetryMiddleware(isStreamingPayload) {
|
||||
return (options) => (next, context) => async (args) => {
|
||||
let retryStrategy = await options.retryStrategy();
|
||||
const maxAttempts = await options.maxAttempts();
|
||||
if (isRetryStrategyV2(retryStrategy)) {
|
||||
retryStrategy = retryStrategy;
|
||||
let retryToken = await retryStrategy.acquireInitialRetryToken((context["partition_id"] ?? "") + (context.__retryLongPoll ? ":longpoll" : ""));
|
||||
let lastError = new Error();
|
||||
let attempts = 0;
|
||||
let totalRetryDelay = 0;
|
||||
const { request } = args;
|
||||
const isRequest = protocols.HttpRequest.isInstance(request);
|
||||
if (isRequest) {
|
||||
request.headers[INVOCATION_ID_HEADER] = serde.v4();
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
if (isRequest) {
|
||||
request.headers[REQUEST_HEADER] = `attempt=${attempts + 1}; max=${maxAttempts}`;
|
||||
}
|
||||
const { response, output } = await next(args);
|
||||
retryStrategy.recordSuccess(retryToken);
|
||||
output.$metadata.attempts = attempts + 1;
|
||||
output.$metadata.totalRetryDelay = totalRetryDelay;
|
||||
return { response, output };
|
||||
}
|
||||
catch (e) {
|
||||
const retryErrorInfo = getRetryErrorInfo(e, options.logger);
|
||||
lastError = asSdkError(e);
|
||||
if (isRequest && isStreamingPayload(request)) {
|
||||
(context.logger instanceof client.NoOpLogger ? console : context.logger)?.warn("An error was encountered in a non-retryable streaming request.");
|
||||
throw lastError;
|
||||
}
|
||||
try {
|
||||
retryToken = await retryStrategy.refreshRetryTokenForRetry(retryToken, retryErrorInfo);
|
||||
}
|
||||
catch (refreshError) {
|
||||
if (!lastError.$metadata) {
|
||||
lastError.$metadata = {};
|
||||
}
|
||||
lastError.$metadata.attempts = attempts + 1;
|
||||
lastError.$metadata.totalRetryDelay = totalRetryDelay;
|
||||
throw lastError;
|
||||
}
|
||||
attempts = retryToken.getRetryCount();
|
||||
const delay = retryToken.getRetryDelay();
|
||||
totalRetryDelay += (retryToken?.$retryLog?.acquisitionDelay ?? 0) + delay;
|
||||
if (delay > 0) {
|
||||
await cooldown(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
retryStrategy = retryStrategy;
|
||||
if (retryStrategy?.mode) {
|
||||
context.userAgent = [...(context.userAgent || []), ["cfg/retry-mode", retryStrategy.mode]];
|
||||
}
|
||||
return retryStrategy.retry(next, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
const cooldown = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const isRetryStrategyV2 = (retryStrategy) => typeof retryStrategy.acquireInitialRetryToken !== "undefined" &&
|
||||
typeof retryStrategy.refreshRetryTokenForRetry !== "undefined" &&
|
||||
typeof retryStrategy.recordSuccess !== "undefined";
|
||||
const getRetryErrorInfo = (error, logger) => {
|
||||
const errorInfo = {
|
||||
error,
|
||||
errorType: getRetryErrorType(error),
|
||||
};
|
||||
const retryAfterHint = parseRetryAfterHeader(error.$response, logger);
|
||||
if (retryAfterHint) {
|
||||
errorInfo.retryAfterHint = retryAfterHint;
|
||||
}
|
||||
return errorInfo;
|
||||
};
|
||||
const getRetryErrorType = (error) => {
|
||||
if (isThrottlingError(error))
|
||||
return "THROTTLING";
|
||||
if (isTransientError(error))
|
||||
return "TRANSIENT";
|
||||
if (isServerError(error))
|
||||
return "SERVER_ERROR";
|
||||
return "CLIENT_ERROR";
|
||||
};
|
||||
const retryMiddlewareOptions = {
|
||||
name: "retryMiddleware",
|
||||
tags: ["RETRY"],
|
||||
step: "finalizeRequest",
|
||||
priority: "high",
|
||||
override: true,
|
||||
};
|
||||
function bindGetRetryPlugin(isStreamingPayload) {
|
||||
const retryMiddleware = bindRetryMiddleware(isStreamingPayload);
|
||||
return (options) => ({
|
||||
applyToStack: (clientStack) => {
|
||||
clientStack.add(retryMiddleware(options), retryMiddlewareOptions);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
class DefaultRateLimiter {
|
||||
static setTimeoutFn = setTimeout;
|
||||
beta;
|
||||
minCapacity;
|
||||
minFillRate;
|
||||
scaleConstant;
|
||||
smooth;
|
||||
enabled = false;
|
||||
availableTokens = 0;
|
||||
lastMaxRate = 0;
|
||||
measuredTxRate = 0;
|
||||
requestCount = 0;
|
||||
fillRate;
|
||||
lastThrottleTime;
|
||||
lastTimestamp = 0;
|
||||
lastTxRateBucket;
|
||||
maxCapacity;
|
||||
timeWindow = 0;
|
||||
constructor(options) {
|
||||
this.beta = options?.beta ?? 0.7;
|
||||
this.minCapacity = options?.minCapacity ?? 1;
|
||||
this.minFillRate = options?.minFillRate ?? 0.5;
|
||||
this.scaleConstant = options?.scaleConstant ?? 0.4;
|
||||
this.smooth = options?.smooth ?? 0.8;
|
||||
this.lastThrottleTime = this.getCurrentTimeInSeconds();
|
||||
this.lastTxRateBucket = Math.floor(this.getCurrentTimeInSeconds());
|
||||
this.fillRate = this.minFillRate;
|
||||
this.maxCapacity = this.minCapacity;
|
||||
}
|
||||
async getSendToken() {
|
||||
return this.acquireTokenBucket(1);
|
||||
}
|
||||
updateClientSendingRate(response) {
|
||||
let calculatedRate;
|
||||
this.updateMeasuredRate();
|
||||
const retryErrorInfo = response;
|
||||
const isThrottling = retryErrorInfo?.errorType === "THROTTLING" || isThrottlingError(retryErrorInfo?.error ?? response);
|
||||
if (isThrottling) {
|
||||
const rateToUse = !this.enabled ? this.measuredTxRate : Math.min(this.measuredTxRate, this.fillRate);
|
||||
this.lastMaxRate = rateToUse;
|
||||
this.calculateTimeWindow();
|
||||
this.lastThrottleTime = this.getCurrentTimeInSeconds();
|
||||
calculatedRate = this.cubicThrottle(rateToUse);
|
||||
this.enableTokenBucket();
|
||||
}
|
||||
else {
|
||||
this.calculateTimeWindow();
|
||||
calculatedRate = this.cubicSuccess(this.getCurrentTimeInSeconds());
|
||||
}
|
||||
const newRate = Math.min(calculatedRate, 2 * this.measuredTxRate);
|
||||
this.updateTokenBucketRate(newRate);
|
||||
}
|
||||
getCurrentTimeInSeconds() {
|
||||
return Date.now() / 1000;
|
||||
}
|
||||
async acquireTokenBucket(amount) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
this.refillTokenBucket();
|
||||
while (amount > this.availableTokens) {
|
||||
const delay = ((amount - this.availableTokens) / this.fillRate) * 1000;
|
||||
await new Promise((resolve) => DefaultRateLimiter.setTimeoutFn(resolve, delay));
|
||||
this.refillTokenBucket();
|
||||
}
|
||||
this.availableTokens = this.availableTokens - amount;
|
||||
}
|
||||
refillTokenBucket() {
|
||||
const timestamp = this.getCurrentTimeInSeconds();
|
||||
if (!this.lastTimestamp) {
|
||||
this.lastTimestamp = timestamp;
|
||||
return;
|
||||
}
|
||||
const fillAmount = (timestamp - this.lastTimestamp) * this.fillRate;
|
||||
this.availableTokens = Math.min(this.maxCapacity, this.availableTokens + fillAmount);
|
||||
this.lastTimestamp = timestamp;
|
||||
}
|
||||
calculateTimeWindow() {
|
||||
this.timeWindow = this.getPrecise(Math.pow((this.lastMaxRate * (1 - this.beta)) / this.scaleConstant, 1 / 3));
|
||||
}
|
||||
cubicThrottle(rateToUse) {
|
||||
return this.getPrecise(rateToUse * this.beta);
|
||||
}
|
||||
cubicSuccess(timestamp) {
|
||||
return this.getPrecise(this.scaleConstant * Math.pow(timestamp - this.lastThrottleTime - this.timeWindow, 3) + this.lastMaxRate);
|
||||
}
|
||||
enableTokenBucket() {
|
||||
this.enabled = true;
|
||||
}
|
||||
updateTokenBucketRate(newRate) {
|
||||
this.refillTokenBucket();
|
||||
this.fillRate = Math.max(newRate, this.minFillRate);
|
||||
this.maxCapacity = Math.max(newRate, this.minCapacity);
|
||||
this.availableTokens = Math.min(this.availableTokens, this.maxCapacity);
|
||||
}
|
||||
updateMeasuredRate() {
|
||||
const t = this.getCurrentTimeInSeconds();
|
||||
const timeBucket = Math.floor(t * 2) / 2;
|
||||
this.requestCount++;
|
||||
if (timeBucket > this.lastTxRateBucket) {
|
||||
const currentRate = this.requestCount / (timeBucket - this.lastTxRateBucket);
|
||||
this.measuredTxRate = this.getPrecise(currentRate * this.smooth + this.measuredTxRate * (1 - this.smooth));
|
||||
this.requestCount = 0;
|
||||
this.lastTxRateBucket = timeBucket;
|
||||
}
|
||||
}
|
||||
getPrecise(num) {
|
||||
return parseFloat(num.toFixed(8));
|
||||
}
|
||||
}
|
||||
|
||||
class Retry {
|
||||
static v2026 = typeof process !== "undefined" && process.env?.SMITHY_NEW_RETRIES_2026 === "true";
|
||||
static delay() {
|
||||
return Retry.v2026 ? 50 : 100;
|
||||
}
|
||||
static throttlingDelay() {
|
||||
return Retry.v2026 ? 1_000 : 500;
|
||||
}
|
||||
static cost() {
|
||||
return Retry.v2026 ? 14 : 5;
|
||||
}
|
||||
static throttlingCost() {
|
||||
return Retry.v2026 ? 5 : 10;
|
||||
}
|
||||
static modifiedCostType() {
|
||||
return Retry.v2026 ? "THROTTLING" : "TRANSIENT";
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultRetryBackoffStrategy {
|
||||
x = Retry.delay();
|
||||
computeNextBackoffDelay(i) {
|
||||
const b = Math.random();
|
||||
const r = 2;
|
||||
const t_i = b * Math.min(this.x * r ** i, MAXIMUM_RETRY_DELAY);
|
||||
return Math.floor(t_i);
|
||||
}
|
||||
setDelayBase(delay) {
|
||||
this.x = delay;
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultRetryToken {
|
||||
delay;
|
||||
count;
|
||||
cost;
|
||||
longPoll;
|
||||
$retryLog = {
|
||||
acquisitionDelay: 0,
|
||||
};
|
||||
constructor(delay, count, cost, longPoll) {
|
||||
this.delay = delay;
|
||||
this.count = count;
|
||||
this.cost = cost;
|
||||
this.longPoll = longPoll;
|
||||
}
|
||||
getRetryCount() {
|
||||
return this.count;
|
||||
}
|
||||
getRetryDelay() {
|
||||
return Math.min(MAXIMUM_RETRY_DELAY, this.delay);
|
||||
}
|
||||
getRetryCost() {
|
||||
return this.cost;
|
||||
}
|
||||
isLongPoll() {
|
||||
return this.longPoll;
|
||||
}
|
||||
}
|
||||
|
||||
exports.RETRY_MODES = void 0;
|
||||
(function (RETRY_MODES) {
|
||||
RETRY_MODES["STANDARD"] = "standard";
|
||||
RETRY_MODES["ADAPTIVE"] = "adaptive";
|
||||
})(exports.RETRY_MODES || (exports.RETRY_MODES = {}));
|
||||
const DEFAULT_MAX_ATTEMPTS = 3;
|
||||
const DEFAULT_RETRY_MODE = exports.RETRY_MODES.STANDARD;
|
||||
|
||||
const refusal = {
|
||||
incompatible: 1,
|
||||
attempts: 2,
|
||||
capacity: 3,
|
||||
};
|
||||
let StandardRetryStrategy$1 = class StandardRetryStrategy {
|
||||
mode = exports.RETRY_MODES.STANDARD;
|
||||
retryBackoffStrategy;
|
||||
capacity = INITIAL_RETRY_TOKENS;
|
||||
maxAttemptsProvider;
|
||||
baseDelay;
|
||||
constructor(arg1) {
|
||||
if (typeof arg1 === "number") {
|
||||
this.maxAttemptsProvider = async () => arg1;
|
||||
}
|
||||
else if (typeof arg1 === "function") {
|
||||
this.maxAttemptsProvider = arg1;
|
||||
}
|
||||
else if (arg1 && typeof arg1 === "object") {
|
||||
this.maxAttemptsProvider = async () => arg1.maxAttempts;
|
||||
this.baseDelay = arg1.baseDelay;
|
||||
this.retryBackoffStrategy = arg1.backoff;
|
||||
}
|
||||
this.maxAttemptsProvider ??= async () => DEFAULT_MAX_ATTEMPTS;
|
||||
this.baseDelay ??= Retry.delay();
|
||||
this.retryBackoffStrategy ??= new DefaultRetryBackoffStrategy();
|
||||
}
|
||||
async acquireInitialRetryToken(retryTokenScope) {
|
||||
return new DefaultRetryToken(Retry.delay(), 0, undefined, Retry.v2026 && retryTokenScope.includes(":longpoll"));
|
||||
}
|
||||
async refreshRetryTokenForRetry(token, errorInfo) {
|
||||
const maxAttempts = await this.getMaxAttempts();
|
||||
const retryCode = this.retryCode(token, errorInfo, maxAttempts);
|
||||
const shouldRetry = retryCode === 0;
|
||||
const isLongPoll = token.isLongPoll?.();
|
||||
if (shouldRetry || isLongPoll) {
|
||||
const errorType = errorInfo.errorType;
|
||||
this.retryBackoffStrategy.setDelayBase(errorType === "THROTTLING" ? Retry.throttlingDelay() : this.baseDelay);
|
||||
const delayFromErrorType = this.retryBackoffStrategy.computeNextBackoffDelay(token.getRetryCount());
|
||||
let retryDelay = delayFromErrorType;
|
||||
if (errorInfo.retryAfterHint instanceof Date) {
|
||||
retryDelay = Math.max(delayFromErrorType, Math.min(errorInfo.retryAfterHint.getTime() - Date.now(), delayFromErrorType + 5_000));
|
||||
}
|
||||
if (!shouldRetry) {
|
||||
const longPollBackoff = Retry.v2026 && retryCode === refusal.capacity && isLongPoll ? retryDelay : 0;
|
||||
if (longPollBackoff > 0) {
|
||||
await new Promise((r) => setTimeout(r, longPollBackoff));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const capacityCost = this.getCapacityCost(errorType);
|
||||
this.capacity -= capacityCost;
|
||||
const nextToken = new DefaultRetryToken(0, token.getRetryCount() + 1, capacityCost, token.isLongPoll?.() ?? false);
|
||||
await new Promise((r) => setTimeout(r, retryDelay));
|
||||
nextToken.$retryLog.acquisitionDelay = retryDelay;
|
||||
return nextToken;
|
||||
}
|
||||
}
|
||||
throw new Error("No retry token available");
|
||||
}
|
||||
recordSuccess(token) {
|
||||
this.capacity = Math.min(INITIAL_RETRY_TOKENS, this.capacity + (token.getRetryCost() ?? NO_RETRY_INCREMENT));
|
||||
}
|
||||
getCapacity() {
|
||||
return this.capacity;
|
||||
}
|
||||
async maxAttempts() {
|
||||
return this.maxAttemptsProvider();
|
||||
}
|
||||
async getMaxAttempts() {
|
||||
try {
|
||||
return await this.maxAttemptsProvider();
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(`Max attempts provider could not resolve. Using default of ${DEFAULT_MAX_ATTEMPTS}`);
|
||||
return DEFAULT_MAX_ATTEMPTS;
|
||||
}
|
||||
}
|
||||
retryCode(tokenToRenew, errorInfo, maxAttempts) {
|
||||
const attempts = tokenToRenew.getRetryCount() + 1;
|
||||
const retryableStatus = this.isRetryableError(errorInfo.errorType) ? 0 : refusal.incompatible;
|
||||
const attemptStatus = attempts < maxAttempts ? 0 : refusal.attempts;
|
||||
const capacityStatus = this.capacity >= this.getCapacityCost(errorInfo.errorType) ? 0 : refusal.capacity;
|
||||
return retryableStatus || attemptStatus || capacityStatus;
|
||||
}
|
||||
getCapacityCost(errorType) {
|
||||
return errorType === Retry.modifiedCostType() ? Retry.throttlingCost() : Retry.cost();
|
||||
}
|
||||
isRetryableError(errorType) {
|
||||
return errorType === "THROTTLING" || errorType === "TRANSIENT";
|
||||
}
|
||||
};
|
||||
|
||||
let AdaptiveRetryStrategy$1 = class AdaptiveRetryStrategy {
|
||||
mode = exports.RETRY_MODES.ADAPTIVE;
|
||||
rateLimiter;
|
||||
standardRetryStrategy;
|
||||
constructor(maxAttemptsProvider, options) {
|
||||
const { rateLimiter } = options ?? {};
|
||||
this.rateLimiter = rateLimiter ?? new DefaultRateLimiter();
|
||||
this.standardRetryStrategy = options
|
||||
? new StandardRetryStrategy$1({
|
||||
maxAttempts: typeof maxAttemptsProvider === "number" ? maxAttemptsProvider : 3,
|
||||
...options,
|
||||
})
|
||||
: new StandardRetryStrategy$1(maxAttemptsProvider);
|
||||
}
|
||||
async acquireInitialRetryToken(retryTokenScope) {
|
||||
const token = await this.standardRetryStrategy.acquireInitialRetryToken(retryTokenScope);
|
||||
await this.rateLimiter.getSendToken();
|
||||
return token;
|
||||
}
|
||||
async refreshRetryTokenForRetry(tokenToRenew, errorInfo) {
|
||||
this.rateLimiter.updateClientSendingRate(errorInfo);
|
||||
const token = await this.standardRetryStrategy.refreshRetryTokenForRetry(tokenToRenew, errorInfo);
|
||||
await this.rateLimiter.getSendToken();
|
||||
return token;
|
||||
}
|
||||
recordSuccess(token) {
|
||||
this.rateLimiter.updateClientSendingRate({});
|
||||
this.standardRetryStrategy.recordSuccess(token);
|
||||
}
|
||||
async maxAttemptsProvider() {
|
||||
return this.standardRetryStrategy.maxAttempts();
|
||||
}
|
||||
};
|
||||
|
||||
class ConfiguredRetryStrategy extends StandardRetryStrategy$1 {
|
||||
computeNextBackoffDelay;
|
||||
constructor(maxAttempts, computeNextBackoffDelay = Retry.delay()) {
|
||||
super(typeof maxAttempts === "function" ? maxAttempts : async () => maxAttempts);
|
||||
if (typeof computeNextBackoffDelay === "number") {
|
||||
this.computeNextBackoffDelay = () => computeNextBackoffDelay;
|
||||
}
|
||||
else {
|
||||
this.computeNextBackoffDelay = computeNextBackoffDelay;
|
||||
}
|
||||
this.retryBackoffStrategy.computeNextBackoffDelay = (completedAttempt) => {
|
||||
const nextAttempt = completedAttempt + 1;
|
||||
return this.computeNextBackoffDelay(nextAttempt);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const getDefaultRetryQuota = (initialRetryTokens, options) => {
|
||||
const MAX_CAPACITY = initialRetryTokens;
|
||||
const noRetryIncrement = NO_RETRY_INCREMENT;
|
||||
const retryCost = RETRY_COST;
|
||||
const timeoutRetryCost = TIMEOUT_RETRY_COST;
|
||||
let availableCapacity = initialRetryTokens;
|
||||
const getCapacityAmount = (error) => (error.name === "TimeoutError" ? timeoutRetryCost : retryCost);
|
||||
const hasRetryTokens = (error) => getCapacityAmount(error) <= availableCapacity;
|
||||
const retrieveRetryTokens = (error) => {
|
||||
if (!hasRetryTokens(error)) {
|
||||
throw new Error("No retry token available");
|
||||
}
|
||||
const capacityAmount = getCapacityAmount(error);
|
||||
availableCapacity -= capacityAmount;
|
||||
return capacityAmount;
|
||||
};
|
||||
const releaseRetryTokens = (capacityReleaseAmount) => {
|
||||
availableCapacity += capacityReleaseAmount ?? noRetryIncrement;
|
||||
availableCapacity = Math.min(availableCapacity, MAX_CAPACITY);
|
||||
};
|
||||
return Object.freeze({
|
||||
hasRetryTokens,
|
||||
retrieveRetryTokens,
|
||||
releaseRetryTokens,
|
||||
});
|
||||
};
|
||||
|
||||
const defaultDelayDecider = (delayBase, attempts) => Math.floor(Math.min(MAXIMUM_RETRY_DELAY, Math.random() * 2 ** attempts * delayBase));
|
||||
|
||||
const defaultRetryDecider = (error) => {
|
||||
if (!error) {
|
||||
return false;
|
||||
}
|
||||
return isRetryableByTrait(error) || isClockSkewError(error) || isThrottlingError(error) || isTransientError(error);
|
||||
};
|
||||
|
||||
class StandardRetryStrategy {
|
||||
maxAttemptsProvider;
|
||||
retryDecider;
|
||||
delayDecider;
|
||||
retryQuota;
|
||||
mode = exports.RETRY_MODES.STANDARD;
|
||||
constructor(maxAttemptsProvider, options) {
|
||||
this.maxAttemptsProvider = maxAttemptsProvider;
|
||||
this.retryDecider = options?.retryDecider ?? defaultRetryDecider;
|
||||
this.delayDecider = options?.delayDecider ?? defaultDelayDecider;
|
||||
this.retryQuota = options?.retryQuota ?? getDefaultRetryQuota(INITIAL_RETRY_TOKENS);
|
||||
}
|
||||
shouldRetry(error, attempts, maxAttempts) {
|
||||
return attempts < maxAttempts && this.retryDecider(error) && this.retryQuota.hasRetryTokens(error);
|
||||
}
|
||||
async getMaxAttempts() {
|
||||
let maxAttempts;
|
||||
try {
|
||||
maxAttempts = await this.maxAttemptsProvider();
|
||||
}
|
||||
catch (error) {
|
||||
maxAttempts = DEFAULT_MAX_ATTEMPTS;
|
||||
}
|
||||
return maxAttempts;
|
||||
}
|
||||
async retry(next, args, options) {
|
||||
let retryTokenAmount;
|
||||
let attempts = 0;
|
||||
let totalDelay = 0;
|
||||
const maxAttempts = await this.getMaxAttempts();
|
||||
const { request } = args;
|
||||
if (protocols.HttpRequest.isInstance(request)) {
|
||||
request.headers[INVOCATION_ID_HEADER] = serde.v4();
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
if (protocols.HttpRequest.isInstance(request)) {
|
||||
request.headers[REQUEST_HEADER] = `attempt=${attempts + 1}; max=${maxAttempts}`;
|
||||
}
|
||||
if (options?.beforeRequest) {
|
||||
await options.beforeRequest();
|
||||
}
|
||||
const { response, output } = await next(args);
|
||||
if (options?.afterRequest) {
|
||||
options.afterRequest(response);
|
||||
}
|
||||
this.retryQuota.releaseRetryTokens(retryTokenAmount);
|
||||
output.$metadata.attempts = attempts + 1;
|
||||
output.$metadata.totalRetryDelay = totalDelay;
|
||||
return { response, output };
|
||||
}
|
||||
catch (e) {
|
||||
const err = asSdkError(e);
|
||||
attempts++;
|
||||
if (this.shouldRetry(err, attempts, maxAttempts)) {
|
||||
retryTokenAmount = this.retryQuota.retrieveRetryTokens(err);
|
||||
const delayFromDecider = this.delayDecider(isThrottlingError(err) ? THROTTLING_RETRY_DELAY_BASE : DEFAULT_RETRY_DELAY_BASE, attempts);
|
||||
const delayFromResponse = getDelayFromRetryAfterHeader(err.$response);
|
||||
const delay = Math.max(delayFromResponse || 0, delayFromDecider);
|
||||
totalDelay += delay;
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
continue;
|
||||
}
|
||||
if (!err.$metadata) {
|
||||
err.$metadata = {};
|
||||
}
|
||||
err.$metadata.attempts = attempts;
|
||||
err.$metadata.totalRetryDelay = totalDelay;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const getDelayFromRetryAfterHeader = (response) => {
|
||||
if (!protocols.HttpResponse.isInstance(response))
|
||||
return;
|
||||
const retryAfterHeaderName = Object.keys(response.headers).find((key) => key.toLowerCase() === "retry-after");
|
||||
if (!retryAfterHeaderName)
|
||||
return;
|
||||
const retryAfter = response.headers[retryAfterHeaderName];
|
||||
const retryAfterSeconds = Number(retryAfter);
|
||||
if (!Number.isNaN(retryAfterSeconds))
|
||||
return retryAfterSeconds * 1000;
|
||||
const retryAfterDate = new Date(retryAfter);
|
||||
return retryAfterDate.getTime() - Date.now();
|
||||
};
|
||||
|
||||
class AdaptiveRetryStrategy extends StandardRetryStrategy {
|
||||
rateLimiter;
|
||||
constructor(maxAttemptsProvider, options) {
|
||||
const { rateLimiter, ...superOptions } = options ?? {};
|
||||
super(maxAttemptsProvider, superOptions);
|
||||
this.rateLimiter = rateLimiter ?? new DefaultRateLimiter();
|
||||
this.mode = exports.RETRY_MODES.ADAPTIVE;
|
||||
}
|
||||
async retry(next, args) {
|
||||
return super.retry(next, args, {
|
||||
beforeRequest: async () => {
|
||||
return this.rateLimiter.getSendToken();
|
||||
},
|
||||
afterRequest: (response) => {
|
||||
this.rateLimiter.updateClientSendingRate(response);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const resolveRetryConfig = (input, defaults) => {
|
||||
const { retryStrategy, retryMode } = input;
|
||||
const { defaultMaxAttempts = DEFAULT_MAX_ATTEMPTS, defaultBaseDelay = Retry.delay() } = defaults ?? {};
|
||||
const maxAttemptsProvider = client.normalizeProvider(input.maxAttempts ?? defaultMaxAttempts);
|
||||
let controller = retryStrategy
|
||||
? Promise.resolve(retryStrategy)
|
||||
: undefined;
|
||||
const getDefault = async () => {
|
||||
const maxAttempts = await maxAttemptsProvider();
|
||||
const adaptive = (await client.normalizeProvider(retryMode)()) === exports.RETRY_MODES.ADAPTIVE;
|
||||
if (adaptive) {
|
||||
return new AdaptiveRetryStrategy$1(maxAttemptsProvider, {
|
||||
maxAttempts,
|
||||
baseDelay: defaultBaseDelay,
|
||||
});
|
||||
}
|
||||
return new StandardRetryStrategy$1({
|
||||
maxAttempts,
|
||||
baseDelay: defaultBaseDelay,
|
||||
});
|
||||
};
|
||||
return Object.assign(input, {
|
||||
maxAttempts: maxAttemptsProvider,
|
||||
retryStrategy: () => (controller ??= getDefault()),
|
||||
});
|
||||
};
|
||||
|
||||
const omitRetryHeadersMiddleware = () => (next) => async (args) => {
|
||||
const { request } = args;
|
||||
if (protocols.HttpRequest.isInstance(request)) {
|
||||
delete request.headers[INVOCATION_ID_HEADER];
|
||||
delete request.headers[REQUEST_HEADER];
|
||||
}
|
||||
return next(args);
|
||||
};
|
||||
const omitRetryHeadersMiddlewareOptions = {
|
||||
name: "omitRetryHeadersMiddleware",
|
||||
tags: ["RETRY", "HEADERS", "OMIT_RETRY_HEADERS"],
|
||||
relation: "before",
|
||||
toMiddleware: "awsAuthMiddleware",
|
||||
override: true,
|
||||
};
|
||||
const getOmitRetryHeadersPlugin = (options) => ({
|
||||
applyToStack: (clientStack) => {
|
||||
clientStack.addRelativeTo(omitRetryHeadersMiddleware(), omitRetryHeadersMiddlewareOptions);
|
||||
},
|
||||
});
|
||||
|
||||
const no = Symbol.for("node-only");
|
||||
const ENV_MAX_ATTEMPTS = no;
|
||||
const CONFIG_MAX_ATTEMPTS = no;
|
||||
const NODE_MAX_ATTEMPT_CONFIG_OPTIONS = no;
|
||||
const ENV_RETRY_MODE = no;
|
||||
const CONFIG_RETRY_MODE = no;
|
||||
const NODE_RETRY_MODE_CONFIG_OPTIONS = no;
|
||||
const retryMiddleware = bindRetryMiddleware(isStreamingPayload);
|
||||
const getRetryPlugin = bindGetRetryPlugin(isStreamingPayload);
|
||||
|
||||
exports.AdaptiveRetryStrategy = AdaptiveRetryStrategy$1;
|
||||
exports.CONFIG_MAX_ATTEMPTS = CONFIG_MAX_ATTEMPTS;
|
||||
exports.CONFIG_RETRY_MODE = CONFIG_RETRY_MODE;
|
||||
exports.ConfiguredRetryStrategy = ConfiguredRetryStrategy;
|
||||
exports.DEFAULT_MAX_ATTEMPTS = DEFAULT_MAX_ATTEMPTS;
|
||||
exports.DEFAULT_RETRY_DELAY_BASE = DEFAULT_RETRY_DELAY_BASE;
|
||||
exports.DEFAULT_RETRY_MODE = DEFAULT_RETRY_MODE;
|
||||
exports.DefaultRateLimiter = DefaultRateLimiter;
|
||||
exports.DeprecatedAdaptiveRetryStrategy = AdaptiveRetryStrategy;
|
||||
exports.DeprecatedStandardRetryStrategy = StandardRetryStrategy;
|
||||
exports.ENV_MAX_ATTEMPTS = ENV_MAX_ATTEMPTS;
|
||||
exports.ENV_RETRY_MODE = ENV_RETRY_MODE;
|
||||
exports.INITIAL_RETRY_TOKENS = INITIAL_RETRY_TOKENS;
|
||||
exports.INVOCATION_ID_HEADER = INVOCATION_ID_HEADER;
|
||||
exports.MAXIMUM_RETRY_DELAY = MAXIMUM_RETRY_DELAY;
|
||||
exports.NODE_MAX_ATTEMPT_CONFIG_OPTIONS = NODE_MAX_ATTEMPT_CONFIG_OPTIONS;
|
||||
exports.NODE_RETRY_MODE_CONFIG_OPTIONS = NODE_RETRY_MODE_CONFIG_OPTIONS;
|
||||
exports.NO_RETRY_INCREMENT = NO_RETRY_INCREMENT;
|
||||
exports.REQUEST_HEADER = REQUEST_HEADER;
|
||||
exports.RETRY_COST = RETRY_COST;
|
||||
exports.Retry = Retry;
|
||||
exports.StandardRetryStrategy = StandardRetryStrategy$1;
|
||||
exports.THROTTLING_RETRY_DELAY_BASE = THROTTLING_RETRY_DELAY_BASE;
|
||||
exports.TIMEOUT_RETRY_COST = TIMEOUT_RETRY_COST;
|
||||
exports.defaultDelayDecider = defaultDelayDecider;
|
||||
exports.defaultRetryDecider = defaultRetryDecider;
|
||||
exports.getOmitRetryHeadersPlugin = getOmitRetryHeadersPlugin;
|
||||
exports.getRetryAfterHint = getRetryAfterHint;
|
||||
exports.getRetryPlugin = getRetryPlugin;
|
||||
exports.isBrowserNetworkError = isBrowserNetworkError;
|
||||
exports.isClockSkewCorrectedError = isClockSkewCorrectedError;
|
||||
exports.isClockSkewError = isClockSkewError;
|
||||
exports.isNodeJsHttp2TransientError = isNodeJsHttp2TransientError;
|
||||
exports.isRetryableByTrait = isRetryableByTrait;
|
||||
exports.isServerError = isServerError;
|
||||
exports.isThrottlingError = isThrottlingError;
|
||||
exports.isTransientError = isTransientError;
|
||||
exports.omitRetryHeadersMiddleware = omitRetryHeadersMiddleware;
|
||||
exports.omitRetryHeadersMiddlewareOptions = omitRetryHeadersMiddlewareOptions;
|
||||
exports.resolveRetryConfig = resolveRetryConfig;
|
||||
exports.retryMiddleware = retryMiddleware;
|
||||
exports.retryMiddlewareOptions = retryMiddlewareOptions;
|
||||
+844
@@ -0,0 +1,844 @@
|
||||
'use strict';
|
||||
|
||||
var node_stream = require('node:stream');
|
||||
var client = require('@smithy/core/client');
|
||||
var protocols = require('@smithy/core/protocols');
|
||||
var serde = require('@smithy/core/serde');
|
||||
|
||||
const isStreamingPayload = (request) => request?.body instanceof node_stream.Readable ||
|
||||
(typeof ReadableStream !== "undefined" && request?.body instanceof ReadableStream);
|
||||
|
||||
const CLOCK_SKEW_ERROR_CODES = [
|
||||
"AuthFailure",
|
||||
"InvalidSignatureException",
|
||||
"RequestExpired",
|
||||
"RequestInTheFuture",
|
||||
"RequestTimeTooSkewed",
|
||||
"SignatureDoesNotMatch",
|
||||
];
|
||||
const THROTTLING_ERROR_CODES = [
|
||||
"BandwidthLimitExceeded",
|
||||
"EC2ThrottledException",
|
||||
"LimitExceededException",
|
||||
"PriorRequestNotComplete",
|
||||
"ProvisionedThroughputExceededException",
|
||||
"RequestLimitExceeded",
|
||||
"RequestThrottled",
|
||||
"RequestThrottledException",
|
||||
"SlowDown",
|
||||
"ThrottledException",
|
||||
"Throttling",
|
||||
"ThrottlingException",
|
||||
"TooManyRequestsException",
|
||||
"TransactionInProgressException",
|
||||
];
|
||||
const TRANSIENT_ERROR_CODES = ["TimeoutError", "RequestTimeout", "RequestTimeoutException"];
|
||||
const TRANSIENT_ERROR_STATUS_CODES = [500, 502, 503, 504];
|
||||
const NODEJS_TIMEOUT_ERROR_CODES = ["ECONNRESET", "ECONNREFUSED", "EPIPE", "ETIMEDOUT"];
|
||||
const NODEJS_NETWORK_ERROR_CODES = ["EHOSTUNREACH", "ENETUNREACH", "ENOTFOUND", "EAI_AGAIN"];
|
||||
|
||||
const isRetryableByTrait = (error) => error?.$retryable !== undefined;
|
||||
const isClockSkewError = (error) => CLOCK_SKEW_ERROR_CODES.includes(error.name);
|
||||
const isClockSkewCorrectedError = (error) => error.$metadata?.clockSkewCorrected;
|
||||
const isBrowserNetworkError = (error) => {
|
||||
const errorMessages = new Set([
|
||||
"Failed to fetch",
|
||||
"NetworkError when attempting to fetch resource",
|
||||
"The Internet connection appears to be offline",
|
||||
"Load failed",
|
||||
"Network request failed",
|
||||
]);
|
||||
const isValid = error && error instanceof TypeError;
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
return errorMessages.has(error.message);
|
||||
};
|
||||
const isThrottlingError = (error) => error.$metadata?.httpStatusCode === 429 ||
|
||||
THROTTLING_ERROR_CODES.includes(error.name) ||
|
||||
error.$retryable?.throttling == true;
|
||||
const isTransientError = (error, depth = 0) => isRetryableByTrait(error) ||
|
||||
isClockSkewCorrectedError(error) ||
|
||||
(error.name === "InvalidSignatureException" && error.message?.includes("Signature expired")) ||
|
||||
TRANSIENT_ERROR_CODES.includes(error.name) ||
|
||||
NODEJS_TIMEOUT_ERROR_CODES.includes(error?.code || "") ||
|
||||
NODEJS_NETWORK_ERROR_CODES.includes(error?.code || "") ||
|
||||
TRANSIENT_ERROR_STATUS_CODES.includes(error.$metadata?.httpStatusCode || 0) ||
|
||||
isBrowserNetworkError(error) ||
|
||||
isNodeJsHttp2TransientError(error) ||
|
||||
(error.cause !== undefined && depth <= 10 && isTransientError(error.cause, depth + 1));
|
||||
const isServerError = (error) => {
|
||||
if (error.$metadata?.httpStatusCode !== undefined) {
|
||||
const statusCode = error.$metadata.httpStatusCode;
|
||||
if (500 <= statusCode && statusCode <= 599 && !isTransientError(error)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
function isNodeJsHttp2TransientError(error) {
|
||||
return error.code === "ERR_HTTP2_STREAM_ERROR" && error.message.includes("NGHTTP2_REFUSED_STREAM");
|
||||
}
|
||||
|
||||
const DEFAULT_RETRY_DELAY_BASE = 100;
|
||||
const MAXIMUM_RETRY_DELAY = 20 * 1000;
|
||||
const THROTTLING_RETRY_DELAY_BASE = 500;
|
||||
const INITIAL_RETRY_TOKENS = 500;
|
||||
const RETRY_COST = 5;
|
||||
const TIMEOUT_RETRY_COST = 10;
|
||||
const NO_RETRY_INCREMENT = 1;
|
||||
const INVOCATION_ID_HEADER = "amz-sdk-invocation-id";
|
||||
const REQUEST_HEADER = "amz-sdk-request";
|
||||
|
||||
function parseRetryAfterHeader(response, logger) {
|
||||
if (!protocols.HttpResponse.isInstance(response)) {
|
||||
return;
|
||||
}
|
||||
for (const header of Object.keys(response.headers)) {
|
||||
const h = header.toLowerCase();
|
||||
if (h === "retry-after") {
|
||||
const retryAfter = response.headers[header];
|
||||
let retryAfterSeconds = NaN;
|
||||
if (retryAfter.endsWith("GMT")) {
|
||||
try {
|
||||
const date = serde.parseRfc7231DateTime(retryAfter);
|
||||
retryAfterSeconds = (date.getTime() - Date.now()) / 1000;
|
||||
}
|
||||
catch (e) {
|
||||
logger?.trace?.("Failed to parse retry-after header");
|
||||
logger?.trace?.(e);
|
||||
}
|
||||
}
|
||||
else if (retryAfter.match(/ GMT, ((\d+)|(\d+\.\d+))$/)) {
|
||||
retryAfterSeconds = Number(retryAfter.match(/ GMT, ([\d.]+)$/)?.[1]);
|
||||
}
|
||||
else if (retryAfter.match(/^((\d+)|(\d+\.\d+))$/)) {
|
||||
retryAfterSeconds = Number(retryAfter);
|
||||
}
|
||||
else if (Date.parse(retryAfter) >= Date.now()) {
|
||||
retryAfterSeconds = (Date.parse(retryAfter) - Date.now()) / 1000;
|
||||
}
|
||||
if (isNaN(retryAfterSeconds)) {
|
||||
return;
|
||||
}
|
||||
return new Date(Date.now() + retryAfterSeconds * 1000);
|
||||
}
|
||||
else if (h === "x-amz-retry-after") {
|
||||
const v = response.headers[header];
|
||||
const backoffMilliseconds = Number(v);
|
||||
if (isNaN(backoffMilliseconds)) {
|
||||
logger?.trace?.(`Failed to parse x-amz-retry-after=${v}`);
|
||||
return;
|
||||
}
|
||||
return new Date(Date.now() + backoffMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
function getRetryAfterHint(response, logger) {
|
||||
return parseRetryAfterHeader(response, logger);
|
||||
}
|
||||
|
||||
const asSdkError = (error) => {
|
||||
if (error instanceof Error)
|
||||
return error;
|
||||
if (error instanceof Object)
|
||||
return Object.assign(new Error(), error);
|
||||
if (typeof error === "string")
|
||||
return new Error(error);
|
||||
return new Error(`AWS SDK error wrapper for ${error}`);
|
||||
};
|
||||
|
||||
function bindRetryMiddleware(isStreamingPayload) {
|
||||
return (options) => (next, context) => async (args) => {
|
||||
let retryStrategy = await options.retryStrategy();
|
||||
const maxAttempts = await options.maxAttempts();
|
||||
if (isRetryStrategyV2(retryStrategy)) {
|
||||
retryStrategy = retryStrategy;
|
||||
let retryToken = await retryStrategy.acquireInitialRetryToken((context["partition_id"] ?? "") + (context.__retryLongPoll ? ":longpoll" : ""));
|
||||
let lastError = new Error();
|
||||
let attempts = 0;
|
||||
let totalRetryDelay = 0;
|
||||
const { request } = args;
|
||||
const isRequest = protocols.HttpRequest.isInstance(request);
|
||||
if (isRequest) {
|
||||
request.headers[INVOCATION_ID_HEADER] = serde.v4();
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
if (isRequest) {
|
||||
request.headers[REQUEST_HEADER] = `attempt=${attempts + 1}; max=${maxAttempts}`;
|
||||
}
|
||||
const { response, output } = await next(args);
|
||||
retryStrategy.recordSuccess(retryToken);
|
||||
output.$metadata.attempts = attempts + 1;
|
||||
output.$metadata.totalRetryDelay = totalRetryDelay;
|
||||
return { response, output };
|
||||
}
|
||||
catch (e) {
|
||||
const retryErrorInfo = getRetryErrorInfo(e, options.logger);
|
||||
lastError = asSdkError(e);
|
||||
if (isRequest && isStreamingPayload(request)) {
|
||||
(context.logger instanceof client.NoOpLogger ? console : context.logger)?.warn("An error was encountered in a non-retryable streaming request.");
|
||||
throw lastError;
|
||||
}
|
||||
try {
|
||||
retryToken = await retryStrategy.refreshRetryTokenForRetry(retryToken, retryErrorInfo);
|
||||
}
|
||||
catch (refreshError) {
|
||||
if (!lastError.$metadata) {
|
||||
lastError.$metadata = {};
|
||||
}
|
||||
lastError.$metadata.attempts = attempts + 1;
|
||||
lastError.$metadata.totalRetryDelay = totalRetryDelay;
|
||||
throw lastError;
|
||||
}
|
||||
attempts = retryToken.getRetryCount();
|
||||
const delay = retryToken.getRetryDelay();
|
||||
totalRetryDelay += (retryToken?.$retryLog?.acquisitionDelay ?? 0) + delay;
|
||||
if (delay > 0) {
|
||||
await cooldown(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
retryStrategy = retryStrategy;
|
||||
if (retryStrategy?.mode) {
|
||||
context.userAgent = [...(context.userAgent || []), ["cfg/retry-mode", retryStrategy.mode]];
|
||||
}
|
||||
return retryStrategy.retry(next, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
const cooldown = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const isRetryStrategyV2 = (retryStrategy) => typeof retryStrategy.acquireInitialRetryToken !== "undefined" &&
|
||||
typeof retryStrategy.refreshRetryTokenForRetry !== "undefined" &&
|
||||
typeof retryStrategy.recordSuccess !== "undefined";
|
||||
const getRetryErrorInfo = (error, logger) => {
|
||||
const errorInfo = {
|
||||
error,
|
||||
errorType: getRetryErrorType(error),
|
||||
};
|
||||
const retryAfterHint = parseRetryAfterHeader(error.$response, logger);
|
||||
if (retryAfterHint) {
|
||||
errorInfo.retryAfterHint = retryAfterHint;
|
||||
}
|
||||
return errorInfo;
|
||||
};
|
||||
const getRetryErrorType = (error) => {
|
||||
if (isThrottlingError(error))
|
||||
return "THROTTLING";
|
||||
if (isTransientError(error))
|
||||
return "TRANSIENT";
|
||||
if (isServerError(error))
|
||||
return "SERVER_ERROR";
|
||||
return "CLIENT_ERROR";
|
||||
};
|
||||
const retryMiddlewareOptions = {
|
||||
name: "retryMiddleware",
|
||||
tags: ["RETRY"],
|
||||
step: "finalizeRequest",
|
||||
priority: "high",
|
||||
override: true,
|
||||
};
|
||||
function bindGetRetryPlugin(isStreamingPayload) {
|
||||
const retryMiddleware = bindRetryMiddleware(isStreamingPayload);
|
||||
return (options) => ({
|
||||
applyToStack: (clientStack) => {
|
||||
clientStack.add(retryMiddleware(options), retryMiddlewareOptions);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
class DefaultRateLimiter {
|
||||
static setTimeoutFn = setTimeout;
|
||||
beta;
|
||||
minCapacity;
|
||||
minFillRate;
|
||||
scaleConstant;
|
||||
smooth;
|
||||
enabled = false;
|
||||
availableTokens = 0;
|
||||
lastMaxRate = 0;
|
||||
measuredTxRate = 0;
|
||||
requestCount = 0;
|
||||
fillRate;
|
||||
lastThrottleTime;
|
||||
lastTimestamp = 0;
|
||||
lastTxRateBucket;
|
||||
maxCapacity;
|
||||
timeWindow = 0;
|
||||
constructor(options) {
|
||||
this.beta = options?.beta ?? 0.7;
|
||||
this.minCapacity = options?.minCapacity ?? 1;
|
||||
this.minFillRate = options?.minFillRate ?? 0.5;
|
||||
this.scaleConstant = options?.scaleConstant ?? 0.4;
|
||||
this.smooth = options?.smooth ?? 0.8;
|
||||
this.lastThrottleTime = this.getCurrentTimeInSeconds();
|
||||
this.lastTxRateBucket = Math.floor(this.getCurrentTimeInSeconds());
|
||||
this.fillRate = this.minFillRate;
|
||||
this.maxCapacity = this.minCapacity;
|
||||
}
|
||||
async getSendToken() {
|
||||
return this.acquireTokenBucket(1);
|
||||
}
|
||||
updateClientSendingRate(response) {
|
||||
let calculatedRate;
|
||||
this.updateMeasuredRate();
|
||||
const retryErrorInfo = response;
|
||||
const isThrottling = retryErrorInfo?.errorType === "THROTTLING" || isThrottlingError(retryErrorInfo?.error ?? response);
|
||||
if (isThrottling) {
|
||||
const rateToUse = !this.enabled ? this.measuredTxRate : Math.min(this.measuredTxRate, this.fillRate);
|
||||
this.lastMaxRate = rateToUse;
|
||||
this.calculateTimeWindow();
|
||||
this.lastThrottleTime = this.getCurrentTimeInSeconds();
|
||||
calculatedRate = this.cubicThrottle(rateToUse);
|
||||
this.enableTokenBucket();
|
||||
}
|
||||
else {
|
||||
this.calculateTimeWindow();
|
||||
calculatedRate = this.cubicSuccess(this.getCurrentTimeInSeconds());
|
||||
}
|
||||
const newRate = Math.min(calculatedRate, 2 * this.measuredTxRate);
|
||||
this.updateTokenBucketRate(newRate);
|
||||
}
|
||||
getCurrentTimeInSeconds() {
|
||||
return Date.now() / 1000;
|
||||
}
|
||||
async acquireTokenBucket(amount) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
this.refillTokenBucket();
|
||||
while (amount > this.availableTokens) {
|
||||
const delay = ((amount - this.availableTokens) / this.fillRate) * 1000;
|
||||
await new Promise((resolve) => DefaultRateLimiter.setTimeoutFn(resolve, delay));
|
||||
this.refillTokenBucket();
|
||||
}
|
||||
this.availableTokens = this.availableTokens - amount;
|
||||
}
|
||||
refillTokenBucket() {
|
||||
const timestamp = this.getCurrentTimeInSeconds();
|
||||
if (!this.lastTimestamp) {
|
||||
this.lastTimestamp = timestamp;
|
||||
return;
|
||||
}
|
||||
const fillAmount = (timestamp - this.lastTimestamp) * this.fillRate;
|
||||
this.availableTokens = Math.min(this.maxCapacity, this.availableTokens + fillAmount);
|
||||
this.lastTimestamp = timestamp;
|
||||
}
|
||||
calculateTimeWindow() {
|
||||
this.timeWindow = this.getPrecise(Math.pow((this.lastMaxRate * (1 - this.beta)) / this.scaleConstant, 1 / 3));
|
||||
}
|
||||
cubicThrottle(rateToUse) {
|
||||
return this.getPrecise(rateToUse * this.beta);
|
||||
}
|
||||
cubicSuccess(timestamp) {
|
||||
return this.getPrecise(this.scaleConstant * Math.pow(timestamp - this.lastThrottleTime - this.timeWindow, 3) + this.lastMaxRate);
|
||||
}
|
||||
enableTokenBucket() {
|
||||
this.enabled = true;
|
||||
}
|
||||
updateTokenBucketRate(newRate) {
|
||||
this.refillTokenBucket();
|
||||
this.fillRate = Math.max(newRate, this.minFillRate);
|
||||
this.maxCapacity = Math.max(newRate, this.minCapacity);
|
||||
this.availableTokens = Math.min(this.availableTokens, this.maxCapacity);
|
||||
}
|
||||
updateMeasuredRate() {
|
||||
const t = this.getCurrentTimeInSeconds();
|
||||
const timeBucket = Math.floor(t * 2) / 2;
|
||||
this.requestCount++;
|
||||
if (timeBucket > this.lastTxRateBucket) {
|
||||
const currentRate = this.requestCount / (timeBucket - this.lastTxRateBucket);
|
||||
this.measuredTxRate = this.getPrecise(currentRate * this.smooth + this.measuredTxRate * (1 - this.smooth));
|
||||
this.requestCount = 0;
|
||||
this.lastTxRateBucket = timeBucket;
|
||||
}
|
||||
}
|
||||
getPrecise(num) {
|
||||
return parseFloat(num.toFixed(8));
|
||||
}
|
||||
}
|
||||
|
||||
class Retry {
|
||||
static v2026 = typeof process !== "undefined" && process.env?.SMITHY_NEW_RETRIES_2026 === "true";
|
||||
static delay() {
|
||||
return Retry.v2026 ? 50 : 100;
|
||||
}
|
||||
static throttlingDelay() {
|
||||
return Retry.v2026 ? 1_000 : 500;
|
||||
}
|
||||
static cost() {
|
||||
return Retry.v2026 ? 14 : 5;
|
||||
}
|
||||
static throttlingCost() {
|
||||
return Retry.v2026 ? 5 : 10;
|
||||
}
|
||||
static modifiedCostType() {
|
||||
return Retry.v2026 ? "THROTTLING" : "TRANSIENT";
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultRetryBackoffStrategy {
|
||||
x = Retry.delay();
|
||||
computeNextBackoffDelay(i) {
|
||||
const b = Math.random();
|
||||
const r = 2;
|
||||
const t_i = b * Math.min(this.x * r ** i, MAXIMUM_RETRY_DELAY);
|
||||
return Math.floor(t_i);
|
||||
}
|
||||
setDelayBase(delay) {
|
||||
this.x = delay;
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultRetryToken {
|
||||
delay;
|
||||
count;
|
||||
cost;
|
||||
longPoll;
|
||||
$retryLog = {
|
||||
acquisitionDelay: 0,
|
||||
};
|
||||
constructor(delay, count, cost, longPoll) {
|
||||
this.delay = delay;
|
||||
this.count = count;
|
||||
this.cost = cost;
|
||||
this.longPoll = longPoll;
|
||||
}
|
||||
getRetryCount() {
|
||||
return this.count;
|
||||
}
|
||||
getRetryDelay() {
|
||||
return Math.min(MAXIMUM_RETRY_DELAY, this.delay);
|
||||
}
|
||||
getRetryCost() {
|
||||
return this.cost;
|
||||
}
|
||||
isLongPoll() {
|
||||
return this.longPoll;
|
||||
}
|
||||
}
|
||||
|
||||
exports.RETRY_MODES = void 0;
|
||||
(function (RETRY_MODES) {
|
||||
RETRY_MODES["STANDARD"] = "standard";
|
||||
RETRY_MODES["ADAPTIVE"] = "adaptive";
|
||||
})(exports.RETRY_MODES || (exports.RETRY_MODES = {}));
|
||||
const DEFAULT_MAX_ATTEMPTS = 3;
|
||||
const DEFAULT_RETRY_MODE = exports.RETRY_MODES.STANDARD;
|
||||
|
||||
const refusal = {
|
||||
incompatible: 1,
|
||||
attempts: 2,
|
||||
capacity: 3,
|
||||
};
|
||||
let StandardRetryStrategy$1 = class StandardRetryStrategy {
|
||||
mode = exports.RETRY_MODES.STANDARD;
|
||||
retryBackoffStrategy;
|
||||
capacity = INITIAL_RETRY_TOKENS;
|
||||
maxAttemptsProvider;
|
||||
baseDelay;
|
||||
constructor(arg1) {
|
||||
if (typeof arg1 === "number") {
|
||||
this.maxAttemptsProvider = async () => arg1;
|
||||
}
|
||||
else if (typeof arg1 === "function") {
|
||||
this.maxAttemptsProvider = arg1;
|
||||
}
|
||||
else if (arg1 && typeof arg1 === "object") {
|
||||
this.maxAttemptsProvider = async () => arg1.maxAttempts;
|
||||
this.baseDelay = arg1.baseDelay;
|
||||
this.retryBackoffStrategy = arg1.backoff;
|
||||
}
|
||||
this.maxAttemptsProvider ??= async () => DEFAULT_MAX_ATTEMPTS;
|
||||
this.baseDelay ??= Retry.delay();
|
||||
this.retryBackoffStrategy ??= new DefaultRetryBackoffStrategy();
|
||||
}
|
||||
async acquireInitialRetryToken(retryTokenScope) {
|
||||
return new DefaultRetryToken(Retry.delay(), 0, undefined, Retry.v2026 && retryTokenScope.includes(":longpoll"));
|
||||
}
|
||||
async refreshRetryTokenForRetry(token, errorInfo) {
|
||||
const maxAttempts = await this.getMaxAttempts();
|
||||
const retryCode = this.retryCode(token, errorInfo, maxAttempts);
|
||||
const shouldRetry = retryCode === 0;
|
||||
const isLongPoll = token.isLongPoll?.();
|
||||
if (shouldRetry || isLongPoll) {
|
||||
const errorType = errorInfo.errorType;
|
||||
this.retryBackoffStrategy.setDelayBase(errorType === "THROTTLING" ? Retry.throttlingDelay() : this.baseDelay);
|
||||
const delayFromErrorType = this.retryBackoffStrategy.computeNextBackoffDelay(token.getRetryCount());
|
||||
let retryDelay = delayFromErrorType;
|
||||
if (errorInfo.retryAfterHint instanceof Date) {
|
||||
retryDelay = Math.max(delayFromErrorType, Math.min(errorInfo.retryAfterHint.getTime() - Date.now(), delayFromErrorType + 5_000));
|
||||
}
|
||||
if (!shouldRetry) {
|
||||
const longPollBackoff = Retry.v2026 && retryCode === refusal.capacity && isLongPoll ? retryDelay : 0;
|
||||
if (longPollBackoff > 0) {
|
||||
await new Promise((r) => setTimeout(r, longPollBackoff));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const capacityCost = this.getCapacityCost(errorType);
|
||||
this.capacity -= capacityCost;
|
||||
const nextToken = new DefaultRetryToken(0, token.getRetryCount() + 1, capacityCost, token.isLongPoll?.() ?? false);
|
||||
await new Promise((r) => setTimeout(r, retryDelay));
|
||||
nextToken.$retryLog.acquisitionDelay = retryDelay;
|
||||
return nextToken;
|
||||
}
|
||||
}
|
||||
throw new Error("No retry token available");
|
||||
}
|
||||
recordSuccess(token) {
|
||||
this.capacity = Math.min(INITIAL_RETRY_TOKENS, this.capacity + (token.getRetryCost() ?? NO_RETRY_INCREMENT));
|
||||
}
|
||||
getCapacity() {
|
||||
return this.capacity;
|
||||
}
|
||||
async maxAttempts() {
|
||||
return this.maxAttemptsProvider();
|
||||
}
|
||||
async getMaxAttempts() {
|
||||
try {
|
||||
return await this.maxAttemptsProvider();
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(`Max attempts provider could not resolve. Using default of ${DEFAULT_MAX_ATTEMPTS}`);
|
||||
return DEFAULT_MAX_ATTEMPTS;
|
||||
}
|
||||
}
|
||||
retryCode(tokenToRenew, errorInfo, maxAttempts) {
|
||||
const attempts = tokenToRenew.getRetryCount() + 1;
|
||||
const retryableStatus = this.isRetryableError(errorInfo.errorType) ? 0 : refusal.incompatible;
|
||||
const attemptStatus = attempts < maxAttempts ? 0 : refusal.attempts;
|
||||
const capacityStatus = this.capacity >= this.getCapacityCost(errorInfo.errorType) ? 0 : refusal.capacity;
|
||||
return retryableStatus || attemptStatus || capacityStatus;
|
||||
}
|
||||
getCapacityCost(errorType) {
|
||||
return errorType === Retry.modifiedCostType() ? Retry.throttlingCost() : Retry.cost();
|
||||
}
|
||||
isRetryableError(errorType) {
|
||||
return errorType === "THROTTLING" || errorType === "TRANSIENT";
|
||||
}
|
||||
};
|
||||
|
||||
let AdaptiveRetryStrategy$1 = class AdaptiveRetryStrategy {
|
||||
mode = exports.RETRY_MODES.ADAPTIVE;
|
||||
rateLimiter;
|
||||
standardRetryStrategy;
|
||||
constructor(maxAttemptsProvider, options) {
|
||||
const { rateLimiter } = options ?? {};
|
||||
this.rateLimiter = rateLimiter ?? new DefaultRateLimiter();
|
||||
this.standardRetryStrategy = options
|
||||
? new StandardRetryStrategy$1({
|
||||
maxAttempts: typeof maxAttemptsProvider === "number" ? maxAttemptsProvider : 3,
|
||||
...options,
|
||||
})
|
||||
: new StandardRetryStrategy$1(maxAttemptsProvider);
|
||||
}
|
||||
async acquireInitialRetryToken(retryTokenScope) {
|
||||
const token = await this.standardRetryStrategy.acquireInitialRetryToken(retryTokenScope);
|
||||
await this.rateLimiter.getSendToken();
|
||||
return token;
|
||||
}
|
||||
async refreshRetryTokenForRetry(tokenToRenew, errorInfo) {
|
||||
this.rateLimiter.updateClientSendingRate(errorInfo);
|
||||
const token = await this.standardRetryStrategy.refreshRetryTokenForRetry(tokenToRenew, errorInfo);
|
||||
await this.rateLimiter.getSendToken();
|
||||
return token;
|
||||
}
|
||||
recordSuccess(token) {
|
||||
this.rateLimiter.updateClientSendingRate({});
|
||||
this.standardRetryStrategy.recordSuccess(token);
|
||||
}
|
||||
async maxAttemptsProvider() {
|
||||
return this.standardRetryStrategy.maxAttempts();
|
||||
}
|
||||
};
|
||||
|
||||
class ConfiguredRetryStrategy extends StandardRetryStrategy$1 {
|
||||
computeNextBackoffDelay;
|
||||
constructor(maxAttempts, computeNextBackoffDelay = Retry.delay()) {
|
||||
super(typeof maxAttempts === "function" ? maxAttempts : async () => maxAttempts);
|
||||
if (typeof computeNextBackoffDelay === "number") {
|
||||
this.computeNextBackoffDelay = () => computeNextBackoffDelay;
|
||||
}
|
||||
else {
|
||||
this.computeNextBackoffDelay = computeNextBackoffDelay;
|
||||
}
|
||||
this.retryBackoffStrategy.computeNextBackoffDelay = (completedAttempt) => {
|
||||
const nextAttempt = completedAttempt + 1;
|
||||
return this.computeNextBackoffDelay(nextAttempt);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const getDefaultRetryQuota = (initialRetryTokens, options) => {
|
||||
const MAX_CAPACITY = initialRetryTokens;
|
||||
const noRetryIncrement = NO_RETRY_INCREMENT;
|
||||
const retryCost = RETRY_COST;
|
||||
const timeoutRetryCost = TIMEOUT_RETRY_COST;
|
||||
let availableCapacity = initialRetryTokens;
|
||||
const getCapacityAmount = (error) => (error.name === "TimeoutError" ? timeoutRetryCost : retryCost);
|
||||
const hasRetryTokens = (error) => getCapacityAmount(error) <= availableCapacity;
|
||||
const retrieveRetryTokens = (error) => {
|
||||
if (!hasRetryTokens(error)) {
|
||||
throw new Error("No retry token available");
|
||||
}
|
||||
const capacityAmount = getCapacityAmount(error);
|
||||
availableCapacity -= capacityAmount;
|
||||
return capacityAmount;
|
||||
};
|
||||
const releaseRetryTokens = (capacityReleaseAmount) => {
|
||||
availableCapacity += capacityReleaseAmount ?? noRetryIncrement;
|
||||
availableCapacity = Math.min(availableCapacity, MAX_CAPACITY);
|
||||
};
|
||||
return Object.freeze({
|
||||
hasRetryTokens,
|
||||
retrieveRetryTokens,
|
||||
releaseRetryTokens,
|
||||
});
|
||||
};
|
||||
|
||||
const defaultDelayDecider = (delayBase, attempts) => Math.floor(Math.min(MAXIMUM_RETRY_DELAY, Math.random() * 2 ** attempts * delayBase));
|
||||
|
||||
const defaultRetryDecider = (error) => {
|
||||
if (!error) {
|
||||
return false;
|
||||
}
|
||||
return isRetryableByTrait(error) || isClockSkewError(error) || isThrottlingError(error) || isTransientError(error);
|
||||
};
|
||||
|
||||
class StandardRetryStrategy {
|
||||
maxAttemptsProvider;
|
||||
retryDecider;
|
||||
delayDecider;
|
||||
retryQuota;
|
||||
mode = exports.RETRY_MODES.STANDARD;
|
||||
constructor(maxAttemptsProvider, options) {
|
||||
this.maxAttemptsProvider = maxAttemptsProvider;
|
||||
this.retryDecider = options?.retryDecider ?? defaultRetryDecider;
|
||||
this.delayDecider = options?.delayDecider ?? defaultDelayDecider;
|
||||
this.retryQuota = options?.retryQuota ?? getDefaultRetryQuota(INITIAL_RETRY_TOKENS);
|
||||
}
|
||||
shouldRetry(error, attempts, maxAttempts) {
|
||||
return attempts < maxAttempts && this.retryDecider(error) && this.retryQuota.hasRetryTokens(error);
|
||||
}
|
||||
async getMaxAttempts() {
|
||||
let maxAttempts;
|
||||
try {
|
||||
maxAttempts = await this.maxAttemptsProvider();
|
||||
}
|
||||
catch (error) {
|
||||
maxAttempts = DEFAULT_MAX_ATTEMPTS;
|
||||
}
|
||||
return maxAttempts;
|
||||
}
|
||||
async retry(next, args, options) {
|
||||
let retryTokenAmount;
|
||||
let attempts = 0;
|
||||
let totalDelay = 0;
|
||||
const maxAttempts = await this.getMaxAttempts();
|
||||
const { request } = args;
|
||||
if (protocols.HttpRequest.isInstance(request)) {
|
||||
request.headers[INVOCATION_ID_HEADER] = serde.v4();
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
if (protocols.HttpRequest.isInstance(request)) {
|
||||
request.headers[REQUEST_HEADER] = `attempt=${attempts + 1}; max=${maxAttempts}`;
|
||||
}
|
||||
if (options?.beforeRequest) {
|
||||
await options.beforeRequest();
|
||||
}
|
||||
const { response, output } = await next(args);
|
||||
if (options?.afterRequest) {
|
||||
options.afterRequest(response);
|
||||
}
|
||||
this.retryQuota.releaseRetryTokens(retryTokenAmount);
|
||||
output.$metadata.attempts = attempts + 1;
|
||||
output.$metadata.totalRetryDelay = totalDelay;
|
||||
return { response, output };
|
||||
}
|
||||
catch (e) {
|
||||
const err = asSdkError(e);
|
||||
attempts++;
|
||||
if (this.shouldRetry(err, attempts, maxAttempts)) {
|
||||
retryTokenAmount = this.retryQuota.retrieveRetryTokens(err);
|
||||
const delayFromDecider = this.delayDecider(isThrottlingError(err) ? THROTTLING_RETRY_DELAY_BASE : DEFAULT_RETRY_DELAY_BASE, attempts);
|
||||
const delayFromResponse = getDelayFromRetryAfterHeader(err.$response);
|
||||
const delay = Math.max(delayFromResponse || 0, delayFromDecider);
|
||||
totalDelay += delay;
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
continue;
|
||||
}
|
||||
if (!err.$metadata) {
|
||||
err.$metadata = {};
|
||||
}
|
||||
err.$metadata.attempts = attempts;
|
||||
err.$metadata.totalRetryDelay = totalDelay;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const getDelayFromRetryAfterHeader = (response) => {
|
||||
if (!protocols.HttpResponse.isInstance(response))
|
||||
return;
|
||||
const retryAfterHeaderName = Object.keys(response.headers).find((key) => key.toLowerCase() === "retry-after");
|
||||
if (!retryAfterHeaderName)
|
||||
return;
|
||||
const retryAfter = response.headers[retryAfterHeaderName];
|
||||
const retryAfterSeconds = Number(retryAfter);
|
||||
if (!Number.isNaN(retryAfterSeconds))
|
||||
return retryAfterSeconds * 1000;
|
||||
const retryAfterDate = new Date(retryAfter);
|
||||
return retryAfterDate.getTime() - Date.now();
|
||||
};
|
||||
|
||||
class AdaptiveRetryStrategy extends StandardRetryStrategy {
|
||||
rateLimiter;
|
||||
constructor(maxAttemptsProvider, options) {
|
||||
const { rateLimiter, ...superOptions } = options ?? {};
|
||||
super(maxAttemptsProvider, superOptions);
|
||||
this.rateLimiter = rateLimiter ?? new DefaultRateLimiter();
|
||||
this.mode = exports.RETRY_MODES.ADAPTIVE;
|
||||
}
|
||||
async retry(next, args) {
|
||||
return super.retry(next, args, {
|
||||
beforeRequest: async () => {
|
||||
return this.rateLimiter.getSendToken();
|
||||
},
|
||||
afterRequest: (response) => {
|
||||
this.rateLimiter.updateClientSendingRate(response);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const ENV_MAX_ATTEMPTS = "AWS_MAX_ATTEMPTS";
|
||||
const CONFIG_MAX_ATTEMPTS = "max_attempts";
|
||||
const NODE_MAX_ATTEMPT_CONFIG_OPTIONS = {
|
||||
environmentVariableSelector: (env) => {
|
||||
const value = env[ENV_MAX_ATTEMPTS];
|
||||
if (!value)
|
||||
return undefined;
|
||||
const maxAttempt = parseInt(value);
|
||||
if (Number.isNaN(maxAttempt)) {
|
||||
throw new Error(`Environment variable ${ENV_MAX_ATTEMPTS} mast be a number, got "${value}"`);
|
||||
}
|
||||
return maxAttempt;
|
||||
},
|
||||
configFileSelector: (profile) => {
|
||||
const value = profile[CONFIG_MAX_ATTEMPTS];
|
||||
if (!value)
|
||||
return undefined;
|
||||
const maxAttempt = parseInt(value);
|
||||
if (Number.isNaN(maxAttempt)) {
|
||||
throw new Error(`Shared config file entry ${CONFIG_MAX_ATTEMPTS} mast be a number, got "${value}"`);
|
||||
}
|
||||
return maxAttempt;
|
||||
},
|
||||
default: DEFAULT_MAX_ATTEMPTS,
|
||||
};
|
||||
const resolveRetryConfig = (input, defaults) => {
|
||||
const { retryStrategy, retryMode } = input;
|
||||
const { defaultMaxAttempts = DEFAULT_MAX_ATTEMPTS, defaultBaseDelay = Retry.delay() } = defaults ?? {};
|
||||
const maxAttemptsProvider = client.normalizeProvider(input.maxAttempts ?? defaultMaxAttempts);
|
||||
let controller = retryStrategy
|
||||
? Promise.resolve(retryStrategy)
|
||||
: undefined;
|
||||
const getDefault = async () => {
|
||||
const maxAttempts = await maxAttemptsProvider();
|
||||
const adaptive = (await client.normalizeProvider(retryMode)()) === exports.RETRY_MODES.ADAPTIVE;
|
||||
if (adaptive) {
|
||||
return new AdaptiveRetryStrategy$1(maxAttemptsProvider, {
|
||||
maxAttempts,
|
||||
baseDelay: defaultBaseDelay,
|
||||
});
|
||||
}
|
||||
return new StandardRetryStrategy$1({
|
||||
maxAttempts,
|
||||
baseDelay: defaultBaseDelay,
|
||||
});
|
||||
};
|
||||
return Object.assign(input, {
|
||||
maxAttempts: maxAttemptsProvider,
|
||||
retryStrategy: () => (controller ??= getDefault()),
|
||||
});
|
||||
};
|
||||
const ENV_RETRY_MODE = "AWS_RETRY_MODE";
|
||||
const CONFIG_RETRY_MODE = "retry_mode";
|
||||
const NODE_RETRY_MODE_CONFIG_OPTIONS = {
|
||||
environmentVariableSelector: (env) => env[ENV_RETRY_MODE],
|
||||
configFileSelector: (profile) => profile[CONFIG_RETRY_MODE],
|
||||
default: DEFAULT_RETRY_MODE,
|
||||
};
|
||||
|
||||
const omitRetryHeadersMiddleware = () => (next) => async (args) => {
|
||||
const { request } = args;
|
||||
if (protocols.HttpRequest.isInstance(request)) {
|
||||
delete request.headers[INVOCATION_ID_HEADER];
|
||||
delete request.headers[REQUEST_HEADER];
|
||||
}
|
||||
return next(args);
|
||||
};
|
||||
const omitRetryHeadersMiddlewareOptions = {
|
||||
name: "omitRetryHeadersMiddleware",
|
||||
tags: ["RETRY", "HEADERS", "OMIT_RETRY_HEADERS"],
|
||||
relation: "before",
|
||||
toMiddleware: "awsAuthMiddleware",
|
||||
override: true,
|
||||
};
|
||||
const getOmitRetryHeadersPlugin = (options) => ({
|
||||
applyToStack: (clientStack) => {
|
||||
clientStack.addRelativeTo(omitRetryHeadersMiddleware(), omitRetryHeadersMiddlewareOptions);
|
||||
},
|
||||
});
|
||||
|
||||
const retryMiddleware = bindRetryMiddleware(isStreamingPayload);
|
||||
const getRetryPlugin = bindGetRetryPlugin(isStreamingPayload);
|
||||
|
||||
exports.AdaptiveRetryStrategy = AdaptiveRetryStrategy$1;
|
||||
exports.CONFIG_MAX_ATTEMPTS = CONFIG_MAX_ATTEMPTS;
|
||||
exports.CONFIG_RETRY_MODE = CONFIG_RETRY_MODE;
|
||||
exports.ConfiguredRetryStrategy = ConfiguredRetryStrategy;
|
||||
exports.DEFAULT_MAX_ATTEMPTS = DEFAULT_MAX_ATTEMPTS;
|
||||
exports.DEFAULT_RETRY_DELAY_BASE = DEFAULT_RETRY_DELAY_BASE;
|
||||
exports.DEFAULT_RETRY_MODE = DEFAULT_RETRY_MODE;
|
||||
exports.DefaultRateLimiter = DefaultRateLimiter;
|
||||
exports.DeprecatedAdaptiveRetryStrategy = AdaptiveRetryStrategy;
|
||||
exports.DeprecatedStandardRetryStrategy = StandardRetryStrategy;
|
||||
exports.ENV_MAX_ATTEMPTS = ENV_MAX_ATTEMPTS;
|
||||
exports.ENV_RETRY_MODE = ENV_RETRY_MODE;
|
||||
exports.INITIAL_RETRY_TOKENS = INITIAL_RETRY_TOKENS;
|
||||
exports.INVOCATION_ID_HEADER = INVOCATION_ID_HEADER;
|
||||
exports.MAXIMUM_RETRY_DELAY = MAXIMUM_RETRY_DELAY;
|
||||
exports.NODE_MAX_ATTEMPT_CONFIG_OPTIONS = NODE_MAX_ATTEMPT_CONFIG_OPTIONS;
|
||||
exports.NODE_RETRY_MODE_CONFIG_OPTIONS = NODE_RETRY_MODE_CONFIG_OPTIONS;
|
||||
exports.NO_RETRY_INCREMENT = NO_RETRY_INCREMENT;
|
||||
exports.REQUEST_HEADER = REQUEST_HEADER;
|
||||
exports.RETRY_COST = RETRY_COST;
|
||||
exports.Retry = Retry;
|
||||
exports.StandardRetryStrategy = StandardRetryStrategy$1;
|
||||
exports.THROTTLING_RETRY_DELAY_BASE = THROTTLING_RETRY_DELAY_BASE;
|
||||
exports.TIMEOUT_RETRY_COST = TIMEOUT_RETRY_COST;
|
||||
exports.defaultDelayDecider = defaultDelayDecider;
|
||||
exports.defaultRetryDecider = defaultRetryDecider;
|
||||
exports.getOmitRetryHeadersPlugin = getOmitRetryHeadersPlugin;
|
||||
exports.getRetryAfterHint = getRetryAfterHint;
|
||||
exports.getRetryPlugin = getRetryPlugin;
|
||||
exports.isBrowserNetworkError = isBrowserNetworkError;
|
||||
exports.isClockSkewCorrectedError = isClockSkewCorrectedError;
|
||||
exports.isClockSkewError = isClockSkewError;
|
||||
exports.isNodeJsHttp2TransientError = isNodeJsHttp2TransientError;
|
||||
exports.isRetryableByTrait = isRetryableByTrait;
|
||||
exports.isServerError = isServerError;
|
||||
exports.isThrottlingError = isThrottlingError;
|
||||
exports.isTransientError = isTransientError;
|
||||
exports.omitRetryHeadersMiddleware = omitRetryHeadersMiddleware;
|
||||
exports.omitRetryHeadersMiddlewareOptions = omitRetryHeadersMiddlewareOptions;
|
||||
exports.resolveRetryConfig = resolveRetryConfig;
|
||||
exports.retryMiddleware = retryMiddleware;
|
||||
exports.retryMiddlewareOptions = retryMiddlewareOptions;
|
||||
+713
@@ -0,0 +1,713 @@
|
||||
'use strict';
|
||||
|
||||
var transport = require('@smithy/core/transport');
|
||||
|
||||
const deref = (schemaRef) => {
|
||||
if (typeof schemaRef === "function") {
|
||||
return schemaRef();
|
||||
}
|
||||
return schemaRef;
|
||||
};
|
||||
|
||||
const operation = (namespace, name, traits, input, output) => ({
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
input,
|
||||
output,
|
||||
});
|
||||
|
||||
const schemaDeserializationMiddleware = (config) => (next, context) => async (args) => {
|
||||
const { response } = await next(args);
|
||||
const { operationSchema } = transport.getSmithyContext(context);
|
||||
const [, ns, n, t, i, o] = operationSchema ?? [];
|
||||
try {
|
||||
const parsed = await config.protocol.deserializeResponse(operation(ns, n, t, i, o), {
|
||||
...config,
|
||||
...context,
|
||||
}, response);
|
||||
return {
|
||||
response,
|
||||
output: parsed,
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
Object.defineProperty(error, "$response", {
|
||||
value: response,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
configurable: false,
|
||||
});
|
||||
if (!("$metadata" in error)) {
|
||||
const hint = `Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.`;
|
||||
try {
|
||||
error.message += "\n " + hint;
|
||||
}
|
||||
catch (e) {
|
||||
if (!context.logger || context.logger?.constructor?.name === "NoOpLogger") {
|
||||
console.warn(hint);
|
||||
}
|
||||
else {
|
||||
context.logger?.warn?.(hint);
|
||||
}
|
||||
}
|
||||
if (typeof error.$responseBodyText !== "undefined") {
|
||||
if (error.$response) {
|
||||
error.$response.body = error.$responseBodyText;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (transport.HttpResponse.isInstance(response)) {
|
||||
const { headers = {}, statusCode } = response;
|
||||
const headerEntries = Object.entries(headers);
|
||||
error.$metadata = {
|
||||
httpStatusCode: statusCode,
|
||||
requestId: findHeader(/^x-[\w-]+-request-?id$/, headerEntries),
|
||||
extendedRequestId: findHeader(/^x-[\w-]+-id-2$/, headerEntries),
|
||||
cfId: findHeader(/^x-[\w-]+-cf-id$/, headerEntries),
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const findHeader = (pattern, headers) => {
|
||||
return (headers.find(([k]) => {
|
||||
return k.match(pattern);
|
||||
}) || [void 0, void 0])[1];
|
||||
};
|
||||
|
||||
const schemaSerializationMiddleware = (config) => (next, context) => async (args) => {
|
||||
const { operationSchema } = transport.getSmithyContext(context);
|
||||
const [, ns, n, t, i, o] = operationSchema ?? [];
|
||||
const endpoint = context.endpointV2
|
||||
? async () => transport.toEndpointV1(context.endpointV2)
|
||||
: config.endpoint;
|
||||
const request = await config.protocol.serializeRequest(operation(ns, n, t, i, o), args.input, {
|
||||
...config,
|
||||
...context,
|
||||
endpoint,
|
||||
});
|
||||
return next({
|
||||
...args,
|
||||
request,
|
||||
});
|
||||
};
|
||||
|
||||
const deserializerMiddlewareOption = {
|
||||
name: "deserializerMiddleware",
|
||||
step: "deserialize",
|
||||
tags: ["DESERIALIZER"],
|
||||
override: true,
|
||||
};
|
||||
const serializerMiddlewareOption = {
|
||||
name: "serializerMiddleware",
|
||||
step: "serialize",
|
||||
tags: ["SERIALIZER"],
|
||||
override: true,
|
||||
};
|
||||
function getSchemaSerdePlugin(config) {
|
||||
return {
|
||||
applyToStack: (commandStack) => {
|
||||
commandStack.add(schemaSerializationMiddleware(config), serializerMiddlewareOption);
|
||||
commandStack.add(schemaDeserializationMiddleware(config), deserializerMiddlewareOption);
|
||||
config.protocol.setSerdeContext(config);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class Schema {
|
||||
name;
|
||||
namespace;
|
||||
traits;
|
||||
static assign(instance, values) {
|
||||
const schema = Object.assign(instance, values);
|
||||
return schema;
|
||||
}
|
||||
static [Symbol.hasInstance](lhs) {
|
||||
const isPrototype = this.prototype.isPrototypeOf(lhs);
|
||||
if (!isPrototype && typeof lhs === "object" && lhs !== null) {
|
||||
const list = lhs;
|
||||
return list.symbol === this.symbol;
|
||||
}
|
||||
return isPrototype;
|
||||
}
|
||||
getName() {
|
||||
return this.namespace + "#" + this.name;
|
||||
}
|
||||
}
|
||||
|
||||
class ListSchema extends Schema {
|
||||
static symbol = Symbol.for("@smithy/lis");
|
||||
name;
|
||||
traits;
|
||||
valueSchema;
|
||||
symbol = ListSchema.symbol;
|
||||
}
|
||||
const list = (namespace, name, traits, valueSchema) => Schema.assign(new ListSchema(), {
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
valueSchema,
|
||||
});
|
||||
|
||||
class MapSchema extends Schema {
|
||||
static symbol = Symbol.for("@smithy/map");
|
||||
name;
|
||||
traits;
|
||||
keySchema;
|
||||
valueSchema;
|
||||
symbol = MapSchema.symbol;
|
||||
}
|
||||
const map = (namespace, name, traits, keySchema, valueSchema) => Schema.assign(new MapSchema(), {
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
keySchema,
|
||||
valueSchema,
|
||||
});
|
||||
|
||||
class OperationSchema extends Schema {
|
||||
static symbol = Symbol.for("@smithy/ope");
|
||||
name;
|
||||
traits;
|
||||
input;
|
||||
output;
|
||||
symbol = OperationSchema.symbol;
|
||||
}
|
||||
const op = (namespace, name, traits, input, output) => Schema.assign(new OperationSchema(), {
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
input,
|
||||
output,
|
||||
});
|
||||
|
||||
class StructureSchema extends Schema {
|
||||
static symbol = Symbol.for("@smithy/str");
|
||||
name;
|
||||
traits;
|
||||
memberNames;
|
||||
memberList;
|
||||
symbol = StructureSchema.symbol;
|
||||
}
|
||||
const struct = (namespace, name, traits, memberNames, memberList) => Schema.assign(new StructureSchema(), {
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
memberNames,
|
||||
memberList,
|
||||
});
|
||||
|
||||
class ErrorSchema extends StructureSchema {
|
||||
static symbol = Symbol.for("@smithy/err");
|
||||
ctor;
|
||||
symbol = ErrorSchema.symbol;
|
||||
}
|
||||
const error = (namespace, name, traits, memberNames, memberList, ctor) => Schema.assign(new ErrorSchema(), {
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
memberNames,
|
||||
memberList,
|
||||
ctor: null,
|
||||
});
|
||||
|
||||
const traitsCache = [];
|
||||
function translateTraits(indicator) {
|
||||
if (typeof indicator === "object") {
|
||||
return indicator;
|
||||
}
|
||||
indicator = indicator | 0;
|
||||
if (traitsCache[indicator]) {
|
||||
return traitsCache[indicator];
|
||||
}
|
||||
const traits = {};
|
||||
let i = 0;
|
||||
for (const trait of [
|
||||
"httpLabel",
|
||||
"idempotent",
|
||||
"idempotencyToken",
|
||||
"sensitive",
|
||||
"httpPayload",
|
||||
"httpResponseCode",
|
||||
"httpQueryParams",
|
||||
]) {
|
||||
if (((indicator >> i++) & 1) === 1) {
|
||||
traits[trait] = 1;
|
||||
}
|
||||
}
|
||||
return (traitsCache[indicator] = traits);
|
||||
}
|
||||
|
||||
const anno = {
|
||||
it: Symbol.for("@smithy/nor-struct-it"),
|
||||
ns: Symbol.for("@smithy/ns"),
|
||||
};
|
||||
const simpleSchemaCacheN = [];
|
||||
const simpleSchemaCacheS = {};
|
||||
class NormalizedSchema {
|
||||
ref;
|
||||
memberName;
|
||||
static symbol = Symbol.for("@smithy/nor");
|
||||
symbol = NormalizedSchema.symbol;
|
||||
name;
|
||||
schema;
|
||||
_isMemberSchema;
|
||||
traits;
|
||||
memberTraits;
|
||||
normalizedTraits;
|
||||
constructor(ref, memberName) {
|
||||
this.ref = ref;
|
||||
this.memberName = memberName;
|
||||
const traitStack = [];
|
||||
let _ref = ref;
|
||||
let schema = ref;
|
||||
this._isMemberSchema = false;
|
||||
while (isMemberSchema(_ref)) {
|
||||
traitStack.push(_ref[1]);
|
||||
_ref = _ref[0];
|
||||
schema = deref(_ref);
|
||||
this._isMemberSchema = true;
|
||||
}
|
||||
if (traitStack.length > 0) {
|
||||
this.memberTraits = {};
|
||||
for (let i = traitStack.length - 1; i >= 0; --i) {
|
||||
const traitSet = traitStack[i];
|
||||
Object.assign(this.memberTraits, translateTraits(traitSet));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.memberTraits = 0;
|
||||
}
|
||||
if (schema instanceof NormalizedSchema) {
|
||||
const computedMemberTraits = this.memberTraits;
|
||||
Object.assign(this, schema);
|
||||
this.memberTraits = Object.assign({}, computedMemberTraits, schema.getMemberTraits(), this.getMemberTraits());
|
||||
this.normalizedTraits = void 0;
|
||||
this.memberName = memberName ?? schema.memberName;
|
||||
return;
|
||||
}
|
||||
this.schema = deref(schema);
|
||||
if (isStaticSchema(this.schema)) {
|
||||
this.name = `${this.schema[1]}#${this.schema[2]}`;
|
||||
this.traits = this.schema[3];
|
||||
}
|
||||
else {
|
||||
this.name = this.memberName ?? String(schema);
|
||||
this.traits = 0;
|
||||
}
|
||||
if (this._isMemberSchema && !memberName) {
|
||||
throw new Error(`@smithy/core/schema - NormalizedSchema member init ${this.getName(true)} missing member name.`);
|
||||
}
|
||||
}
|
||||
static [Symbol.hasInstance](lhs) {
|
||||
const isPrototype = this.prototype.isPrototypeOf(lhs);
|
||||
if (!isPrototype && typeof lhs === "object" && lhs !== null) {
|
||||
const ns = lhs;
|
||||
return ns.symbol === this.symbol;
|
||||
}
|
||||
return isPrototype;
|
||||
}
|
||||
static of(ref) {
|
||||
const keyAble = typeof ref === "function" || (typeof ref === "object" && ref !== null);
|
||||
if (typeof ref === "number") {
|
||||
if (simpleSchemaCacheN[ref]) {
|
||||
return simpleSchemaCacheN[ref];
|
||||
}
|
||||
}
|
||||
else if (typeof ref === "string") {
|
||||
if (simpleSchemaCacheS[ref]) {
|
||||
return simpleSchemaCacheS[ref];
|
||||
}
|
||||
}
|
||||
else if (keyAble) {
|
||||
if (ref[anno.ns]) {
|
||||
return ref[anno.ns];
|
||||
}
|
||||
}
|
||||
const sc = deref(ref);
|
||||
if (sc instanceof NormalizedSchema) {
|
||||
return sc;
|
||||
}
|
||||
if (isMemberSchema(sc)) {
|
||||
const [ns, traits] = sc;
|
||||
if (ns instanceof NormalizedSchema) {
|
||||
Object.assign(ns.getMergedTraits(), translateTraits(traits));
|
||||
return ns;
|
||||
}
|
||||
throw new Error(`@smithy/core/schema - may not init unwrapped member schema=${JSON.stringify(ref, null, 2)}.`);
|
||||
}
|
||||
const ns = new NormalizedSchema(sc);
|
||||
if (keyAble) {
|
||||
return (ref[anno.ns] = ns);
|
||||
}
|
||||
if (typeof sc === "string") {
|
||||
return (simpleSchemaCacheS[sc] = ns);
|
||||
}
|
||||
if (typeof sc === "number") {
|
||||
return (simpleSchemaCacheN[sc] = ns);
|
||||
}
|
||||
return ns;
|
||||
}
|
||||
getSchema() {
|
||||
const sc = this.schema;
|
||||
if (Array.isArray(sc) && sc[0] === 0) {
|
||||
return sc[4];
|
||||
}
|
||||
return sc;
|
||||
}
|
||||
getName(withNamespace = false) {
|
||||
const { name } = this;
|
||||
const short = !withNamespace && name && name.includes("#");
|
||||
return short ? name.split("#")[1] : name || undefined;
|
||||
}
|
||||
getMemberName() {
|
||||
return this.memberName;
|
||||
}
|
||||
isMemberSchema() {
|
||||
return this._isMemberSchema;
|
||||
}
|
||||
isListSchema() {
|
||||
const sc = this.getSchema();
|
||||
return typeof sc === "number"
|
||||
? sc >= 64 && sc < 128
|
||||
: sc[0] === 1;
|
||||
}
|
||||
isMapSchema() {
|
||||
const sc = this.getSchema();
|
||||
return typeof sc === "number"
|
||||
? sc >= 128 && sc <= 0b1111_1111
|
||||
: sc[0] === 2;
|
||||
}
|
||||
isStructSchema() {
|
||||
const sc = this.getSchema();
|
||||
if (typeof sc !== "object") {
|
||||
return false;
|
||||
}
|
||||
const id = sc[0];
|
||||
return (id === 3 ||
|
||||
id === -3 ||
|
||||
id === 4);
|
||||
}
|
||||
isUnionSchema() {
|
||||
const sc = this.getSchema();
|
||||
if (typeof sc !== "object") {
|
||||
return false;
|
||||
}
|
||||
return sc[0] === 4;
|
||||
}
|
||||
isBlobSchema() {
|
||||
const sc = this.getSchema();
|
||||
return sc === 21 || sc === 42;
|
||||
}
|
||||
isTimestampSchema() {
|
||||
const sc = this.getSchema();
|
||||
return (typeof sc === "number" &&
|
||||
sc >= 4 &&
|
||||
sc <= 7);
|
||||
}
|
||||
isUnitSchema() {
|
||||
return this.getSchema() === "unit";
|
||||
}
|
||||
isDocumentSchema() {
|
||||
return this.getSchema() === 15;
|
||||
}
|
||||
isStringSchema() {
|
||||
return this.getSchema() === 0;
|
||||
}
|
||||
isBooleanSchema() {
|
||||
return this.getSchema() === 2;
|
||||
}
|
||||
isNumericSchema() {
|
||||
return this.getSchema() === 1;
|
||||
}
|
||||
isBigIntegerSchema() {
|
||||
return this.getSchema() === 17;
|
||||
}
|
||||
isBigDecimalSchema() {
|
||||
return this.getSchema() === 19;
|
||||
}
|
||||
isStreaming() {
|
||||
const { streaming } = this.getMergedTraits();
|
||||
return !!streaming || this.getSchema() === 42;
|
||||
}
|
||||
isIdempotencyToken() {
|
||||
return !!this.getMergedTraits().idempotencyToken;
|
||||
}
|
||||
getMergedTraits() {
|
||||
return (this.normalizedTraits ??
|
||||
(this.normalizedTraits = {
|
||||
...this.getOwnTraits(),
|
||||
...this.getMemberTraits(),
|
||||
}));
|
||||
}
|
||||
getMemberTraits() {
|
||||
return translateTraits(this.memberTraits);
|
||||
}
|
||||
getOwnTraits() {
|
||||
return translateTraits(this.traits);
|
||||
}
|
||||
getKeySchema() {
|
||||
const [isDoc, isMap] = [this.isDocumentSchema(), this.isMapSchema()];
|
||||
if (!isDoc && !isMap) {
|
||||
throw new Error(`@smithy/core/schema - cannot get key for non-map: ${this.getName(true)}`);
|
||||
}
|
||||
const schema = this.getSchema();
|
||||
const memberSchema = isDoc
|
||||
? 15
|
||||
: schema[4] ?? 0;
|
||||
return member([memberSchema, 0], "key");
|
||||
}
|
||||
getValueSchema() {
|
||||
const sc = this.getSchema();
|
||||
const [isDoc, isMap, isList] = [this.isDocumentSchema(), this.isMapSchema(), this.isListSchema()];
|
||||
const memberSchema = typeof sc === "number"
|
||||
? 0b0011_1111 & sc
|
||||
: sc && typeof sc === "object" && (isMap || isList)
|
||||
? sc[3 + sc[0]]
|
||||
: isDoc
|
||||
? 15
|
||||
: void 0;
|
||||
if (memberSchema != null) {
|
||||
return member([memberSchema, 0], isMap ? "value" : "member");
|
||||
}
|
||||
throw new Error(`@smithy/core/schema - ${this.getName(true)} has no value member.`);
|
||||
}
|
||||
getMemberSchema(memberName) {
|
||||
const struct = this.getSchema();
|
||||
if (this.isStructSchema() && struct[4].includes(memberName)) {
|
||||
const i = struct[4].indexOf(memberName);
|
||||
const memberSchema = struct[5][i];
|
||||
return member(isMemberSchema(memberSchema) ? memberSchema : [memberSchema, 0], memberName);
|
||||
}
|
||||
if (this.isDocumentSchema()) {
|
||||
return member([15, 0], memberName);
|
||||
}
|
||||
throw new Error(`@smithy/core/schema - ${this.getName(true)} has no member=${memberName}.`);
|
||||
}
|
||||
getMemberSchemas() {
|
||||
const buffer = {};
|
||||
try {
|
||||
for (const [k, v] of this.structIterator()) {
|
||||
buffer[k] = v;
|
||||
}
|
||||
}
|
||||
catch (ignored) { }
|
||||
return buffer;
|
||||
}
|
||||
getEventStreamMember() {
|
||||
if (this.isStructSchema()) {
|
||||
for (const [memberName, memberSchema] of this.structIterator()) {
|
||||
if (memberSchema.isStreaming() && memberSchema.isStructSchema()) {
|
||||
return memberName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
*structIterator() {
|
||||
if (this.isUnitSchema()) {
|
||||
return;
|
||||
}
|
||||
if (!this.isStructSchema()) {
|
||||
throw new Error("@smithy/core/schema - cannot iterate non-struct schema.");
|
||||
}
|
||||
const struct = this.getSchema();
|
||||
const z = struct[4].length;
|
||||
let it = struct[anno.it];
|
||||
if (it && z === it.length) {
|
||||
yield* it;
|
||||
return;
|
||||
}
|
||||
it = Array(z);
|
||||
for (let i = 0; i < z; ++i) {
|
||||
const k = struct[4][i];
|
||||
const v = member([struct[5][i], 0], k);
|
||||
yield (it[i] = [k, v]);
|
||||
}
|
||||
struct[anno.it] = it;
|
||||
}
|
||||
}
|
||||
function member(memberSchema, memberName) {
|
||||
if (memberSchema instanceof NormalizedSchema) {
|
||||
return Object.assign(memberSchema, {
|
||||
memberName,
|
||||
_isMemberSchema: true,
|
||||
});
|
||||
}
|
||||
const internalCtorAccess = NormalizedSchema;
|
||||
return new internalCtorAccess(memberSchema, memberName);
|
||||
}
|
||||
const isMemberSchema = (sc) => Array.isArray(sc) && sc.length === 2;
|
||||
const isStaticSchema = (sc) => Array.isArray(sc) && sc.length >= 5;
|
||||
|
||||
class SimpleSchema extends Schema {
|
||||
static symbol = Symbol.for("@smithy/sim");
|
||||
name;
|
||||
schemaRef;
|
||||
traits;
|
||||
symbol = SimpleSchema.symbol;
|
||||
}
|
||||
const sim = (namespace, name, schemaRef, traits) => Schema.assign(new SimpleSchema(), {
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
schemaRef,
|
||||
});
|
||||
const simAdapter = (namespace, name, traits, schemaRef) => Schema.assign(new SimpleSchema(), {
|
||||
name,
|
||||
namespace,
|
||||
traits,
|
||||
schemaRef,
|
||||
});
|
||||
|
||||
const SCHEMA = {
|
||||
BLOB: 0b0001_0101,
|
||||
STREAMING_BLOB: 0b0010_1010,
|
||||
BOOLEAN: 0b0000_0010,
|
||||
STRING: 0b0000_0000,
|
||||
NUMERIC: 0b0000_0001,
|
||||
BIG_INTEGER: 0b0001_0001,
|
||||
BIG_DECIMAL: 0b0001_0011,
|
||||
DOCUMENT: 0b0000_1111,
|
||||
TIMESTAMP_DEFAULT: 0b0000_0100,
|
||||
TIMESTAMP_DATE_TIME: 0b0000_0101,
|
||||
TIMESTAMP_HTTP_DATE: 0b0000_0110,
|
||||
TIMESTAMP_EPOCH_SECONDS: 0b0000_0111,
|
||||
LIST_MODIFIER: 0b0100_0000,
|
||||
MAP_MODIFIER: 0b1000_0000,
|
||||
};
|
||||
|
||||
class TypeRegistry {
|
||||
namespace;
|
||||
schemas;
|
||||
exceptions;
|
||||
static registries = new Map();
|
||||
constructor(namespace, schemas = new Map(), exceptions = new Map()) {
|
||||
this.namespace = namespace;
|
||||
this.schemas = schemas;
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
static for(namespace) {
|
||||
if (!TypeRegistry.registries.has(namespace)) {
|
||||
TypeRegistry.registries.set(namespace, new TypeRegistry(namespace));
|
||||
}
|
||||
return TypeRegistry.registries.get(namespace);
|
||||
}
|
||||
copyFrom(other) {
|
||||
const { schemas, exceptions } = this;
|
||||
for (const [k, v] of other.schemas) {
|
||||
if (!schemas.has(k)) {
|
||||
schemas.set(k, v);
|
||||
}
|
||||
}
|
||||
for (const [k, v] of other.exceptions) {
|
||||
if (!exceptions.has(k)) {
|
||||
exceptions.set(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
register(shapeId, schema) {
|
||||
const qualifiedName = this.normalizeShapeId(shapeId);
|
||||
for (const r of [this, TypeRegistry.for(qualifiedName.split("#")[0])]) {
|
||||
r.schemas.set(qualifiedName, schema);
|
||||
}
|
||||
}
|
||||
getSchema(shapeId) {
|
||||
const id = this.normalizeShapeId(shapeId);
|
||||
if (!this.schemas.has(id)) {
|
||||
if (!shapeId.includes("#")) {
|
||||
const suffix = "#" + shapeId;
|
||||
const candidates = [];
|
||||
for (const [shapeId, schema] of this.schemas.entries()) {
|
||||
if (shapeId.endsWith(suffix)) {
|
||||
candidates.push(schema);
|
||||
}
|
||||
}
|
||||
if (candidates.length === 1) {
|
||||
return candidates[0];
|
||||
}
|
||||
}
|
||||
throw new Error(`@smithy/core/schema - schema not found for ${id}`);
|
||||
}
|
||||
return this.schemas.get(id);
|
||||
}
|
||||
registerError(es, ctor) {
|
||||
const $error = es;
|
||||
const ns = $error[1];
|
||||
for (const r of [this, TypeRegistry.for(ns)]) {
|
||||
r.schemas.set(ns + "#" + $error[2], $error);
|
||||
r.exceptions.set($error, ctor);
|
||||
}
|
||||
}
|
||||
getErrorCtor(es) {
|
||||
const $error = es;
|
||||
if (this.exceptions.has($error)) {
|
||||
return this.exceptions.get($error);
|
||||
}
|
||||
const registry = TypeRegistry.for($error[1]);
|
||||
return registry.exceptions.get($error);
|
||||
}
|
||||
getBaseException() {
|
||||
for (const exceptionKey of this.exceptions.keys()) {
|
||||
if (Array.isArray(exceptionKey)) {
|
||||
const [, ns, name] = exceptionKey;
|
||||
const id = ns + "#" + name;
|
||||
if (id.startsWith("smithy.ts.sdk.synthetic.") && id.endsWith("ServiceException")) {
|
||||
return exceptionKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
find(predicate) {
|
||||
for (const schema of this.schemas.values()) {
|
||||
if (predicate(schema)) {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
clear() {
|
||||
this.schemas.clear();
|
||||
this.exceptions.clear();
|
||||
}
|
||||
normalizeShapeId(shapeId) {
|
||||
if (shapeId.includes("#")) {
|
||||
return shapeId;
|
||||
}
|
||||
return this.namespace + "#" + shapeId;
|
||||
}
|
||||
}
|
||||
|
||||
exports.ErrorSchema = ErrorSchema;
|
||||
exports.ListSchema = ListSchema;
|
||||
exports.MapSchema = MapSchema;
|
||||
exports.NormalizedSchema = NormalizedSchema;
|
||||
exports.OperationSchema = OperationSchema;
|
||||
exports.SCHEMA = SCHEMA;
|
||||
exports.Schema = Schema;
|
||||
exports.SimpleSchema = SimpleSchema;
|
||||
exports.StructureSchema = StructureSchema;
|
||||
exports.TypeRegistry = TypeRegistry;
|
||||
exports.deref = deref;
|
||||
exports.deserializerMiddlewareOption = deserializerMiddlewareOption;
|
||||
exports.error = error;
|
||||
exports.getSchemaSerdePlugin = getSchemaSerdePlugin;
|
||||
exports.isStaticSchema = isStaticSchema;
|
||||
exports.list = list;
|
||||
exports.map = map;
|
||||
exports.op = op;
|
||||
exports.operation = operation;
|
||||
exports.serializerMiddlewareOption = serializerMiddlewareOption;
|
||||
exports.sim = sim;
|
||||
exports.simAdapter = simAdapter;
|
||||
exports.simpleSchemaCacheN = simpleSchemaCacheN;
|
||||
exports.simpleSchemaCacheS = simpleSchemaCacheS;
|
||||
exports.struct = struct;
|
||||
exports.traitsCache = traitsCache;
|
||||
exports.translateTraits = translateTraits;
|
||||
+1405
File diff suppressed because it is too large
Load Diff
+1789
File diff suppressed because it is too large
Load Diff
+1405
File diff suppressed because it is too large
Load Diff
+184
@@ -0,0 +1,184 @@
|
||||
'use strict';
|
||||
|
||||
var types = require('@smithy/types');
|
||||
|
||||
const getSmithyContext = (context) => context[types.SMITHY_CONTEXT_KEY] || (context[types.SMITHY_CONTEXT_KEY] = {});
|
||||
|
||||
class HttpRequest {
|
||||
method;
|
||||
protocol;
|
||||
hostname;
|
||||
port;
|
||||
path;
|
||||
query;
|
||||
headers;
|
||||
username;
|
||||
password;
|
||||
fragment;
|
||||
body;
|
||||
constructor(options) {
|
||||
this.method = options.method || "GET";
|
||||
this.hostname = options.hostname || "localhost";
|
||||
this.port = options.port;
|
||||
this.query = options.query || {};
|
||||
this.headers = options.headers || {};
|
||||
this.body = options.body;
|
||||
this.protocol = options.protocol
|
||||
? options.protocol.slice(-1) !== ":"
|
||||
? `${options.protocol}:`
|
||||
: options.protocol
|
||||
: "https:";
|
||||
this.path = options.path ? (options.path.charAt(0) !== "/" ? `/${options.path}` : options.path) : "/";
|
||||
this.username = options.username;
|
||||
this.password = options.password;
|
||||
this.fragment = options.fragment;
|
||||
}
|
||||
static clone(request) {
|
||||
const cloned = new HttpRequest({
|
||||
...request,
|
||||
headers: { ...request.headers },
|
||||
});
|
||||
if (cloned.query) {
|
||||
cloned.query = cloneQuery(cloned.query);
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
static isInstance(request) {
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
const req = request;
|
||||
return ("method" in req &&
|
||||
"protocol" in req &&
|
||||
"hostname" in req &&
|
||||
"path" in req &&
|
||||
typeof req["query"] === "object" &&
|
||||
typeof req["headers"] === "object");
|
||||
}
|
||||
clone() {
|
||||
return HttpRequest.clone(this);
|
||||
}
|
||||
}
|
||||
function cloneQuery(query) {
|
||||
return Object.keys(query).reduce((carry, paramName) => {
|
||||
const param = query[paramName];
|
||||
return {
|
||||
...carry,
|
||||
[paramName]: Array.isArray(param) ? [...param] : param,
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
|
||||
class HttpResponse {
|
||||
statusCode;
|
||||
reason;
|
||||
headers;
|
||||
body;
|
||||
constructor(options) {
|
||||
this.statusCode = options.statusCode;
|
||||
this.reason = options.reason;
|
||||
this.headers = options.headers || {};
|
||||
this.body = options.body;
|
||||
}
|
||||
static isInstance(response) {
|
||||
if (!response)
|
||||
return false;
|
||||
const resp = response;
|
||||
return typeof resp.statusCode === "number" && typeof resp.headers === "object";
|
||||
}
|
||||
}
|
||||
|
||||
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 isValidHostname(hostname) {
|
||||
const hostPattern = /^[a-z0-9][a-z0-9\.\-]*[a-z0-9]$/;
|
||||
return hostPattern.test(hostname);
|
||||
}
|
||||
|
||||
const normalizeProvider = (input) => {
|
||||
if (typeof input === "function")
|
||||
return input;
|
||||
const promisified = Promise.resolve(input);
|
||||
return () => promisified;
|
||||
};
|
||||
|
||||
function parseQueryString(querystring) {
|
||||
const query = {};
|
||||
querystring = querystring.replace(/^\?/, "");
|
||||
if (querystring) {
|
||||
for (const pair of querystring.split("&")) {
|
||||
let [key, value = null] = pair.split("=");
|
||||
key = decodeURIComponent(key);
|
||||
if (value) {
|
||||
value = decodeURIComponent(value);
|
||||
}
|
||||
if (!(key in query)) {
|
||||
query[key] = value;
|
||||
}
|
||||
else if (Array.isArray(query[key])) {
|
||||
query[key].push(value);
|
||||
}
|
||||
else {
|
||||
query[key] = [query[key], value];
|
||||
}
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
const parseUrl = (url) => {
|
||||
if (typeof url === "string") {
|
||||
return parseUrl(new URL(url));
|
||||
}
|
||||
const { hostname, pathname, port, protocol, search } = url;
|
||||
let query;
|
||||
if (search) {
|
||||
query = parseQueryString(search);
|
||||
}
|
||||
return {
|
||||
hostname,
|
||||
port: port ? parseInt(port) : undefined,
|
||||
protocol,
|
||||
path: pathname,
|
||||
query,
|
||||
};
|
||||
};
|
||||
|
||||
const toEndpointV1 = (endpoint) => {
|
||||
if (typeof endpoint === "object") {
|
||||
if ("url" in endpoint) {
|
||||
const v1Endpoint = 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 parseUrl(endpoint);
|
||||
};
|
||||
|
||||
exports.HttpRequest = HttpRequest;
|
||||
exports.HttpResponse = HttpResponse;
|
||||
exports.getSmithyContext = getSmithyContext;
|
||||
exports.isValidHostLabel = isValidHostLabel;
|
||||
exports.isValidHostname = isValidHostname;
|
||||
exports.normalizeProvider = normalizeProvider;
|
||||
exports.parseQueryString = parseQueryString;
|
||||
exports.parseUrl = parseUrl;
|
||||
exports.toEndpointV1 = toEndpointV1;
|
||||
Reference in New Issue
Block a user