@typed/async-data 0.11.0 → 0.13.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/.nvmrc +1 -0
- package/biome.json +36 -0
- package/dist/AsyncData.d.ts +196 -0
- package/dist/AsyncData.js +285 -0
- package/dist/AsyncData.js.map +1 -0
- package/dist/LazyRef.d.ts +21 -0
- package/dist/LazyRef.js +27 -0
- package/dist/LazyRef.js.map +1 -0
- package/dist/Progress.d.ts +25 -0
- package/dist/Progress.js +21 -0
- package/dist/Progress.js.map +1 -0
- package/dist/{dts/TypeId.d.ts → TypeId.d.ts} +0 -1
- package/dist/TypeId.js +8 -0
- package/dist/TypeId.js.map +1 -0
- package/dist/_internal.d.ts +16 -0
- package/dist/_internal.js +53 -0
- package/dist/_internal.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -48
- package/readme.md +218 -0
- package/src/AsyncData.test.ts +89 -0
- package/src/AsyncData.ts +555 -595
- package/src/LazyRef.ts +87 -0
- package/src/Progress.ts +18 -65
- package/src/TypeId.ts +1 -1
- package/src/_internal.ts +114 -0
- package/src/index.ts +4 -0
- package/tsconfig.json +27 -0
- package/AsyncData/package.json +0 -6
- package/LICENSE +0 -21
- package/Progress/package.json +0 -6
- package/README.md +0 -5
- package/Schema/package.json +0 -6
- package/TypeId/package.json +0 -6
- package/dist/cjs/AsyncData.js +0 -399
- package/dist/cjs/AsyncData.js.map +0 -1
- package/dist/cjs/Progress.js +0 -62
- package/dist/cjs/Progress.js.map +0 -1
- package/dist/cjs/Schema.js +0 -538
- package/dist/cjs/Schema.js.map +0 -1
- package/dist/cjs/TypeId.js +0 -14
- package/dist/cjs/TypeId.js.map +0 -1
- package/dist/cjs/internal/async-data.js +0 -94
- package/dist/cjs/internal/async-data.js.map +0 -1
- package/dist/cjs/internal/tag.js +0 -12
- package/dist/cjs/internal/tag.js.map +0 -1
- package/dist/dts/AsyncData.d.ts +0 -386
- package/dist/dts/AsyncData.d.ts.map +0 -1
- package/dist/dts/Progress.d.ts +0 -43
- package/dist/dts/Progress.d.ts.map +0 -1
- package/dist/dts/Schema.d.ts +0 -78
- package/dist/dts/Schema.d.ts.map +0 -1
- package/dist/dts/TypeId.d.ts.map +0 -1
- package/dist/dts/internal/async-data.d.ts +0 -43
- package/dist/dts/internal/async-data.d.ts.map +0 -1
- package/dist/dts/internal/tag.d.ts +0 -6
- package/dist/dts/internal/tag.d.ts.map +0 -1
- package/dist/esm/AsyncData.js +0 -372
- package/dist/esm/AsyncData.js.map +0 -1
- package/dist/esm/Progress.js +0 -49
- package/dist/esm/Progress.js.map +0 -1
- package/dist/esm/Schema.js +0 -500
- package/dist/esm/Schema.js.map +0 -1
- package/dist/esm/TypeId.js +0 -8
- package/dist/esm/TypeId.js.map +0 -1
- package/dist/esm/internal/async-data.js +0 -91
- package/dist/esm/internal/async-data.js.map +0 -1
- package/dist/esm/internal/tag.js +0 -6
- package/dist/esm/internal/tag.js.map +0 -1
- package/dist/esm/package.json +0 -4
- package/src/Schema.ts +0 -750
- package/src/internal/async-data.ts +0 -114
- package/src/internal/tag.ts +0 -9
package/src/AsyncData.ts
CHANGED
|
@@ -1,49 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* AsyncData represents a piece of data which is acquired asynchronously with loading, failure, and progress states
|
|
28
|
-
* in addition to Option-like states of NoData and Success.
|
|
29
|
-
*
|
|
30
|
-
* @since 1.0.0
|
|
31
|
-
*/
|
|
32
|
-
export type AsyncData<A, E = never> = NoData | Loading | Failure<E> | Success<A> | Optimistic<A, E>
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @since 1.0.0
|
|
36
|
-
*/
|
|
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
|
+
|
|
37
26
|
export namespace AsyncData {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
+
}
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
}
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
57
|
* @category models
|
|
@@ -51,15 +59,24 @@ export namespace AsyncData {
|
|
|
51
59
|
*/
|
|
52
60
|
export interface Unify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
|
|
53
61
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
54
|
-
AsyncData: () => Unify_<A[Unify.typeSymbol]> extends AsyncData<infer E0, infer A0> | infer _
|
|
62
|
+
AsyncData: () => Unify_<A[Unify.typeSymbol]> extends AsyncData<infer E0, infer A0> | infer _
|
|
63
|
+
? AsyncData<E0, A0>
|
|
55
64
|
: never
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
type Unify_<T extends AsyncData<any, any>> = T extends NoData
|
|
59
|
-
|
|
60
|
-
T extends
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
63
80
|
|
|
64
81
|
/**
|
|
65
82
|
* @category models
|
|
@@ -70,625 +87,568 @@ export namespace AsyncData {
|
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
export class NoData extends Data.TaggedError(NO_DATA_TAG)<{}> {
|
|
77
|
-
/**
|
|
78
|
-
* @since 1.0.0
|
|
79
|
-
*/
|
|
80
|
-
readonly [AsyncDataTypeId]: AsyncDataTypeId = AsyncDataTypeId
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @since 1.0.0
|
|
84
|
-
*/
|
|
85
|
-
readonly [Unify.typeSymbol]!: unknown
|
|
86
|
-
/**
|
|
87
|
-
* @since 1.0.0
|
|
88
|
-
*/
|
|
89
|
-
readonly [Unify.unifySymbol]!: AsyncData.Unify<this>
|
|
90
|
-
/**
|
|
91
|
-
* @since 1.0.0
|
|
92
|
-
*/
|
|
93
|
-
readonly [Unify.ignoreSymbol]!: AsyncData.IgnoreList
|
|
94
|
-
}
|
|
90
|
+
export class NoData
|
|
91
|
+
extends Schema.TaggedError<NoData>()('NoData', {})
|
|
92
|
+
implements Effect.Effect<never, NoData, never> {}
|
|
95
93
|
|
|
96
|
-
/**
|
|
97
|
-
* @since 1.0.0
|
|
98
|
-
*/
|
|
99
94
|
export const noData: {
|
|
100
95
|
(): NoData
|
|
101
|
-
<A, E>(): AsyncData<A, E>
|
|
102
|
-
} = ()
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* @since 1.0.0
|
|
106
|
-
*/
|
|
107
|
-
export class Loading extends Data.TaggedError(LOADING_TAG)<LoadingOptions> {
|
|
108
|
-
/**
|
|
109
|
-
* @since 1.0.0
|
|
110
|
-
*/
|
|
111
|
-
readonly [AsyncDataTypeId]: AsyncDataTypeId = AsyncDataTypeId
|
|
96
|
+
<A, E = never>(): AsyncData<A, E>
|
|
97
|
+
} = () => NoData.make()
|
|
112
98
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
* @since 1.0.0
|
|
119
|
-
*/
|
|
120
|
-
readonly [Unify.unifySymbol]!: AsyncData.Unify<this>
|
|
121
|
-
/**
|
|
122
|
-
* @since 1.0.0
|
|
123
|
-
*/
|
|
124
|
-
readonly [Unify.ignoreSymbol]!: AsyncData.IgnoreList
|
|
125
|
-
}
|
|
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> {}
|
|
126
104
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
readonly timestamp: number // Date.now()
|
|
132
|
-
readonly progress: Option.Option<Progress.Progress>
|
|
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) })
|
|
133
109
|
}
|
|
134
110
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
|
|
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))
|
|
141
117
|
}
|
|
142
|
-
] extends [infer R] ? { readonly [K in keyof R]: R[K] } : never
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @since 1.0.0
|
|
146
|
-
*/
|
|
147
|
-
export const loading: {
|
|
148
|
-
(options?: OptionalPartial<LoadingOptions>): Loading
|
|
149
|
-
<A, E>(options?: OptionalPartial<LoadingOptions>): AsyncData<A, E>
|
|
150
|
-
} = (options?: OptionalPartial<LoadingOptions>): Loading =>
|
|
151
|
-
new Loading({
|
|
152
|
-
[AsyncDataTypeId]: AsyncDataTypeId,
|
|
153
|
-
timestamp: options?.timestamp ?? getCurrentTimestamp(),
|
|
154
|
-
progress: Option.fromNullable(options?.progress)
|
|
155
|
-
} as any)
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @since 1.0.0
|
|
159
|
-
*/
|
|
160
|
-
export interface Failure<out E> extends Effect.Effect<never, E, never> {
|
|
161
|
-
readonly [AsyncDataTypeId]: AsyncDataTypeId
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* @since 1.18.0
|
|
165
|
-
*/
|
|
166
|
-
readonly _tag: typeof FAILURE_TAG
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* @since 1.18.0
|
|
170
|
-
*/
|
|
171
|
-
readonly cause: Cause.Cause<E>
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* @since 1.20.0
|
|
175
|
-
*/
|
|
176
|
-
readonly timestamp: number // Date.now()
|
|
177
118
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
+
}
|
|
182
134
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
+
}
|
|
187
140
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
}
|
|
192
148
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
+
}
|
|
197
166
|
}
|
|
198
167
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
readonly timestamp: number // Date.now()
|
|
204
|
-
readonly refreshing: Option.Option<Loading>
|
|
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)
|
|
205
172
|
}
|
|
206
173
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<E>(cause: Cause.Cause<E>, options?: OptionalPartial<FailureOptions>): Failure<E>
|
|
212
|
-
<A, E>(cause: Cause.Cause<E>, options?: OptionalPartial<FailureOptions>): AsyncData<A, E>
|
|
213
|
-
} = <E>(cause: Cause.Cause<E>, options?: OptionalPartial<FailureOptions>): Failure<E> =>
|
|
214
|
-
new internal.FailureImpl(
|
|
215
|
-
cause,
|
|
216
|
-
options?.timestamp ?? getCurrentTimestamp(),
|
|
217
|
-
Option.fromNullable(options?.refreshing)
|
|
218
|
-
) as any
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* @since 1.0.0
|
|
222
|
-
*/
|
|
223
|
-
export const fail: {
|
|
224
|
-
<E>(error: E, options?: OptionalPartial<FailureOptions>): Failure<E>
|
|
225
|
-
<A, E>(error: E, options?: OptionalPartial<FailureOptions>): AsyncData<A, E>
|
|
226
|
-
} = <E>(error: E, options?: OptionalPartial<FailureOptions>): Failure<E> => failCause<E>(Cause.fail(error), options)
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* @since 1.0.0
|
|
230
|
-
*/
|
|
231
|
-
export interface Success<out A> extends Effect.Effect<never, never, A> {
|
|
232
|
-
readonly [AsyncDataTypeId]: AsyncDataTypeId
|
|
233
|
-
|
|
234
|
-
readonly _tag: typeof SUCCESS_TAG
|
|
235
|
-
readonly value: A
|
|
236
|
-
/**
|
|
237
|
-
* @since 1.20.0
|
|
238
|
-
*/
|
|
239
|
-
readonly timestamp: number // Date.now()
|
|
240
|
-
readonly refreshing: Option.Option<Loading>
|
|
241
|
-
|
|
242
|
-
readonly [Unify.typeSymbol]: unknown
|
|
243
|
-
readonly [Unify.unifySymbol]: AsyncData.Unify<this>
|
|
244
|
-
readonly [Unify.ignoreSymbol]: AsyncData.IgnoreList
|
|
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))
|
|
245
178
|
}
|
|
246
179
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
*/
|
|
250
|
-
export type SuccessOptions = {
|
|
251
|
-
readonly timestamp: number // Date.now()
|
|
252
|
-
readonly refreshing: Option.Option<Loading>
|
|
180
|
+
export function fail<E>(error: E): Failure<E> {
|
|
181
|
+
return new Failure(Cause.fail(error))
|
|
253
182
|
}
|
|
254
183
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
+
}
|
|
281
216
|
}
|
|
282
217
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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))
|
|
288
223
|
}
|
|
289
224
|
|
|
290
|
-
|
|
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
|
+
}
|
|
291
232
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
(args) => args.length === 3 || isAsyncDataFirst(args),
|
|
300
|
-
<A, E>(previous: AsyncData<A, E>, value: A, options?: OptionalPartial<OptimisticOptions>): Optimistic<A, E> =>
|
|
301
|
-
new internal.OptimisticImpl<A, E>(
|
|
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,
|
|
302
240
|
value,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
*/
|
|
312
|
-
export const isSuccess = <A, E>(data: AsyncData<A, E>): data is Success<A> => data._tag === SUCCESS_TAG
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* @since 1.0.0
|
|
316
|
-
*/
|
|
317
|
-
export const isOptimistic = <A, E>(data: AsyncData<A, E>): data is Optimistic<A, E> => data._tag === OPTIMISTIC_TAG
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* @since 1.0.0
|
|
321
|
-
*/
|
|
322
|
-
export const isFailure = <A, E>(data: AsyncData<A, E>): data is Failure<E> => data._tag === FAILURE_TAG
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* @since 1.0.0
|
|
326
|
-
*/
|
|
327
|
-
export const isLoading = <A, E>(data: AsyncData<A, E>): data is Loading => data._tag === LOADING_TAG
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* @since 1.0.0
|
|
331
|
-
*/
|
|
332
|
-
export const isNoData = <A, E>(data: AsyncData<A, E>): data is NoData => data._tag === NO_DATA_TAG
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* @since 1.0.0
|
|
336
|
-
*/
|
|
337
|
-
export type Refreshing<A, E> = RefreshingFailure<E> | RefreshingSuccess<A>
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* @since 1.0.0
|
|
341
|
-
*/
|
|
342
|
-
export interface RefreshingFailure<E> extends Failure<E> {
|
|
343
|
-
readonly refreshing: Option.Some<Loading>
|
|
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
|
+
}
|
|
344
249
|
}
|
|
345
250
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
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
|
+
})
|
|
352
257
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
export const isRefreshing = <A, E>(data: AsyncData<A, E>): data is Refreshing<A, E> =>
|
|
357
|
-
isSuccess(data) || isFailure(data)
|
|
358
|
-
? Option.isSome(data.refreshing)
|
|
359
|
-
: isOptimistic(data)
|
|
360
|
-
? isRefreshing(data.previous)
|
|
361
|
-
: false
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* @since 1.0.0
|
|
365
|
-
*/
|
|
366
|
-
export const isLoadingOrRefreshing = <A, E>(data: AsyncData<A, E>): data is Loading | Refreshing<A, E> =>
|
|
367
|
-
isLoading(data) || isRefreshing(data) || (isOptimistic(data) && isLoadingOrRefreshing(data.previous))
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* @since 1.0.0
|
|
371
|
-
*/
|
|
372
|
-
export const match: {
|
|
373
|
-
<A, E, R1, R2, R3, R4, R5>(
|
|
258
|
+
export const matchAll: {
|
|
259
|
+
<A, E, R1, R2, R3, R4, R5, R6>(
|
|
260
|
+
data: AsyncData<A, E>,
|
|
374
261
|
matchers: {
|
|
375
|
-
NoData: (
|
|
376
|
-
Loading: (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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>(
|
|
384
275
|
data: AsyncData<A, E>,
|
|
385
276
|
matchers: {
|
|
386
|
-
NoData: (
|
|
387
|
-
Loading: (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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)
|
|
391
317
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
NoData: (data: NoData) => R1
|
|
395
|
-
Loading: (data: Loading) => R2
|
|
396
|
-
Failure: (cause: Cause.Cause<E>, data: Failure<E>) => R3
|
|
397
|
-
Success: (value: A, data: Success<A>) => R4
|
|
398
|
-
Optimistic: (value: A, data: Optimistic<A, E>) => R5
|
|
399
|
-
}): Unify.Unify<R1 | R2 | R3 | R4> => {
|
|
400
|
-
if (isSuccess(data)) {
|
|
401
|
-
return matchers.Success(data.value, data) as Unify.Unify<R1 | R2 | R3 | R4>
|
|
402
|
-
} else if (isFailure(data)) {
|
|
403
|
-
return matchers.Failure(data.cause, data) as Unify.Unify<R1 | R2 | R3 | R4>
|
|
404
|
-
} else if (isLoading(data)) {
|
|
405
|
-
return matchers.Loading(data) as Unify.Unify<R1 | R2 | R3 | R4>
|
|
406
|
-
} else if (isNoData(data)) {
|
|
407
|
-
return matchers.NoData(data) as Unify.Unify<R1 | R2 | R3 | R4>
|
|
408
|
-
} else {
|
|
409
|
-
return matchers.Optimistic(data.value, data) as Unify.Unify<R1 | R2 | R3 | R4>
|
|
410
|
-
}
|
|
411
|
-
})
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* @since 1.0.0
|
|
415
|
-
*/
|
|
416
|
-
export const map: {
|
|
417
|
-
<A, B>(f: (a: A) => B): <E>(data: AsyncData<A, E>) => AsyncData<B, E>
|
|
418
|
-
<A, E, B>(data: AsyncData<A, E>, f: (a: A) => B): AsyncData<B, E>
|
|
419
|
-
} = dual(2, function map<A, E, B>(data: AsyncData<A, E>, f: (a: A) => B): AsyncData<B, E> {
|
|
420
|
-
if (isSuccess(data)) {
|
|
421
|
-
return success(f(data.value), {
|
|
422
|
-
timestamp: data.timestamp,
|
|
423
|
-
refreshing: Option.getOrUndefined(data.refreshing)
|
|
424
|
-
})
|
|
425
|
-
} else if (isOptimistic(data)) {
|
|
426
|
-
return optimistic(map(data.previous, f), f(data.value), { timestamp: data.timestamp })
|
|
427
|
-
} else {
|
|
428
|
-
return data
|
|
429
|
-
}
|
|
430
|
-
})
|
|
318
|
+
},
|
|
319
|
+
)
|
|
431
320
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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>(
|
|
440
340
|
data: AsyncData<A, E>,
|
|
441
|
-
|
|
442
|
-
|
|
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>
|
|
443
358
|
} = dual(
|
|
444
359
|
2,
|
|
445
|
-
function<A, E,
|
|
360
|
+
function match<A, E, R1, R2, R3, R4>(
|
|
446
361
|
data: AsyncData<A, E>,
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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:
|
|
452
417
|
return data
|
|
453
|
-
}
|
|
454
418
|
}
|
|
455
|
-
|
|
419
|
+
}
|
|
456
420
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return Option.isSome(data.refreshing)
|
|
468
|
-
? data
|
|
469
|
-
: failCause(data.cause, { ...data, refreshing: loading(options) })
|
|
470
|
-
} else if (isOptimistic(data)) {
|
|
471
|
-
return optimistic(startLoading(data.previous, options), data.value, data)
|
|
472
|
-
} else {
|
|
473
|
-
return loading(options)
|
|
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
|
|
474
431
|
}
|
|
475
432
|
}
|
|
476
433
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
} else {
|
|
488
|
-
return noData()
|
|
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
|
|
489
444
|
}
|
|
490
445
|
}
|
|
491
446
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
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
|
+
}
|
|
505
460
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
+
})
|
|
511
492
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
+
})
|
|
517
507
|
|
|
518
|
-
|
|
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
|
+
}
|
|
519
520
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
521
|
+
function getIdentifier(ast: SchemaAST.AST) {
|
|
522
|
+
return SchemaAST.getJSONIdentifier(ast).pipe(
|
|
523
|
+
Option.orElse(() => SchemaAST.getTitleAnnotation(ast)),
|
|
524
|
+
)
|
|
525
|
+
}
|
|
525
526
|
|
|
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
|
+
}
|
|
527
530
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
timestamp: Equivalence.number,
|
|
532
|
-
refreshing: optionLoadingEq
|
|
533
|
-
})
|
|
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
534
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
value: valueEq,
|
|
539
|
-
timestamp: Equivalence.number,
|
|
540
|
-
refreshing: optionLoadingEq
|
|
541
|
-
})
|
|
535
|
+
export function fromOption<A>(option: Option.Option<A>): AsyncData<A> {
|
|
536
|
+
return Option.match(option, { onSome: success<A>, onNone: noData<A> })
|
|
537
|
+
}
|
|
542
538
|
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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))
|
|
550
552
|
}
|
|
551
|
-
return previousEq
|
|
552
553
|
}
|
|
553
554
|
|
|
554
|
-
return
|
|
555
|
-
|
|
556
|
-
value: valueEq,
|
|
557
|
-
timestamp: Equivalence.number,
|
|
558
|
-
previous: (a: AsyncData<A, E>, b: AsyncData<A, E>) => get()(a, b)
|
|
559
|
-
})
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* @since 1.0.0
|
|
564
|
-
*/
|
|
565
|
-
export const getEquivalence = <A, E>(
|
|
566
|
-
valueEq: Equivalence.Equivalence<A> = Equal.equals
|
|
567
|
-
): Equivalence.Equivalence<AsyncData<A, E>> => {
|
|
568
|
-
const successEq_ = successEquivalence(valueEq)
|
|
569
|
-
const optimisticEq_ = optimisticEquivalence(valueEq)
|
|
570
|
-
return (a, b) => {
|
|
571
|
-
if (a === b) return true
|
|
555
|
+
return data as AsyncData<B, E>
|
|
556
|
+
})
|
|
572
557
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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)
|
|
580
571
|
}
|
|
581
|
-
}
|
|
582
572
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
export function
|
|
587
|
-
return
|
|
588
|
-
onFailure: (cause) => failCause(cause),
|
|
589
|
-
onSuccess: (value) => success(value)
|
|
590
|
-
})
|
|
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'
|
|
591
578
|
}
|
|
592
579
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
*/
|
|
596
|
-
export function fromEither<A, E = never>(either: Either.Either<A, E>): Success<A> | Failure<E> {
|
|
597
|
-
return Either.match(either, {
|
|
598
|
-
onLeft: (e) => fail(e),
|
|
599
|
-
onRight: (a) => success(a)
|
|
600
|
-
})
|
|
580
|
+
export function isLoading<A, E>(data: AsyncData<A, E>): data is Loading {
|
|
581
|
+
return data._tag === 'Loading'
|
|
601
582
|
}
|
|
602
583
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
export const isExpired: {
|
|
607
|
-
(ttl: Duration.DurationInput, now?: number): <A, E>(data: AsyncData<A, E>) => boolean
|
|
608
|
-
<A, E>(data: AsyncData<A, E>, ttl: Duration.DurationInput, now?: number): boolean
|
|
609
|
-
} = dual(isAsyncDataFirst, function isExpired<A, E>(
|
|
610
|
-
data: AsyncData<A, E>,
|
|
611
|
-
ttl: Duration.DurationInput,
|
|
612
|
-
now: number = getCurrentTimestamp()
|
|
613
|
-
): boolean {
|
|
614
|
-
return match(data, {
|
|
615
|
-
NoData: () => true,
|
|
616
|
-
Loading: ({ timestamp }) => isPastTTL(timestamp, ttl, now),
|
|
617
|
-
Failure: (_, f) =>
|
|
618
|
-
Option.isNone(f.refreshing)
|
|
619
|
-
? isPastTTL(f.timestamp, ttl, now)
|
|
620
|
-
: isPastTTL(f.refreshing.value.timestamp, ttl, now),
|
|
621
|
-
Success: (_, s) =>
|
|
622
|
-
Option.isNone(s.refreshing)
|
|
623
|
-
? isPastTTL(s.timestamp, ttl, now) :
|
|
624
|
-
isPastTTL(s.refreshing.value.timestamp, ttl, now),
|
|
625
|
-
Optimistic: (_, o) =>
|
|
626
|
-
isPastTTL(o.timestamp, ttl, now) || (o.previous._tag === NO_DATA_TAG ? false : isExpired(o.previous, ttl, now))
|
|
627
|
-
})
|
|
628
|
-
})
|
|
584
|
+
export function isSuccess<A, E>(data: AsyncData<A, E>): data is Success<A> {
|
|
585
|
+
return data._tag === 'Success'
|
|
586
|
+
}
|
|
629
587
|
|
|
630
|
-
function
|
|
631
|
-
|
|
588
|
+
export function isFailure<A, E>(data: AsyncData<A, E>): data is Failure<E> {
|
|
589
|
+
return data._tag === 'Failure'
|
|
590
|
+
}
|
|
632
591
|
|
|
633
|
-
|
|
592
|
+
export function isRefreshing<A, E>(data: AsyncData<A, E>): data is Refreshing<A, E> {
|
|
593
|
+
return data._tag === 'Refreshing'
|
|
634
594
|
}
|
|
635
595
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
* without needing to manage timestamps.
|
|
641
|
-
*
|
|
642
|
-
* @since 1.0.0
|
|
643
|
-
*/
|
|
644
|
-
export function dataEqual<A, E>(first: AsyncData<A, E>, second: AsyncData<A, E>): boolean {
|
|
645
|
-
return match(first, {
|
|
646
|
-
NoData: () => isNoData(second),
|
|
647
|
-
Loading: (l) => isLoading(second) && Equal.equals(l.progress, second.progress),
|
|
648
|
-
Failure: (_, f1) =>
|
|
649
|
-
isFailure(second) && Equal.equals(f1.cause, second.cause) && optionDataEqual(f1.refreshing, second.refreshing),
|
|
650
|
-
Success: (_, s1) =>
|
|
651
|
-
isSuccess(second) && Equal.equals(s1.value, second.value) && optionDataEqual(s1.refreshing, second.refreshing),
|
|
652
|
-
Optimistic: (_, o1) =>
|
|
653
|
-
isOptimistic(second) && Equal.equals(o1.value, second.value) && dataEqual(o1.previous, second.previous)
|
|
654
|
-
})
|
|
596
|
+
export function isLoadingOrRefreshing<A, E>(
|
|
597
|
+
data: AsyncData<A, E>,
|
|
598
|
+
): data is Loading | Refreshing<A, E> {
|
|
599
|
+
return isLoading(data) || isRefreshing(data)
|
|
655
600
|
}
|
|
656
601
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
*/
|
|
660
|
-
export function toOption<A, E>(data: AsyncData<A, E>): Option.Option<A> {
|
|
661
|
-
return match(data, {
|
|
662
|
-
NoData: Option.none,
|
|
663
|
-
Loading: Option.none,
|
|
664
|
-
Failure: Option.none,
|
|
665
|
-
Success: Option.some,
|
|
666
|
-
Optimistic: Option.some
|
|
667
|
-
})
|
|
602
|
+
export function isOptimistic<A, E>(data: AsyncData<A, E>): data is Optimistic<A, E> {
|
|
603
|
+
return data._tag === 'Optimistic'
|
|
668
604
|
}
|
|
669
605
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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)
|
|
680
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
|
|
681
636
|
}
|
|
682
637
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
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
|
+
}
|
|
694
652
|
}
|
|
653
|
+
|
|
654
|
+
export const equals = makeEquivalence(Equal.equals, Equal.equals)
|