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