@agnostack/requestd
Version:
Please contact agnoStack via info@agnostack.com for any questions
261 lines • 13.8 kB
JavaScript
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractClient = void 0;
const utils_1 = require("./utils");
const Adapters_1 = require("./Adapters");
const ClientError_1 = require("./ClientError");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sdk_version = require('../package.json').version;
const DEFAULT_DURATION = 15000; // 15 seconds
const MAX_DURATION = 5184000000; // 2 months
// NOTE: this function handles calling authenticate and storage
const authorize = async (options, _fetch, callback, crypto, storage, authorizeConfig) => {
const { storage_key, sdk_version } = authorizeConfig;
let authentication;
const { custom, URIs, auth: { expiration, }, } = options;
const authOptions = Object.assign({ URIs }, custom);
if (storage) {
try {
const now = Date.now();
const _storedCredentials = storage.getItem(storage_key);
const storedCredentials = (0, utils_1.ensureObject)((0, utils_1.safeParse)(_storedCredentials));
const credentials = Object.assign(Object.assign({}, storedCredentials), { isExpired: () => ((storedCredentials.sdk_version == undefined) ||
(storedCredentials.expires == undefined) ||
(storedCredentials.previous == undefined) ||
(storedCredentials.sdk_version !== sdk_version) ||
((storedCredentials.expires !== 0) && (now >= storedCredentials.expires)) ||
(storedCredentials.expires >= (now + MAX_DURATION)) ||
(storedCredentials.previous <= (now - MAX_DURATION))) });
authentication = (!credentials.authentication || credentials.isExpired())
// NOTE: this exposes this.fetch to the implementation of this.authenticate
? await callback.bind({ fetch: _fetch })(authOptions)
: await crypto.decrypt(credentials.authentication);
const expirationDuration = expiration !== null && expiration !== void 0 ? expiration : DEFAULT_DURATION;
if (authentication) {
const updatedCredentials = {
authentication: await crypto.encrypt(authentication),
expires: (expiration === 0) ? expiration : (now + expirationDuration),
previous: now,
sdk_version,
};
await storage.setItem(storage_key, JSON.stringify(updatedCredentials));
}
}
catch (error) {
throw new ClientError_1.ClientError('Authentication Error', { code: 500, error });
}
}
return authentication;
};
class AbstractClient {
constructor(options) {
const { URI, URIs = {
rest: URI,
query: URI,
}, shared_secret, storage_key, application, application: { id: applicationId, version: applicationVersion, installation: installationId, } = {}, headers: _headers, expiration: _expiration, storage: _storage, crypto: _crypto, disallow: _disallow = [], fetch: _fetch } = options, _custom = __rest(options, ["URI", "URIs", "shared_secret", "storage_key", "application", "application", "headers", "expiration", "storage", "crypto", "disallow", "fetch"]);
this.fetch = _fetch || globalThis.fetch;
this.clientType = this.constructor.name;
this.application = application;
this.sharedSecret = shared_secret;
this.storageKey = storage_key || [this.clientType, applicationId, applicationVersion, installationId].filter(utils_1.stringNotEmpty).join('-');
this.storage = _storage || Adapters_1.FactoryStorage.getInstance();
this.crypto = _crypto;
const custom = _custom;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const expiration = _expiration !== null && _expiration !== void 0 ? _expiration : DEFAULT_DURATION;
const _a = (0, utils_1.ensureObject)(_headers), { rest: restHeaders = {}, query: queryHeaders = {} } = _a, otherHeaders = __rest(_a, ["rest", "query"]);
const headers = Object.entries((0, utils_1.ensureObject)(otherHeaders)).reduce((_a, _b) => {
var { rest: previousRest, query: previousQuery } = _a, previousHeaders = __rest(_a, ["rest", "query"]);
var _c = __read(_b, 2), key = _c[0], value = _c[1];
return (Object.assign(Object.assign({}, previousHeaders), URIs[key] ? {
[key]: Object.assign(Object.assign({}, previousHeaders[key]), (0, utils_1.ensureObject)(value)),
rest: previousRest,
query: previousQuery,
} : {
rest: Object.assign(Object.assign({}, previousRest), { [key]: value }),
query: Object.assign(Object.assign({}, previousQuery), { [key]: value })
}));
}, { rest: restHeaders, query: queryHeaders });
const verbs = utils_1.DEFAULT_VERBS.filter((defaultVerb) => !_disallow.includes(defaultVerb));
this.checkVerb = (verb) => verbs.includes(verb);
this.options = {
custom,
URIs,
verbs,
auth: {
expiration,
headers,
},
};
}
initializeCrypto(config) {
return Promise.resolve(Adapters_1.FactoryCrypto.getInstance(config));
}
unAuthenticate() {
if (this.storage) {
this.storage.removeItem(this.storageKey);
}
}
async request({ path, wrapped, authorization, basePath: _basePath, method: _method, headers: requestHeaders = {}, data: requestData = undefined, options: { uriType, } = {}, }) {
var _a, _b;
const { clientType, options: { URIs, auth: { headers: _replacementHeaders = {}, } }, application: { name: applicationName, version: applicationVersion, } = {}, } = this;
const isQuery = _method === 'QUERY';
const method = isQuery ? 'POST' : _method;
const defaultType = isQuery ? 'query' : 'rest';
const basePath = (_a = _basePath !== null && _basePath !== void 0 ? _basePath : URIs[uriType]) !== null && _a !== void 0 ? _a : URIs[defaultType];
const replacementHeaders = (_b = _replacementHeaders[uriType]) !== null && _b !== void 0 ? _b : _replacementHeaders[defaultType];
const url = path ? `${(0, utils_1.removeTrailingSlash)(basePath)}/${(0, utils_1.removeLeadingSlash)(path)}` : basePath;
const body = requestHeaders['Content-Type']
? requestData :
{ body: JSON.stringify(requestData) };
const headers = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ Accept: 'application/json', 'Content-Type': 'application/json', 'X-SDK-Language': 'javascript', 'X-SDK-Client': clientType }, sdk_version && { 'X-SDK-Version': sdk_version }), applicationName && { 'X-Application-Name': (0, utils_1.ensureString)(applicationName) }), applicationVersion && { 'X-Application-Version': (0, utils_1.ensureString)(applicationVersion) }), (0, utils_1.replaceHeaders)(replacementHeaders, authorization)), requestHeaders);
const fetchOptions = Object.assign({ method,
headers }, requestData && Object.assign({}, body));
const response = await this.fetch(url, fetchOptions);
if (!response) {
throw new ClientError_1.ClientError('No Response', {
code: 500,
});
}
const responseHeaders = (0, utils_1.convertHeaders)(response.headers);
let data;
let text;
try {
data = await response.clone()
.json()
.catch(async () => {
text = (await response.text());
return (0, utils_1.ensureObject)((0, utils_1.safeParse)(text));
});
// eslint-disable-next-line no-empty
}
catch (_ignore) { }
if (response.status === 204) {
return wrapped
? {
data,
headers: responseHeaders,
} : text;
}
if (response.status >= 400) {
throw new ClientError_1.ClientError(response.statusText, {
code: response.status,
response: {
data,
headers: responseHeaders,
}
});
}
let errors;
let extensions;
if (isQuery) {
const { data: graphData, errors: graphErrors, extensions: graphExtensions } = (0, utils_1.ensureObject)(data);
data = graphData;
if (graphErrors) {
errors = graphErrors;
}
if (graphExtensions) {
extensions = graphExtensions;
}
}
if (!response.ok) {
throw new ClientError_1.ClientError('Failed Request', {
code: response.status,
response: Object.assign(Object.assign({ data, headers: responseHeaders }, errors && { errors }), extensions && { extensions })
});
}
return wrapped
? Object.assign(Object.assign({ data, headers: responseHeaders }, errors && { errors }), extensions && { extensions }) : data;
}
async authorizedRequest(requestOptions) {
const { method } = requestOptions;
if (!this.checkVerb(method)) {
throw new ClientError_1.ClientError(`${method} method not allowed`, { code: 405 });
}
const { storage, storageKey, options: authOptions, } = this;
let cryptoImplementation = this.crypto;
if (!cryptoImplementation) {
cryptoImplementation = await this.initializeCrypto({
shared_secret: this.sharedSecret,
});
if (!cryptoImplementation) {
cryptoImplementation = Adapters_1.FactoryCrypto.getInstance({
shared_secret: this.sharedSecret,
});
}
else {
this.crypto = cryptoImplementation;
}
}
const authorization = await authorize(authOptions, this.fetch, this.authenticate, cryptoImplementation, storage, { storage_key: storageKey, sdk_version });
return this.request(Object.assign(Object.assign({}, requestOptions), { authorization }));
}
// #region Verbs
head(path, wrapped, headers, options) {
var _a;
return this.authorizedRequest(Object.assign({ method: 'HEAD', path,
wrapped, options: Object.assign(Object.assign({}, (_a = this.options) === null || _a === void 0 ? void 0 : _a.custom), options) }, (headers && { headers })));
}
get(path, wrapped, headers, options) {
var _a;
return this.authorizedRequest(Object.assign({ method: 'GET', path,
wrapped, options: Object.assign(Object.assign({}, (_a = this.options) === null || _a === void 0 ? void 0 : _a.custom), options) }, (headers && { headers })));
}
put(path, data, wrapped, headers, options) {
var _a;
return this.authorizedRequest(Object.assign({ method: 'PUT', path,
data,
wrapped, options: Object.assign(Object.assign({}, (_a = this.options) === null || _a === void 0 ? void 0 : _a.custom), options) }, (headers && { headers })));
}
post(path, data, wrapped, headers, options) {
var _a;
return this.authorizedRequest(Object.assign({ method: 'POST', path,
data,
wrapped, options: Object.assign(Object.assign({}, (_a = this.options) === null || _a === void 0 ? void 0 : _a.custom), options) }, (headers && { headers })));
}
patch(path, data, wrapped, headers, options) {
var _a;
return this.authorizedRequest(Object.assign({ method: 'PATCH', path,
data,
wrapped, options: Object.assign(Object.assign({}, (_a = this.options) === null || _a === void 0 ? void 0 : _a.custom), options) }, (headers && { headers })));
}
delete(path, data, wrapped, headers, options) {
var _a;
return this.authorizedRequest(Object.assign(Object.assign({ method: 'DELETE', path,
wrapped, options: Object.assign(Object.assign({}, (_a = this.options) === null || _a === void 0 ? void 0 : _a.custom), options) }, (data && { data })), (headers && { headers })));
}
query(data, wrapped, headers, options) {
var _a;
return this.authorizedRequest(Object.assign({ method: 'QUERY', data,
wrapped, options: Object.assign(Object.assign({}, (_a = this.options) === null || _a === void 0 ? void 0 : _a.custom), options) }, (headers && { headers })));
}
}
exports.AbstractClient = AbstractClient;
//# sourceMappingURL=AbstractClient.js.map