UNPKG

ts-remote-data

Version:

Utility type for working with snapshot views of asynchronously-available data

180 lines (179 loc) 6.59 kB
"use strict"; // COPYRIGHT 2019 BY EXTRAHOP NETWORKS, INC. // // This file is subject to the terms and conditions defined in // file 'LICENSE', which is part of this source code package. Object.defineProperty(exports, "__esModule", { value: true }); // Establish constants for the different not-loaded states. Explicit types are // used so these can be exported via `RemoteData` without being generalized // to `string`. var NOT_ASKED = 'RemoteData::NOT_ASKED'; var LOADING = 'RemoteData::LOADING'; var FAILURE_PROPERTY = 'RemoteData::FAILURE'; var RemoteData = { /** * Initial state for remote data. The client has not yet sent a request * to the server. */ NOT_ASKED: NOT_ASKED, /** * State when the client has started a request but not yet received a * response. */ LOADING: LOADING, /** * Create a failure object with no associated data. */ fail: function () { return RemoteData.failWith(undefined); }, /** * State when the client has received a conclusive response from the server * that was an error. If a non-initial update fails, it is not required * that consumers of `RemoteData` overwrite the previous data with the * failure, but they may do so if it is contextually appropriate. */ failWith: function (error) { var _a; return (_a = {}, _a[FAILURE_PROPERTY] = true, _a.error = error, _a); }, /** * State when the client has received a conclusive response from the server * that was an error. If a non-initial update fails, it is not required * that consumers of `RemoteData` overwrite the previous data with the * failure, but they may do so if it is contextually appropriate. */ isFailure: function (rd) { return typeof rd === 'object' && rd !== null && Object.prototype.hasOwnProperty.bind(rd, FAILURE_PROPERTY)(); }, /** * Check if some remote data is available. This function acts as a type * guard; if it returns `true` then `remoteData` can now be used as a `T`. */ isReady: function (remoteData) { return RemoteData.isSettled(remoteData) && !RemoteData.isFailure(remoteData); }, /** * Check if some remote data is ready or in the failure state. The term * "settled" comes from Promises, where it means "fulfilled or rejected". * This function acts as a type guard. */ isSettled: function (remoteData) { return remoteData !== NOT_ASKED && remoteData !== LOADING; }, /** * Check if some remote data is ready; if so return it, otherwise return * `undefined`. This can be used with `||` to create a short-circuiting * default. */ asReady: function (remoteData) { return RemoteData.isReady(remoteData) ? remoteData : undefined; }, /** * Check if some remote data has failed; if so, return the failure, * otherwise return `undefined`. */ asFailure: function (rd) { return RemoteData.isFailure(rd) ? rd : undefined; }, /** * Check if some remote data has settled; if so return the value or the * failure. The term "settled" come from Promises, where it means * "fulfilled or rejected". */ asSettled: function (rd) { return RemoteData.isSettled(rd) ? rd : undefined; }, /** * Get the value of some remote data if available, otherwise return a * specified fallback value. */ getOr: function (remoteData, fallback) { return RemoteData.isReady(remoteData) ? remoteData : fallback; }, /** * Get the value of some remote data if available, otherwise throw an * exception. * * # Usage * When the caller is certain that code is only reachable after some * remote data is available, the value can be used by writing * `RemoteData.unwrap(rd).someRdMethod();` * * @throws `Error` if `remoteData` is not in the ready state. */ unwrap: function (remoteData) { if (RemoteData.isReady(remoteData)) return remoteData; throw new Error("Attempted to unwrap " + remoteData); }, /** * Return the first `RemoteData` in the 'ready' state. * This is used to kick off an initial data request but to avoid flattening * data on subsequent updates, or to restart a query after a loading failure. */ or: function (lhs, rhs) { return RemoteData.isReady(lhs) ? lhs : rhs; }, /** * Apply a transform function to a remote data if it is ready, otherwise * return the current state. */ map: function (remoteData, mapFn) { return RemoteData.isReady(remoteData) ? mapFn(remoteData) : remoteData; }, /** * Recover from a failure by using a fallback value, otherwise * returning the passed in remote data. */ recover: function (remoteData, fallback) { return RemoteData.isFailure(remoteData) ? fallback : remoteData; }, /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` */ all: all, }; /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` * * @param args An array or tuple of `RemoteData` values. */ // XXX This is not an arrow function because of the overloads. function all() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } // If everything is ready, then return the array if (args.every(RemoteData.isReady)) return args; // If _any_ of the args are in the `failure` state, the whole thing // is a failure. var firstFailure = args.find(RemoteData.isFailure); if (firstFailure) return firstFailure; // If _any_ of the args are in the `LOADING` state and _none_ of the // args are failures, then the whole thing is `LOADING`. if (args.includes(RemoteData.LOADING)) return RemoteData.LOADING; // We have at least one non-ready item, and no items in failure or loading // states. Therefore, we must not have asked for any of the items yet. return RemoteData.NOT_ASKED; } exports.default = RemoteData;