UNPKG

blockstack

Version:

The Blockstack Javascript library for authentication, identity, and storage.

364 lines 12.8 kB
"use strict"; 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