UNPKG

parse

Version:
342 lines (338 loc) 12.6 kB
"use strict"; var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property"); var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault"); _Object$defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _endsWith = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/ends-with")); var _slice = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/slice")); var _startsWith = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/starts-with")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise")); var _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify")); var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes")); var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each")); var _setTimeout2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-timeout")); var _uuid = _interopRequireDefault(require("./uuid")); var _CoreManager = _interopRequireDefault(require("./CoreManager")); var _ParseError = _interopRequireDefault(require("./ParseError")); var _promiseUtils = require("./promiseUtils"); var _Xhr = require("./Xhr.weapp"); /* global XMLHttpRequest, XDomainRequest */ (0, _Xhr.polyfillFetch)(); let useXDomainRequest = false; // @ts-ignore if (typeof XDomainRequest !== 'undefined' && !('withCredentials' in new XMLHttpRequest())) { useXDomainRequest = true; } function getPath(base, pathname) { if ((0, _endsWith.default)(base).call(base, '/')) { base = (0, _slice.default)(base).call(base, 0, -1); } if (!(0, _startsWith.default)(pathname).call(pathname, '/')) { pathname = '/' + pathname; } return base + pathname; } function ajaxIE9(method, url, data, _headers, options) { return new _promise.default((resolve, reject) => { // @ts-ignore const xdr = new XDomainRequest(); xdr.onload = function () { let response; try { response = JSON.parse(xdr.responseText); } catch (e) { reject(e); } if (response) { resolve({ response }); } }; xdr.onerror = xdr.ontimeout = function () { // Let's fake a real error message. const fakeResponse = { responseText: (0, _stringify.default)({ code: _ParseError.default.X_DOMAIN_REQUEST, error: "IE's XDomainRequest does not supply error info." }) }; reject(fakeResponse); }; xdr.onprogress = function () { if (options && typeof options.progress === 'function') { options.progress(xdr.responseText); } }; xdr.open(method, url); xdr.send(data); // @ts-ignore if (options && typeof options.requestTask === 'function') { // @ts-ignore options.requestTask(xdr); } }); } const RESTController = { async ajax(method, url, data, headers, options) { var _context; if (useXDomainRequest) { return ajaxIE9(method, url, data, headers, options); } if (typeof fetch !== 'function') { throw new Error('Cannot make a request: Fetch API not found.'); } const promise = (0, _promiseUtils.resolvingPromise)(); const isIdempotent = _CoreManager.default.get('IDEMPOTENCY') && (0, _includes.default)(_context = ['POST', 'PUT']).call(_context, method); const requestId = isIdempotent ? (0, _uuid.default)() : ''; let attempts = 0; const dispatch = async function () { const controller = new AbortController(); const { signal } = controller; headers = headers || {}; if (typeof headers['Content-Type'] !== 'string') { headers['Content-Type'] = 'text/plain'; // Avoid pre-flight } if (_CoreManager.default.get('IS_NODE')) { headers['User-Agent'] = 'Parse/' + _CoreManager.default.get('VERSION') + ' (NodeJS ' + process.versions.node + ')'; } if (isIdempotent) { headers['X-Parse-Request-Id'] = requestId; } const customHeaders = _CoreManager.default.get('REQUEST_HEADERS'); for (const key in customHeaders) { headers[key] = customHeaders[key]; } // @ts-ignore if (options && typeof options.requestTask === 'function') { // @ts-ignore options.requestTask(controller); } try { var _context3; const fetchOptions = { method, headers, signal, redirect: 'manual' }; if (data) { fetchOptions.body = data; } const response = await fetch(url, fetchOptions); const { status } = response; if (status >= 200 && status < 300) { var _context2; let result; const responseHeaders = {}; const availableHeaders = response.headers.get('access-control-expose-headers') || ''; (0, _forEach.default)(_context2 = availableHeaders.split(', ')).call(_context2, header => { if (header && response.headers.has(header)) { responseHeaders[header] = response.headers.get(header); } }); if (options && typeof options.progress === 'function' && response.body) { const reader = response.body.getReader(); const length = +response.headers.get('Content-Length') || 0; if (length === 0) { options.progress(null, null, null); result = await response.json(); } else { let recieved = 0; const chunks = []; while (true) { const { done, value } = await reader.read(); if (done) { break; } chunks.push(value); recieved += value?.length || 0; options.progress(recieved / length, recieved, length); } const body = new Uint8Array(recieved); let offset = 0; for (const chunk of chunks) { body.set(chunk, offset); offset += chunk.length; } const jsonString = new TextDecoder().decode(body); result = JSON.parse(jsonString); } } else { result = await response.json(); } promise.resolve({ status, response: result, headers: responseHeaders }); } else if (status >= 400 && status < 500) { const error = await response.json(); promise.reject(error); } else if ((0, _includes.default)(_context3 = [301, 302, 303, 307, 308]).call(_context3, status)) { const location = response.headers.get('location'); promise.resolve({ status, location, method: status === 303 ? 'GET' : method, dropBody: status === 303 }); } else if (status >= 500 || status === 0) { // retry on 5XX or library error if (++attempts < _CoreManager.default.get('REQUEST_ATTEMPT_LIMIT')) { // Exponentially-growing random delay const delay = Math.round(Math.random() * 125 * Math.pow(2, attempts)); (0, _setTimeout2.default)(dispatch, delay); } else if (status === 0) { promise.reject('Unable to connect to the Parse API'); } else { // After the retry limit is reached, fail const error = await response.json(); promise.reject(error); } } else { promise.reject(response); } } catch (error) { if (error.name === 'AbortError') { promise.resolve({ response: { results: [] }, status: 0 }); } else if (error.cause?.code === 'ECONNREFUSED') { promise.reject('Unable to connect to the Parse API'); } else { promise.reject(error); } } }; dispatch(); return promise; }, request(method, path, data, options) { options = options || {}; const url = getPath(_CoreManager.default.get('SERVER_URL'), path); const payload = {}; if (data && typeof data === 'object') { for (const k in data) { payload[k] = data[k]; } } // Add context const context = options.context; if (context !== undefined) { payload._context = context; } if (method !== 'POST') { payload._method = method; method = 'POST'; } payload._ApplicationId = _CoreManager.default.get('APPLICATION_ID'); const jsKey = _CoreManager.default.get('JAVASCRIPT_KEY'); if (jsKey) { payload._JavaScriptKey = jsKey; } payload._ClientVersion = _CoreManager.default.get('VERSION'); let useMasterKey = options.useMasterKey; if (typeof useMasterKey === 'undefined') { useMasterKey = _CoreManager.default.get('USE_MASTER_KEY'); } if (useMasterKey) { if (_CoreManager.default.get('MASTER_KEY')) { delete payload._JavaScriptKey; payload._MasterKey = _CoreManager.default.get('MASTER_KEY'); } else { throw new Error('Cannot use the Master Key, it has not been provided.'); } } if (options.useMaintenanceKey) { payload._MaintenanceKey = _CoreManager.default.get('MAINTENANCE_KEY'); } if (_CoreManager.default.get('FORCE_REVOCABLE_SESSION')) { payload._RevocableSession = '1'; } const installationId = options.installationId; let installationIdPromise; if (installationId && typeof installationId === 'string') { installationIdPromise = _promise.default.resolve(installationId); } else { const installationController = _CoreManager.default.getInstallationController(); installationIdPromise = installationController.currentInstallationId(); } return installationIdPromise.then(iid => { payload._InstallationId = iid; const userController = _CoreManager.default.getUserController(); if (options && typeof options.sessionToken === 'string') { return _promise.default.resolve(options.sessionToken); } else if (userController) { return userController.currentUserAsync().then(user => { if (user) { return _promise.default.resolve(user.getSessionToken()); } return _promise.default.resolve(null); }); } return _promise.default.resolve(null); }).then(token => { if (token) { payload._SessionToken = token; } const payloadString = (0, _stringify.default)(payload); return RESTController.ajax(method, url, payloadString, {}, options).then(async result => { if (result.location) { let newURL = getPath(result.location, path); let newMethod = result.method; let newBody = result.dropBody ? undefined : payloadString; // Follow up to 5 redirects to avoid loops for (let i = 0; i < 5; i += 1) { const r = await RESTController.ajax(newMethod, newURL, newBody, {}, options); if (!r.location) { result = r; break; } newURL = getPath(r.location, path); newMethod = r.method; newBody = r.dropBody ? undefined : payloadString; } } const { response, status, headers } = result; if (options.returnStatus) { return { ...response, _status: status, _headers: headers }; } else { return response; } }); }).catch(RESTController.handleError); }, handleError(errorJSON) { // Transform the error into an instance of ParseError by trying to parse // the error string as JSON let error; if (errorJSON.code || errorJSON.error || errorJSON.message) { error = new _ParseError.default(errorJSON.code, errorJSON.error || errorJSON.message); } else { error = new _ParseError.default(_ParseError.default.CONNECTION_FAILED, 'XMLHttpRequest failed: ' + (0, _stringify.default)(errorJSON)); } return _promise.default.reject(error); } }; var _default = exports.default = RESTController;