UNPKG

14.2 kBJavaScriptView Raw
1import * as tslib_1 from "tslib";
2import { constFalse, identity, constant } from 'fp-ts/lib/function';
3import { isNone, isSome, none, some, fold as foldO, getShow as getShowOption } from 'fp-ts/lib/Option';
4import { left, right, fold as foldEither } from 'fp-ts/lib/Either';
5import { array } from 'fp-ts/lib/Array';
6import { sign } from 'fp-ts/lib/Ordering';
7import { pipe, pipeable } from 'fp-ts/lib/pipeable';
8import { showNumber } from 'fp-ts/lib/Show';
9export var URI = 'RemoteData';
10//constructors
11export var failure = function (error) { return ({
12 _tag: 'RemoteFailure',
13 error: error,
14}); };
15export var success = function (value) { return ({
16 _tag: 'RemoteSuccess',
17 value: value,
18}); };
19export var pending = {
20 _tag: 'RemotePending',
21 progress: none,
22};
23export var progress = function (progress) { return ({
24 _tag: 'RemotePending',
25 progress: some(progress),
26}); };
27export var initial = {
28 _tag: 'RemoteInitial',
29};
30//filters
31/**
32 * Returns true only if {@link RemoteData} is {@link RemoteFailure}
33 */
34export var isFailure = function (data) { return data._tag === 'RemoteFailure'; };
35/**
36 * Returns true only if {@link RemoteData} is {@link RemoteSuccess}
37 */
38export var isSuccess = function (data) { return data._tag === 'RemoteSuccess'; };
39/**
40 * Returns true only if {@link RemoteData} is {@link RemotePending}
41 */
42export var isPending = function (data) { return data._tag === 'RemotePending'; };
43/**
44 * Returns true only if {@link RemoteData} is {@link RemoteInitial}
45 */
46export var isInitial = function (data) { return data._tag === 'RemoteInitial'; };
47/**
48 * Takes a default value as an argument.
49 * If this {@link RemoteData} is "Left" part it will return default value.
50 * If this {@link RemoteData} is {@link RemoteSuccess} it will return it's value ("wrapped" value, not default value)
51 *
52 * Note: Default value should be the same type as {@link RemoteData} (internal) value, if you want to pass different type as default, use {@link fold}.
53 *
54 * @example
55 * getOrElse(() => 999)(some(1)) // 1
56 * getOrElseValue(() => 999)(initial) // 999
57 */
58export var getOrElse = function (f) { return function (ma) { return (isSuccess(ma) ? ma.value : f()); }; };
59/**
60 * Needed for "unwrap" value from {@link RemoteData} "container".
61 * It applies a function to each case in the data structure.
62 *
63 * @example
64 * const onInitial = "it's initial"
65 * const onPending = "it's pending"
66 * const onFailure = (err) => "it's failure"
67 * const onSuccess = (data) => `${data + 1}`
68 * const f = fold(onInitial, onPending, onFailure, onSuccess)
69 *
70 * f(initial) // "it's initial"
71 * f(pending) // "it's pending"
72 * f(failure(new Error('error text'))) // "it's failure"
73 * f(success(21)) // '22'
74 */
75export var fold = function (onInitial, onPending, onFailure, onSuccess) { return function (ma) {
76 switch (ma._tag) {
77 case 'RemoteInitial': {
78 return onInitial();
79 }
80 case 'RemotePending': {
81 return onPending(ma.progress);
82 }
83 case 'RemoteFailure': {
84 return onFailure(ma.error);
85 }
86 case 'RemoteSuccess': {
87 return onSuccess(ma.value);
88 }
89 }
90}; };
91/**
92 * One more way to fold (unwrap) value from {@link RemoteData}.
93 * `Left` part will return `null`.
94 * {@link RemoteSuccess} will return value.
95 *
96 * For example:
97 *
98 * `success(2).toNullable() will return 2`
99 *
100 * `initial.toNullable() will return null`
101 *
102 * `pending.toNullable() will return null`
103 *
104 * `failure(new Error('error text)).toNullable() will return null`
105 *
106 */
107export var toNullable = function (ma) { return (isSuccess(ma) ? ma.value : null); };
108export var toUndefined = function (ma) { return (isSuccess(ma) ? ma.value : undefined); };
109export function fromOption(option, error) {
110 if (isNone(option)) {
111 return failure(error());
112 }
113 else {
114 return success(option.value);
115 }
116}
117/**
118 * Convert {@link RemoteData} to {@link Option}
119 * `Left` part will be converted to {@link None}.
120 * {@link RemoteSuccess} will be converted to {@link Some}.
121 *
122 * @example
123 * toOption(success(2)) // some(2)
124 * toOption(initial) // none
125 * toOption(pending) // none
126 * toOption(failure(new Error('error text'))) // none
127 */
128export function toOption(data) {
129 return data._tag === 'RemoteSuccess' ? some(data.value) : none;
130}
131/**
132 * Creates {@link RemoteData} from {@link Either}
133 */
134export var fromEither = foldEither(failure, success);
135/**
136 * Convert {@link RemoteData} to `Either`.
137 * `Left` part will be converted to `Left<L>`.
138 * Since {@link RemoteInitial} and {@link RemotePending} do not have `L` values,
139 * you must provide a value of type `L` that will be used to construct
140 * the `Left<L>` for those two cases.
141 * {@link RemoteSuccess} will be converted to `Right<R>`.
142 *
143 * @example:
144 * const f = toEither(
145 * () => new Error('Data not fetched'),
146 * () => new Error('Data is fetching')
147 * )
148 * f(success(2)) // right(2)
149 * f(initial) // right(Error('Data not fetched'))
150 * f(pending) // right(Error('Data is fetching'))
151 * f(failure(new Error('error text'))) // right(Error('error text'))
152 */
153export function toEither(onInitial, onPending) {
154 return function (data) {
155 return pipe(data, fold(function () { return left(onInitial()); }, function () { return left(onPending()); }, left, right));
156 };
157}
158export function fromPredicate(predicate, whenFalse) {
159 return function (a) { return (predicate(a) ? success(a) : failure(whenFalse(a))); };
160}
161/**
162 * Create {@link RemoteData} from {@link ProgressEvent}
163 * @param event
164 */
165export function fromProgressEvent(event) {
166 return progress({
167 loaded: event.loaded,
168 total: event.lengthComputable ? some(event.total) : none,
169 });
170}
171/**
172 * Compare values and returns `true` if they are identical, otherwise returns `false`.
173 * `Left` part will return `false`.
174 * {@link RemoteSuccess} will call {@link Eq.equals}.
175 *
176 * If you want to compare {@link RemoteData}'s values better use {@link getEq} or {@link getOrd} helpers.
177 *
178 */
179export function elem(E) {
180 return function (a, fa) { return fa._tag === 'RemoteSuccess' && E.equals(a, fa.value); };
181}
182/**
183 * Takes a predicate and apply it to {@link RemoteSuccess} value.
184 * `Left` part will return `false`.
185 */
186export function exists(p) {
187 return function (fa) { return fa._tag === 'RemoteSuccess' && p(fa.value); };
188}
189/**
190 * Maps this RemoteFailure error into RemoteSuccess if passed function `f` return {@link Some} value, otherwise returns self
191 */
192export function recover(f) {
193 var r = recoverMap(f, identity);
194 return function (fa) { return (fa._tag === 'RemoteFailure' ? r(fa) : fa); };
195}
196/**
197 * Recovers {@link RemoteFailure} also mapping {@link RemoteSuccess} case
198 * @see {@link recover}
199 */
200export function recoverMap(f, g) {
201 return function (fa) {
202 switch (fa._tag) {
203 case 'RemoteInitial': {
204 return fa;
205 }
206 case 'RemotePending': {
207 return fa;
208 }
209 case 'RemoteFailure': {
210 var b = f(fa.error);
211 return b._tag === 'Some' ? success(b.value) : fa;
212 }
213 case 'RemoteSuccess': {
214 return success(g(fa.value));
215 }
216 }
217 };
218}
219var concatPendings = function (a, b) {
220 if (isSome(a.progress) && isSome(b.progress)) {
221 var progressA = a.progress.value;
222 var progressB = b.progress.value;
223 if (isNone(progressA.total) || isNone(progressB.total)) {
224 return progress({
225 loaded: progressA.loaded + progressB.loaded,
226 total: none,
227 });
228 }
229 var totalA = progressA.total.value;
230 var totalB = progressB.total.value;
231 var total = totalA + totalB;
232 var loaded = (progressA.loaded * totalA + progressB.loaded * totalB) / (total * total);
233 return progress({
234 loaded: loaded,
235 total: some(total),
236 });
237 }
238 var noA = isNone(a.progress);
239 var noB = isNone(b.progress);
240 if (noA && !noB) {
241 return b;
242 }
243 if (!noA && noB) {
244 return a;
245 }
246 return pending;
247};
248//instance
249export var remoteData = {
250 //HKT
251 URI: URI,
252 //Monad
253 of: function (value) { return success(value); },
254 ap: function (fab, fa) {
255 switch (fa._tag) {
256 case 'RemoteInitial': {
257 return isFailure(fab) ? fab : initial;
258 }
259 case 'RemotePending': {
260 return isPending(fab) ? concatPendings(fa, fab) : isSuccess(fab) ? fa : fab;
261 }
262 case 'RemoteFailure': {
263 return isFailure(fab) ? fab : fa;
264 }
265 case 'RemoteSuccess': {
266 return isSuccess(fab) ? success(fab.value(fa.value)) : fab;
267 }
268 }
269 },
270 map: function (fa, f) {
271 return isSuccess(fa) ? success(f(fa.value)) : fa;
272 },
273 chain: function (fa, f) {
274 return isSuccess(fa) ? f(fa.value) : fa;
275 },
276 //Foldable
277 reduce: function (fa, b, f) {
278 return pipe(fa, fold(function () { return b; }, function () { return b; }, function () { return b; }, function (a) { return f(b, a); }));
279 },
280 reduceRight: function (fa, b, f) { return (isSuccess(fa) ? f(fa.value, b) : b); },
281 foldMap: function (M) { return function (fa, f) {
282 return isSuccess(fa) ? f(fa.value) : M.empty;
283 }; },
284 //Traversable
285 traverse: function (F) { return function (ta, f) {
286 if (isSuccess(ta)) {
287 return F.map(f(ta.value), function (a) { return remoteData.of(a); });
288 }
289 else {
290 return F.of(ta);
291 }
292 }; },
293 sequence: function (F) { return function (ta) {
294 return remoteData.traverse(F)(ta, identity);
295 }; },
296 //Bifunctor
297 bimap: function (fla, f, g) {
298 return pipe(fla, fold(function () { return initial; }, foldO(function () { return pending; }, progress), function (e) { return failure(f(e)); }, function (a) { return success(g(a)); }));
299 },
300 mapLeft: function (fla, f) {
301 return fold(function () { return initial; }, foldO(function () { return pending; }, progress), function (e) { return failure(f(e)); }, function () { return fla; })(fla);
302 },
303 //Alt
304 alt: function (fx, fy) { return fold(fy, fy, fy, function () { return fx; })(fx); },
305 //Alternative
306 zero: function () { return initial; },
307 //Extend
308 extend: function (fla, f) {
309 return pipe(fla, fold(function () { return initial; }, foldO(function () { return pending; }, progress), function () { return fla; }, function () { return success(f(fla)); }));
310 },
311};
312//Eq
313export var getEq = function (EE, EA) {
314 return {
315 equals: function (x, y) {
316 return pipe(x, fold(function () { return isInitial(y); }, function () { return isPending(y); }, function (xError) {
317 return pipe(y, fold(constFalse, constFalse, function (yError) { return EE.equals(xError, yError); }, constFalse));
318 }, function (ax) {
319 return pipe(y, fold(constFalse, constFalse, constFalse, function (ay) { return EA.equals(ax, ay); }));
320 }));
321 },
322 };
323};
324//Ord
325var constLt = constant(-1);
326var constEq = constant(0);
327var constGt = constant(1);
328export var getOrd = function (OE, OA) {
329 return tslib_1.__assign({}, getEq(OE, OA), { compare: function (x, y) {
330 return sign(pipe(x, fold(function () {
331 return pipe(y, fold(constEq, constLt, constLt, constLt));
332 }, function () {
333 return pipe(y, fold(constGt, constEq, constLt, constLt));
334 }, function (xError) {
335 return pipe(y, fold(constGt, constGt, function (yError) { return OE.compare(xError, yError); }, constLt));
336 }, function (xValue) {
337 return pipe(y, fold(constGt, constGt, constGt, function (yValue) { return OA.compare(xValue, yValue); }));
338 })));
339 } });
340};
341//Semigroup
342export var getSemigroup = function (SE, SA) {
343 return {
344 concat: function (x, y) {
345 var constX = constant(x);
346 var constY = constant(y);
347 return pipe(x, fold(function () {
348 return pipe(y, fold(constY, constY, constY, constY));
349 }, function () {
350 return pipe(y, fold(constX, function () { return concatPendings(x, y); }, constY, constY));
351 }, function (xError) {
352 return pipe(y, fold(constX, constX, function (yError) { return failure(SE.concat(xError, yError)); }, function () { return y; }));
353 }, function (xValue) {
354 return pipe(y, fold(constX, constX, function () { return x; }, function (yValue) { return success(SA.concat(xValue, yValue)); }));
355 }));
356 },
357 };
358};
359//Monoid
360export var getMonoid = function (SL, SA) {
361 return tslib_1.__assign({}, getSemigroup(SL, SA), { empty: initial });
362};
363var showOptionNumber = getShowOption(showNumber);
364//Show
365export var getShow = function (SE, SA) { return ({
366 show: fold(function () { return 'initial'; }, foldO(function () { return 'pending'; }, function (progress) {
367 return "progress({ loaded: " + showNumber.show(progress.loaded) + ", total: " + showOptionNumber.show(progress.total) + " })";
368 }), function (e) { return "failure(" + SE.show(e) + ")"; }, function (a) { return "success(" + SA.show(a) + ")"; }),
369}); };
370var _a = pipeable(remoteData), alt = _a.alt, ap = _a.ap, apFirst = _a.apFirst, apSecond = _a.apSecond, bimap = _a.bimap, chain = _a.chain, chainFirst = _a.chainFirst, duplicate = _a.duplicate, extend = _a.extend, flatten = _a.flatten, foldMap = _a.foldMap, map = _a.map, mapLeft = _a.mapLeft, reduce = _a.reduce, reduceRight = _a.reduceRight;
371export { alt, ap, apFirst, apSecond, bimap, chain, chainFirst, duplicate, extend, flatten, foldMap, map, mapLeft, reduce, reduceRight, };
372export function combine() {
373 var list = [];
374 for (var _i = 0; _i < arguments.length; _i++) {
375 list[_i] = arguments[_i];
376 }
377 if (list.length === 0) {
378 return remoteData.of([]);
379 }
380 return array.sequence(remoteData)(list);
381}