blockstack
Version:
The Blockstack Javascript library for authentication, identity, and storage.
364 lines • 12.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const logger_1 = require("./logger");
const errors_1 = require("./errors");
/**
* @ignore
*/
exports.BLOCKSTACK_HANDLER = 'blockstack';
/**
* Time
* @private
* @ignore
*/
function nextYear() {
return new Date(new Date().setFullYear(new Date().getFullYear() + 1));
}
exports.nextYear = nextYear;
/**
* Time
* @private
* @ignore
*/
function nextMonth() {
return new Date(new Date().setMonth(new Date().getMonth() + 1));
}
exports.nextMonth = nextMonth;
/**
* Time
* @private
* @ignore
*/
function nextHour() {
return new Date(new Date().setHours(new Date().getHours() + 1));
}
exports.nextHour = nextHour;
/**
* Converts megabytes to bytes. Returns 0 if the input is not a finite number.
* @ignore
*/
function megabytesToBytes(megabytes) {
if (!Number.isFinite(megabytes)) {
return 0;
}
return Math.floor(megabytes * 1024 * 1024);
}
exports.megabytesToBytes = megabytesToBytes;
/**
* Calculate the AES-CBC ciphertext output byte length a given input length.
* AES has a fixed block size of 16-bytes regardless key size.
* @ignore
*/
function getAesCbcOutputLength(inputByteLength) {
// AES-CBC block mode rounds up to the next block size.
const cipherTextLength = (Math.floor(inputByteLength / 16) + 1) * 16;
return cipherTextLength;
}
exports.getAesCbcOutputLength = getAesCbcOutputLength;
/**
* Calculate the base64 encoded string length for a given input length.
* This is equivalent to the byte length when the string is ASCII or UTF8-8
* encoded.
* @param number
*/
function getBase64OutputLength(inputByteLength) {
const encodedLength = (Math.ceil(inputByteLength / 3) * 4);
return encodedLength;
}
exports.getBase64OutputLength = getBase64OutputLength;
/**
* Query Strings
* @private
* @ignore
*/
function updateQueryStringParameter(uri, key, value) {
const re = new RegExp(`([?&])${key}=.*?(&|$)`, 'i');
const separator = uri.indexOf('?') !== -1 ? '&' : '?';
if (uri.match(re)) {
return uri.replace(re, `$1${key}=${value}$2`);
}
else {
return `${uri}${separator}${key}=${value}`;
}
}
exports.updateQueryStringParameter = updateQueryStringParameter;
/**
* Versioning
* @param {string} v1 - the left half of the version inequality
* @param {string} v2 - right half of the version inequality
* @returns {bool} iff v1 >= v2
* @private
* @ignore
*/
function isLaterVersion(v1, v2) {
if (v1 === undefined) {
v1 = '0.0.0';
}
if (v2 === undefined) {
v2 = '0.0.0';
}
const v1tuple = v1.split('.').map(x => parseInt(x, 10));
const v2tuple = v2.split('.').map(x => parseInt(x, 10));
for (let index = 0; index < v2.length; index++) {
if (index >= v1.length) {
v2tuple.push(0);
}
if (v1tuple[index] < v2tuple[index]) {
return false;
}
}
return true;
}
exports.isLaterVersion = isLaterVersion;
/**
* UUIDs
* @private
* @ignore
*/
function makeUUID4() {
let d = new Date().getTime();
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
d += performance.now(); // use high-precision timer if available
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
exports.makeUUID4 = makeUUID4;
/**
* Checks if both urls pass the same origin check & are absolute
* @param {[type]} uri1 first uri to check
* @param {[type]} uri2 second uri to check
* @return {Boolean} true if they pass the same origin check
* @private
* @ignore
*/
function isSameOriginAbsoluteUrl(uri1, uri2) {
try {
// The globally scoped WHATWG `URL` class is available in modern browsers and
// NodeJS v10 or higher. In older NodeJS versions it must be required from the
// `url` module.
let parseUrl;
if (typeof URL !== 'undefined') {
parseUrl = url => new URL(url);
}
else {
try {
// eslint-disable-next-line import/no-nodejs-modules, global-require
const nodeUrl = require('url').URL;
parseUrl = url => new nodeUrl(url);
}
catch (error) {
console.log(error);
console.error('Global URL class is not available');
}
}
const parsedUri1 = parseUrl(uri1);
const parsedUri2 = parseUrl(uri2);
const port1 = parseInt(parsedUri1.port || '0', 10) | 0 || (parsedUri1.protocol === 'https:' ? 443 : 80);
const port2 = parseInt(parsedUri2.port || '0', 10) | 0 || (parsedUri2.protocol === 'https:' ? 443 : 80);
const match = {
scheme: parsedUri1.protocol === parsedUri2.protocol,
hostname: parsedUri1.hostname === parsedUri2.hostname,
port: port1 === port2,
absolute: (uri1.includes('http://') || uri1.includes('https://'))
&& (uri2.includes('http://') || uri2.includes('https://'))
};
return match.scheme && match.hostname && match.port && match.absolute;
}
catch (error) {
console.log(error);
console.log('Parsing error in same URL origin check');
// Parse error
return false;
}
}
exports.isSameOriginAbsoluteUrl = isSameOriginAbsoluteUrl;
/**
* Returns the global scope `Window`, `WorkerGlobalScope`, or `NodeJS.Global` if available in the
* currently executing environment.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self
* @see https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/self
* @see https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
*
* This could be switched to `globalThis` once it is standardized and widely available.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis
* @ignore
*/
function getGlobalScope() {
if (typeof self !== 'undefined') {
return self;
}
if (typeof window !== 'undefined') {
return window;
}
// This function is meant to be called when accessing APIs that are typically only available in
// web-browser/DOM environments, but we also want to support situations where running in Node.js
// environment, and a polyfill was added to the Node.js `global` object scope without adding the
// `window` global object as well.
if (typeof global !== 'undefined') {
return global;
}
throw new Error('Unexpected runtime environment - no supported global scope (`window`, `self`, `global`) available');
}
exports.getGlobalScope = getGlobalScope;
function getAPIUsageErrorMessage(scopeObject, apiName, usageDesc) {
if (usageDesc) {
return `Use of '${usageDesc}' requires \`${apiName}\` which is unavailable on the '${scopeObject}' object within the currently executing environment.`;
}
else {
return `\`${apiName}\` is unavailable on the '${scopeObject}' object within the currently executing environment.`;
}
}
/**
* Returns an object from the global scope (`Window` or `WorkerGlobalScope`) if it
* is available within the currently executing environment.
* When executing within the Node.js runtime these APIs are unavailable and will be
* `undefined` unless the API is provided via polyfill.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self
* @ignore
*/
function getGlobalObject(name, { throwIfUnavailable, usageDesc, returnEmptyObject } = {}) {
let globalScope;
try {
globalScope = getGlobalScope();
if (globalScope) {
const obj = globalScope[name];
if (obj) {
return obj;
}
}
}
catch (error) {
logger_1.Logger.error(`Error getting object '${name}' from global scope '${globalScope}': ${error}`);
}
if (throwIfUnavailable) {
const errMsg = getAPIUsageErrorMessage(globalScope, name.toString(), usageDesc);
logger_1.Logger.error(errMsg);
throw new Error(errMsg);
}
if (returnEmptyObject) {
return {};
}
return undefined;
}
exports.getGlobalObject = getGlobalObject;
/**
* Returns a specified subset of objects from the global scope (`Window` or `WorkerGlobalScope`)
* if they are available within the currently executing environment.
* When executing within the Node.js runtime these APIs are unavailable will be `undefined`
* unless the API is provided via polyfill.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/self
* @ignore
*/
function getGlobalObjects(names, { throwIfUnavailable, usageDesc, returnEmptyObject } = {}) {
let globalScope;
try {
globalScope = getGlobalScope();
}
catch (error) {
logger_1.Logger.error(`Error getting global scope: ${error}`);
if (throwIfUnavailable) {
const errMsg = getAPIUsageErrorMessage(globalScope, names[0].toString(), usageDesc);
logger_1.Logger.error(errMsg);
throw errMsg;
}
else if (returnEmptyObject) {
globalScope = {};
}
}
const result = {};
for (let i = 0; i < names.length; i++) {
const name = names[i];
try {
if (globalScope) {
const obj = globalScope[name];
if (obj) {
result[name] = obj;
}
else if (throwIfUnavailable) {
const errMsg = getAPIUsageErrorMessage(globalScope, name.toString(), usageDesc);
logger_1.Logger.error(errMsg);
throw new Error(errMsg);
}
else if (returnEmptyObject) {
result[name] = {};
}
}
}
catch (error) {
if (throwIfUnavailable) {
const errMsg = getAPIUsageErrorMessage(globalScope, name.toString(), usageDesc);
logger_1.Logger.error(errMsg);
throw new Error(errMsg);
}
}
}
return result;
}
exports.getGlobalObjects = getGlobalObjects;
function getGaiaErrorResponse(response) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let responseMsg = '';
let responseJson;
try {
responseMsg = yield response.text();
try {
responseJson = JSON.parse(responseMsg);
}
catch (error) {
// Use text instead
}
}
catch (error) {
logger_1.Logger.debug(`Error getting bad http response text: ${error}`);
}
const status = response.status;
const statusText = response.statusText;
const body = responseJson || responseMsg;
return { status, statusText, body };
});
}
/**
* Returns a BlockstackError correlating to the given HTTP response,
* with the provided errorMsg. Throws if the HTTP response is 'ok'.
*/
function getBlockstackErrorFromResponse(response, errorMsg, hubConfig) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (response.ok) {
throw new Error('Cannot get a BlockstackError from a valid response.');
}
const gaiaResponse = yield getGaiaErrorResponse(response);
if (gaiaResponse.status === 401) {
return new errors_1.ValidationError(errorMsg, gaiaResponse);
}
else if (gaiaResponse.status === 402) {
return new errors_1.NotEnoughProofError(errorMsg, gaiaResponse);
}
else if (gaiaResponse.status === 403) {
return new errors_1.BadPathError(errorMsg, gaiaResponse);
}
else if (gaiaResponse.status === 404) {
throw new errors_1.DoesNotExist(errorMsg, gaiaResponse);
}
else if (gaiaResponse.status === 409) {
return new errors_1.ConflictError(errorMsg, gaiaResponse);
}
else if (gaiaResponse.status === 412) {
return new errors_1.PreconditionFailedError(errorMsg, gaiaResponse);
}
else if (gaiaResponse.status === 413) {
const maxBytes = megabytesToBytes((_a = hubConfig) === null || _a === void 0 ? void 0 : _a.max_file_upload_size_megabytes);
return new errors_1.PayloadTooLargeError(errorMsg, gaiaResponse, maxBytes);
}
else {
return new Error(errorMsg);
}
});
}
exports.getBlockstackErrorFromResponse = getBlockstackErrorFromResponse;
//# sourceMappingURL=utils.js.map