@typed/async-data 0.13.1 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/AsyncData.ts DELETED
@@ -1,651 +0,0 @@
1
- import * as Cause from 'effect/Cause'
2
- import * as Effect from 'effect/Effect'
3
- import * as Either from 'effect/Either'
4
- import * as Equal from 'effect/Equal'
5
- import * as Equivalence from 'effect/Equivalence'
6
- import * as Exit from 'effect/Exit'
7
- import { dual, identity } from 'effect/Function'
8
- import * as Option from 'effect/Option'
9
- import * as Schema from 'effect/Schema'
10
- import * as SchemaAST from 'effect/SchemaAST'
11
- import * as Unify from 'effect/Unify'
12
- import { Progress, type ProgressEncoded } from './Progress.js'
13
- import { DataEffect, LiteralWithDefault } from './_internal.js'
14
-
15
- export type AsyncData<A, E = never> =
16
- | NoData
17
- | Loading
18
- | Success<A>
19
- | Failure<E>
20
- | Refreshing<A, E>
21
- | Optimistic<A, E>
22
-
23
- export namespace AsyncData {
24
- export type Encoded<A, E = never> =
25
- | typeof NoData.Encoded
26
- | typeof Loading.Encoded
27
- | SuccessEncoded<A>
28
- | FailureEncoded<E>
29
- | RefreshingEncoded<A, E>
30
- | OptimisticEncoded<A, E>
31
- export interface SuccessEncoded<A> {
32
- readonly _tag: 'Success'
33
- readonly value: A
34
- }
35
-
36
- export interface FailureEncoded<E> {
37
- readonly _tag: 'Failure'
38
- readonly cause: Schema.CauseEncoded<E, unknown>
39
- }
40
-
41
- export type RefreshingEncoded<A, E = never> = {
42
- readonly _tag: 'Refreshing'
43
- readonly previous: SuccessEncoded<A> | FailureEncoded<E>
44
- readonly progress?: ProgressEncoded
45
- }
46
-
47
- export interface OptimisticEncoded<A, E = never> {
48
- readonly _tag: 'Optimistic'
49
- readonly previous: Encoded<A, E>
50
- readonly value: A
51
- }
52
-
53
- /**
54
- * @category models
55
- * @since 1.0.0
56
- */
57
- export interface Unify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
58
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
59
- AsyncData: () => Unify_<A[Unify.typeSymbol]> extends AsyncData<infer E0, infer A0> | infer _
60
- ? AsyncData<E0, A0>
61
- : never
62
- }
63
-
64
- type Unify_<T extends AsyncData<any, any>> = T extends NoData
65
- ? AsyncData<never, never>
66
- : T extends Loading
67
- ? AsyncData<never, never>
68
- : T extends Failure<infer E>
69
- ? AsyncData<E, never>
70
- : T extends Success<infer A>
71
- ? AsyncData<never, A>
72
- : T extends Optimistic<infer A, infer E>
73
- ? AsyncData<A, E>
74
- : T extends Refreshing<infer A, infer E>
75
- ? AsyncData<A, E>
76
- : never
77
-
78
- /**
79
- * @category models
80
- * @since 1.0.0
81
- */
82
- export interface IgnoreList extends Effect.EffectUnifyIgnore {
83
- Effect: true
84
- }
85
- }
86
-
87
- export class NoData
88
- extends Schema.TaggedError<NoData>()('NoData', {})
89
- implements Effect.Effect<never, NoData, never> {}
90
-
91
- export const noData: {
92
- (): NoData
93
- <A, E = never>(): AsyncData<A, E>
94
- } = () => NoData.make()
95
-
96
- export class Loading
97
- extends Schema.TaggedError<Loading>()('Loading', {
98
- progress: Schema.optionalWith(Progress, { as: 'Option', default: undefined }),
99
- })
100
- implements Effect.Effect<never, Loading, never> {}
101
-
102
- export function loading(progress?: Progress): Loading
103
- export function loading<A, E = never>(progress?: Progress): AsyncData<A, E>
104
- export function loading<A, E = never>(progress?: Progress): AsyncData<A, E> {
105
- return Loading.make({ progress: Option.fromNullable(progress) })
106
- }
107
-
108
- export class Success<A>
109
- extends DataEffect('Success')<{ readonly value: A }, A>
110
- implements Effect.Effect<A>
111
- {
112
- constructor(value: A) {
113
- super({ value }, Effect.succeed(value))
114
- }
115
-
116
- static schema<A, I, R>(
117
- value: Schema.Schema<A, I, R>,
118
- ): Schema.SchemaClass<Success<A>, AsyncData.SuccessEncoded<I>, R> {
119
- return Schema.Struct({
120
- _tag: LiteralWithDefault<'_tag'>()('Success'),
121
- value,
122
- }).pipe(
123
- Schema.transform(Schema.instanceOf(Success<A>), {
124
- strict: true,
125
- decode: (from) => new Success(from.value),
126
- encode: identity,
127
- }),
128
- )
129
- }
130
- }
131
-
132
- export function success<A>(value: A): Success<A>
133
- export function success<A, E = never>(value: A): AsyncData<A, E>
134
- export function success<A>(value: A): Success<A> {
135
- return new Success(value)
136
- }
137
-
138
- export class Failure<E>
139
- extends DataEffect('Failure')<{ readonly cause: Cause.Cause<E> }, never, E, never>
140
- implements Effect.Effect<never, E, never>
141
- {
142
- constructor(cause: Cause.Cause<E>) {
143
- super({ cause }, Effect.failCause(cause))
144
- }
145
-
146
- static schema<E, I, R>(
147
- error: Schema.Schema<E, I, R>,
148
- ): Schema.SchemaClass<Failure<E>, AsyncData.FailureEncoded<I>, R> {
149
- return Schema.Struct({
150
- _tag: LiteralWithDefault<'_tag'>()('Failure'),
151
- cause: Schema.Cause({
152
- error,
153
- defect: Schema.Unknown,
154
- }),
155
- }).pipe(
156
- Schema.transform(Schema.instanceOf(Failure<E>), {
157
- strict: true,
158
- decode: (from) => new Failure(from.cause),
159
- encode: identity,
160
- }),
161
- )
162
- }
163
- }
164
-
165
- export function failure<E>(cause: Cause.Cause<E>): Failure<E>
166
- export function failure<E, A = never>(cause: Cause.Cause<E>): AsyncData<A, E>
167
- export function failure<E>(cause: Cause.Cause<E>): Failure<E> {
168
- return new Failure(cause)
169
- }
170
-
171
- export function die(cause: unknown): Failure<never>
172
- export function die<A, E = never>(cause: unknown): AsyncData<A, E>
173
- export function die<E>(cause: unknown): Failure<E> {
174
- return new Failure(Cause.die(cause))
175
- }
176
-
177
- export function fail<E>(error: E): Failure<E> {
178
- return new Failure(Cause.fail(error))
179
- }
180
-
181
- export class Refreshing<A, E>
182
- extends DataEffect('Refreshing')<
183
- { readonly previous: Success<A> | Failure<E>; readonly progress: Option.Option<Progress> },
184
- A,
185
- E,
186
- never
187
- >
188
- implements Effect.Effect<A, E>
189
- {
190
- constructor(previous: Success<A> | Failure<E>, progress: Option.Option<Progress>) {
191
- super({ previous, progress }, previous)
192
- }
193
-
194
- static schema<A, EI, E, AI, R>(
195
- previous: Schema.Schema<
196
- Success<A> | Failure<E>,
197
- AsyncData.SuccessEncoded<AI> | AsyncData.FailureEncoded<EI>,
198
- R
199
- >,
200
- ): Schema.SchemaClass<Refreshing<A, E>, AsyncData.RefreshingEncoded<AI, EI>, R> {
201
- return Schema.Struct({
202
- _tag: LiteralWithDefault<'_tag'>()('Refreshing'),
203
- previous,
204
- progress: Schema.optionalWith(Progress, { as: 'Option', default: undefined }),
205
- }).pipe(
206
- Schema.transform(Schema.instanceOf(Refreshing<A, E>), {
207
- strict: true,
208
- decode: (from) => new Refreshing(from.previous, from.progress),
209
- encode: identity,
210
- }),
211
- )
212
- }
213
- }
214
-
215
- export function refreshing<A = never, E = never>(
216
- previous: Success<A> | Failure<E>,
217
- progress?: Progress,
218
- ): Refreshing<A, E> {
219
- return new Refreshing(previous, Option.fromNullable(progress))
220
- }
221
-
222
- export class Optimistic<A, E>
223
- extends DataEffect('Optimistic')<{ readonly previous: AsyncData<A, E>; readonly value: A }, A>
224
- implements Effect.Effect<A>
225
- {
226
- constructor(previous: AsyncData<A, E>, value: A) {
227
- super({ previous, value }, Effect.succeed(value))
228
- }
229
-
230
- static schema<A, EI, E, AI, R, R2>(
231
- previous: Schema.Schema<AsyncData<A, E>, AsyncData.Encoded<AI, EI>, R>,
232
- value: Schema.Schema<A, AI, R2>,
233
- ): Schema.SchemaClass<Optimistic<A, E>, AsyncData.OptimisticEncoded<AI, EI>, R | R2> {
234
- return Schema.Struct({
235
- _tag: LiteralWithDefault<'_tag'>()('Optimistic'),
236
- previous,
237
- value,
238
- }).pipe(
239
- Schema.transform(Schema.instanceOf(Optimistic<A, E>), {
240
- strict: true,
241
- decode: (from) => new Optimistic(from.previous, from.value),
242
- encode: identity,
243
- }),
244
- )
245
- }
246
- }
247
-
248
- export const optimistic: {
249
- <A>(value: A): <E>(previous: AsyncData<A, E>) => Optimistic<A, E>
250
- <A, E>(previous: AsyncData<A, E>, value: A): Optimistic<A, E>
251
- } = dual(2, function optimistic<A, E>(previous: AsyncData<A, E>, value: A): Optimistic<A, E> {
252
- return new Optimistic(previous, value)
253
- })
254
-
255
- export const matchAll: {
256
- <A, E, R1, R2, R3, R4, R5, R6>(
257
- data: AsyncData<A, E>,
258
- matchers: {
259
- readonly NoData: () => R1
260
- readonly Loading: (progress: Option.Option<Progress>) => R2
261
- readonly Success: (value: A) => R3
262
- readonly Failure: (cause: Cause.Cause<E>) => R4
263
- readonly Refreshing: (
264
- previous: Success<A> | Failure<E>,
265
- progress: Option.Option<Progress>,
266
- ) => R5
267
- readonly Optimistic: (value: A, previous: AsyncData<A, E>) => R6
268
- },
269
- ): R1 | R2 | R3 | R4 | R5 | R6
270
-
271
- <A, E, R1, R2, R3, R4, R5, R6>(
272
- data: AsyncData<A, E>,
273
- matchers: {
274
- readonly NoData: () => R1
275
- readonly Loading: (progress: Option.Option<Progress>) => R2
276
- readonly Success: (value: A) => R3
277
- readonly Failure: (cause: Cause.Cause<E>) => R4
278
- readonly Refreshing: (
279
- previous: Success<A> | Failure<E>,
280
- progress: Option.Option<Progress>,
281
- ) => R5
282
- readonly Optimistic: (value: A, previous: AsyncData<A, E>) => R6
283
- },
284
- ): R1 | R2 | R3 | R4 | R5 | R6
285
- } = dual(
286
- 2,
287
- function matchAll<A, E, R1, R2, R3, R4, R5, R6>(
288
- data: AsyncData<A, E>,
289
- matchers: {
290
- readonly NoData: () => R1
291
- readonly Loading: (progress: Option.Option<Progress>) => R2
292
- readonly Success: (value: A) => R3
293
- readonly Failure: (cause: Cause.Cause<E>) => R4
294
- readonly Refreshing: (
295
- previous: Success<A> | Failure<E>,
296
- progress: Option.Option<Progress>,
297
- ) => R5
298
- readonly Optimistic: (value: A, previous: AsyncData<A, E>) => R6
299
- },
300
- ): R1 | R2 | R3 | R4 | R5 | R6 {
301
- switch (data._tag) {
302
- case 'NoData':
303
- return matchers.NoData()
304
- case 'Loading':
305
- return matchers.Loading(data.progress)
306
- case 'Success':
307
- return matchers.Success(data.value)
308
- case 'Failure':
309
- return matchers.Failure(data.cause)
310
- case 'Refreshing':
311
- return matchers.Refreshing(data.previous, data.progress)
312
- case 'Optimistic':
313
- return matchers.Optimistic(data.value, data.previous)
314
- }
315
- },
316
- )
317
-
318
- export const match: {
319
- <A, E, R1, R2, R3, R4>(matchers: {
320
- readonly NoData: () => R1
321
- readonly Loading: (progress: Option.Option<Progress>) => R2
322
- readonly Success: (
323
- value: A,
324
- params: {
325
- readonly isRefreshing: boolean
326
- readonly isOptimistic: boolean
327
- readonly progress: Option.Option<Progress>
328
- },
329
- ) => R3
330
- readonly Failure: (
331
- cause: Cause.Cause<E>,
332
- params: { readonly isRefreshing: boolean; readonly progress: Option.Option<Progress> },
333
- ) => R4
334
- }): (data: AsyncData<A, E>) => Unify.Unify<R1 | R2 | R3 | R4>
335
-
336
- <A, E, R1, R2, R3, R4>(
337
- data: AsyncData<A, E>,
338
- matchers: {
339
- readonly NoData: () => R1
340
- readonly Loading: (progress: Option.Option<Progress>) => R2
341
- readonly Success: (
342
- value: A,
343
- params: {
344
- readonly isRefreshing: boolean
345
- readonly isOptimistic: boolean
346
- readonly progress: Option.Option<Progress>
347
- },
348
- ) => R3
349
- readonly Failure: (
350
- cause: Cause.Cause<E>,
351
- params: { readonly isRefreshing: boolean; readonly progress: Option.Option<Progress> },
352
- ) => R4
353
- },
354
- ): Unify.Unify<R1 | R2 | R3 | R4>
355
- } = dual(
356
- 2,
357
- function match<A, E, R1, R2, R3, R4>(
358
- data: AsyncData<A, E>,
359
- matchers: {
360
- readonly NoData: () => R1
361
- readonly Loading: (progress: Option.Option<Progress>) => R2
362
- readonly Success: (
363
- value: A,
364
- params: {
365
- readonly isRefreshing: boolean
366
- readonly isOptimistic: boolean
367
- readonly progress: Option.Option<Progress>
368
- },
369
- ) => R3
370
- readonly Failure: (
371
- cause: Cause.Cause<E>,
372
- params: { readonly isRefreshing: boolean; readonly progress: Option.Option<Progress> },
373
- ) => R4
374
- },
375
- ): Unify.Unify<R1 | R2 | R3 | R4> {
376
- const match_ = (
377
- data: AsyncData<A, E>,
378
- params: {
379
- readonly isRefreshing: boolean
380
- readonly isOptimistic: boolean
381
- readonly progress: Option.Option<Progress>
382
- },
383
- ): R1 | R2 | R3 | R4 =>
384
- matchAll(data, {
385
- NoData: matchers.NoData,
386
- Loading: matchers.Loading,
387
- Success: (value) => matchers.Success(value, params),
388
- Failure: (cause) => matchers.Failure(cause, params),
389
- Refreshing: (previous, progress) =>
390
- match_(previous, { ...params, isRefreshing: true, progress }),
391
- Optimistic: (value) => matchers.Success(value, { ...params, isOptimistic: true }),
392
- })
393
-
394
- return Unify.unify(
395
- match_(data, {
396
- isRefreshing: false,
397
- isOptimistic: false,
398
- progress: Option.none(),
399
- }),
400
- )
401
- },
402
- )
403
-
404
- export function startLoading<A, E>(data: AsyncData<A, E>, progress?: Progress): AsyncData<A, E> {
405
- switch (data._tag) {
406
- case 'Success':
407
- case 'Failure':
408
- return refreshing(data, progress)
409
- case 'NoData':
410
- return loading(progress)
411
- case 'Optimistic':
412
- return optimistic(startLoading(data.previous, progress), data.value)
413
- default:
414
- return data
415
- }
416
- }
417
-
418
- export function stopLoading<A, E>(data: AsyncData<A, E>): AsyncData<A, E> {
419
- switch (data._tag) {
420
- case 'Refreshing':
421
- return data.previous
422
- case 'Loading':
423
- return noData()
424
- case 'Optimistic':
425
- return optimistic(stopLoading(data.previous), data.value)
426
- default:
427
- return data
428
- }
429
- }
430
-
431
- export function updateProgress<A, E>(data: AsyncData<A, E>, progress: Progress): AsyncData<A, E> {
432
- switch (data._tag) {
433
- case 'Refreshing':
434
- return refreshing(data.previous, progress)
435
- case 'Loading':
436
- return loading(progress)
437
- case 'Optimistic':
438
- return optimistic(updateProgress(data.previous, progress), data.value)
439
- default:
440
- return data
441
- }
442
- }
443
-
444
- export interface AsyncDataSchemaClass<E, EI, ER, A, AI, AR>
445
- extends Schema.SchemaClass<AsyncData<A, E>, AsyncData.Encoded<AI, EI>, AR | ER> {
446
- readonly eq: Equivalence.Equivalence<AsyncData<A, E>>
447
-
448
- readonly success: (value: A) => AsyncData<A, E>
449
- readonly failCause: (cause: Cause.Cause<E>) => AsyncData<A, E>
450
- readonly fail: (error: E) => AsyncData<A, E>
451
- readonly die: (cause: unknown) => AsyncData<A, E>
452
- readonly optimistic: (previous: AsyncData<A, E>, value: A) => AsyncData<A, E>
453
- readonly refreshing: (previous: Success<A> | Failure<E>, progress?: Progress) => AsyncData<A, E>
454
- readonly loading: (progress?: Progress) => AsyncData<A, E>
455
- readonly noData: () => AsyncData<A, E>
456
- }
457
-
458
- export function AsyncData<A, AI, AR, E, EI, ER>(
459
- schemas: {
460
- readonly success: Schema.Schema<A, AI, AR>
461
- readonly failure: Schema.Schema<E, EI, ER>
462
- },
463
- annotations?: Schema.Annotations.Schema<AsyncData<A, E>>,
464
- ): AsyncDataSchemaClass<E, EI, ER, A, AI, AR> {
465
- const identifier = Option.all({
466
- E: getIdentifier(schemas.failure.ast),
467
- A: getIdentifier(schemas.success.ast),
468
- }).pipe(
469
- Option.match({
470
- onNone: () => 'AsyncData',
471
- onSome: ({ E, A }) => `AsyncData<${E}, ${A}>`,
472
- }),
473
- )
474
-
475
- const equivalence = makeEquivalence(
476
- Schema.equivalence(schemas.success),
477
- Schema.equivalence(schemas.failure),
478
- )
479
-
480
- const recursive: Schema.Schema<
481
- AsyncData<A, E>,
482
- AsyncData.Encoded<AI, EI>,
483
- AR | ER
484
- > = Schema.suspend(() => AsyncData).annotations({
485
- identifier,
486
- equivalence: () => equivalence,
487
- ...annotations,
488
- })
489
-
490
- const successAndFailure = Schema.Union(
491
- Success.schema(schemas.success),
492
- Failure.schema(schemas.failure),
493
- )
494
-
495
- const AsyncData = Schema.Union(
496
- NoData,
497
- Loading,
498
- successAndFailure,
499
- Refreshing.schema(successAndFailure),
500
- Optimistic.schema(recursive, schemas.success),
501
- ).annotations({
502
- equivalence: () => equivalence,
503
- })
504
-
505
- return class extends AsyncData {
506
- static readonly eq = equivalence
507
- static readonly success = success
508
- static readonly failCause = failure
509
- static readonly fail = fail
510
- static readonly die = die
511
- static readonly optimistic = optimistic
512
- static readonly refreshing = refreshing
513
- static readonly loading = loading
514
- static readonly noData = noData
515
- }
516
- }
517
-
518
- function getIdentifier(ast: SchemaAST.AST) {
519
- return SchemaAST.getJSONIdentifier(ast).pipe(
520
- Option.orElse(() => SchemaAST.getTitleAnnotation(ast)),
521
- )
522
- }
523
-
524
- export function fromExit<A, E>(exit: Exit.Exit<A, E>): AsyncData<A, E> {
525
- return Exit.match(exit, { onSuccess: success<A>, onFailure: failure<E> })
526
- }
527
-
528
- export function fromEither<A, E>(either: Either.Either<A, E>): AsyncData<A, E> {
529
- return Either.match(either, { onLeft: fail<E>, onRight: success<A> })
530
- }
531
-
532
- export function fromOption<A>(option: Option.Option<A>): AsyncData<A> {
533
- return Option.match(option, { onSome: success<A>, onNone: noData<A> })
534
- }
535
-
536
- export const map: {
537
- <A, B>(f: (a: A) => B): <E>(data: AsyncData<A, E>) => AsyncData<B, E>
538
- <A, E, B>(data: AsyncData<A, E>, f: (a: A) => B): AsyncData<B, E>
539
- } = dual(2, function map<A, E, B>(data: AsyncData<A, E>, f: (a: A) => B): AsyncData<B, E> {
540
- if (data._tag === 'Success') {
541
- return new Success(f(data.value))
542
- } else if (data._tag === 'Refreshing' && data.previous._tag === 'Success') {
543
- return new Refreshing(new Success(f(data.previous.value)), data.progress)
544
- } else if (data._tag === 'Optimistic') {
545
- if (data.previous._tag === 'Success') {
546
- return new Optimistic(new Success(f(data.previous.value)), f(data.value))
547
- } else if (data.previous._tag === 'Failure') {
548
- return new Optimistic(data.previous, f(data.value))
549
- }
550
- }
551
-
552
- return data as AsyncData<B, E>
553
- })
554
-
555
- export const flatMap: {
556
- <A, B, E2>(f: (a: A) => AsyncData<B, E2>): <E>(data: AsyncData<A, E>) => AsyncData<B, E | E2>
557
- <A, E, B, E2>(data: AsyncData<A, E>, f: (a: A) => AsyncData<B, E2>): AsyncData<B, E | E2>
558
- } = dual(2, function flatMap<
559
- A,
560
- E,
561
- B,
562
- E2,
563
- >(data: AsyncData<A, E>, f: (a: A) => AsyncData<B, E2>): AsyncData<B, E | E2> {
564
- if (data._tag === 'Success' || data._tag === 'Optimistic') {
565
- return f(data.value)
566
- } else if (data._tag === 'Refreshing' && data.previous._tag === 'Success') {
567
- return f(data.previous.value)
568
- }
569
-
570
- return data as AsyncData<B, E>
571
- })
572
-
573
- export function isNoData<A, E>(data: AsyncData<A, E>): data is NoData {
574
- return data._tag === 'NoData'
575
- }
576
-
577
- export function isLoading<A, E>(data: AsyncData<A, E>): data is Loading {
578
- return data._tag === 'Loading'
579
- }
580
-
581
- export function isSuccess<A, E>(data: AsyncData<A, E>): data is Success<A> {
582
- return data._tag === 'Success'
583
- }
584
-
585
- export function isFailure<A, E>(data: AsyncData<A, E>): data is Failure<E> {
586
- return data._tag === 'Failure'
587
- }
588
-
589
- export function isRefreshing<A, E>(data: AsyncData<A, E>): data is Refreshing<A, E> {
590
- return data._tag === 'Refreshing'
591
- }
592
-
593
- export function isLoadingOrRefreshing<A, E>(
594
- data: AsyncData<A, E>,
595
- ): data is Loading | Refreshing<A, E> {
596
- return isLoading(data) || isRefreshing(data)
597
- }
598
-
599
- export function isOptimistic<A, E>(data: AsyncData<A, E>): data is Optimistic<A, E> {
600
- return data._tag === 'Optimistic'
601
- }
602
-
603
- export function makeEquivalence<A, E>(
604
- eqA: Equivalence.Equivalence<A>,
605
- eqB: Equivalence.Equivalence<E>,
606
- ): Equivalence.Equivalence<AsyncData<A, E>> {
607
- const eqCause = Equivalence.make((a: Cause.Cause<E>, b: Cause.Cause<E>) => {
608
- const failuresA = Array.from(Cause.failures(a))
609
- const failuresB = Array.from(Cause.failures(b))
610
- if (failuresA.length > 0) {
611
- return (
612
- failuresA.length === failuresB.length && failuresA.every((e, i) => eqB(e, failuresB[i]))
613
- )
614
- }
615
-
616
- return Equal.equals(a, b)
617
- })
618
-
619
- const eq: Equivalence.Equivalence<AsyncData<A, E>> = Equivalence.make((a, b) =>
620
- matchAll(a, {
621
- NoData: () => isNoData(b),
622
- Loading: (progress) => isLoading(b) && Equal.equals(progress, b.progress),
623
- Success: (value) => isSuccess(b) && eqA(value, b.value),
624
- Failure: (cause) => isFailure(b) && eqCause(cause, b.cause),
625
- Refreshing: (previous, progress) =>
626
- isRefreshing(b) && eq(previous, b.previous) && Equal.equals(progress, b.progress),
627
- Optimistic: (value, previous) =>
628
- isOptimistic(b) && eqA(value, b.value) && eq(previous, b.previous),
629
- }),
630
- )
631
-
632
- return eq
633
- }
634
-
635
- type EqValue<T> = T extends Equivalence.Equivalence<infer A> ? A : never
636
-
637
- function unionEquivalence<EQS extends ReadonlyArray<Equivalence.Equivalence<any>>>(
638
- eqs: EQS,
639
- ): Equivalence.Equivalence<EqValue<EQS[number]>> {
640
- return (a, b) => {
641
- for (const eq of eqs) {
642
- if (eq(a, b)) {
643
- return true
644
- }
645
- }
646
-
647
- return false
648
- }
649
- }
650
-
651
- export const equals = makeEquivalence(Equal.equals, Equal.equals)