@typed/async-data 0.12.0 → 0.13.1
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 +203 -0
- package/dist/AsyncData.js +294 -0
- package/dist/AsyncData.js.map +1 -0
- package/dist/LazyRef.d.ts +22 -0
- package/dist/LazyRef.js +28 -0
- package/dist/LazyRef.js.map +1 -0
- package/dist/Progress.d.ts +25 -0
- package/dist/Progress.js +22 -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 +20 -0
- package/dist/_internal.js +58 -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 -49
- package/readme.md +218 -0
- package/src/AsyncData.test.ts +89 -0
- package/src/AsyncData.ts +552 -595
- package/src/LazyRef.ts +89 -0
- package/src/Progress.ts +20 -66
- package/src/TypeId.ts +1 -1
- package/src/_internal.ts +111 -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 -539
- 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 -754
- package/src/internal/async-data.ts +0 -114
- package/src/internal/tag.ts +0 -9
package/src/AsyncData.ts
CHANGED
|
@@ -1,49 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import * as
|
|
9
|
-
import * as
|
|
10
|
-
import * as
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const getCurrentTimestamp = () => Date.now()
|
|
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 * 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
|
+
|
|
37
23
|
export namespace AsyncData {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
+
}
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
}
|
|
47
52
|
|
|
48
53
|
/**
|
|
49
54
|
* @category models
|
|
@@ -51,15 +56,24 @@ export namespace AsyncData {
|
|
|
51
56
|
*/
|
|
52
57
|
export interface Unify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
|
|
53
58
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
54
|
-
AsyncData: () => Unify_<A[Unify.typeSymbol]> extends AsyncData<infer E0, infer A0> | infer _
|
|
59
|
+
AsyncData: () => Unify_<A[Unify.typeSymbol]> extends AsyncData<infer E0, infer A0> | infer _
|
|
60
|
+
? AsyncData<E0, A0>
|
|
55
61
|
: never
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
type Unify_<T extends AsyncData<any, any>> = T extends NoData
|
|
59
|
-
|
|
60
|
-
T extends
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
63
77
|
|
|
64
78
|
/**
|
|
65
79
|
* @category models
|
|
@@ -70,625 +84,568 @@ export namespace AsyncData {
|
|
|
70
84
|
}
|
|
71
85
|
}
|
|
72
86
|
|
|
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
|
-
}
|
|
87
|
+
export class NoData
|
|
88
|
+
extends Schema.TaggedError<NoData>()('NoData', {})
|
|
89
|
+
implements Effect.Effect<never, NoData, never> {}
|
|
95
90
|
|
|
96
|
-
/**
|
|
97
|
-
* @since 1.0.0
|
|
98
|
-
*/
|
|
99
91
|
export const noData: {
|
|
100
92
|
(): 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
|
|
93
|
+
<A, E = never>(): AsyncData<A, E>
|
|
94
|
+
} = () => NoData.make()
|
|
112
95
|
|
|
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
|
-
}
|
|
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> {}
|
|
126
101
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
readonly timestamp: number // Date.now()
|
|
132
|
-
readonly progress: Option.Option<Progress.Progress>
|
|
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) })
|
|
133
106
|
}
|
|
134
107
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
|
|
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))
|
|
141
114
|
}
|
|
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
115
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
+
}
|
|
182
131
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
+
}
|
|
187
137
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
}
|
|
192
145
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
+
}
|
|
197
163
|
}
|
|
198
164
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
readonly timestamp: number // Date.now()
|
|
204
|
-
readonly refreshing: Option.Option<Loading>
|
|
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)
|
|
205
169
|
}
|
|
206
170
|
|
|
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
|
|
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))
|
|
245
175
|
}
|
|
246
176
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
*/
|
|
250
|
-
export type SuccessOptions = {
|
|
251
|
-
readonly timestamp: number // Date.now()
|
|
252
|
-
readonly refreshing: Option.Option<Loading>
|
|
177
|
+
export function fail<E>(error: E): Failure<E> {
|
|
178
|
+
return new Failure(Cause.fail(error))
|
|
253
179
|
}
|
|
254
180
|
|
|
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
|
-
|
|
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
|
+
}
|
|
281
213
|
}
|
|
282
214
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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))
|
|
288
220
|
}
|
|
289
221
|
|
|
290
|
-
|
|
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
|
+
}
|
|
291
229
|
|
|
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>(
|
|
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,
|
|
302
237
|
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>
|
|
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
|
+
}
|
|
344
246
|
}
|
|
345
247
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
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
|
+
})
|
|
352
254
|
|
|
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>(
|
|
255
|
+
export const matchAll: {
|
|
256
|
+
<A, E, R1, R2, R3, R4, R5, R6>(
|
|
257
|
+
data: AsyncData<A, E>,
|
|
374
258
|
matchers: {
|
|
375
|
-
NoData: (
|
|
376
|
-
Loading: (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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>(
|
|
384
272
|
data: AsyncData<A, E>,
|
|
385
273
|
matchers: {
|
|
386
|
-
NoData: (
|
|
387
|
-
Loading: (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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)
|
|
391
314
|
}
|
|
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
|
-
})
|
|
315
|
+
},
|
|
316
|
+
)
|
|
431
317
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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>(
|
|
440
337
|
data: AsyncData<A, E>,
|
|
441
|
-
|
|
442
|
-
|
|
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>
|
|
443
355
|
} = dual(
|
|
444
356
|
2,
|
|
445
|
-
function<A, E,
|
|
357
|
+
function match<A, E, R1, R2, R3, R4>(
|
|
446
358
|
data: AsyncData<A, E>,
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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:
|
|
452
414
|
return data
|
|
453
|
-
}
|
|
454
415
|
}
|
|
455
|
-
|
|
416
|
+
}
|
|
456
417
|
|
|
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)
|
|
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
|
|
474
428
|
}
|
|
475
429
|
}
|
|
476
430
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
} else {
|
|
488
|
-
return noData()
|
|
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
|
|
489
441
|
}
|
|
490
442
|
}
|
|
491
443
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
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
|
+
}
|
|
505
457
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
+
})
|
|
511
489
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
+
})
|
|
517
504
|
|
|
518
|
-
|
|
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
|
+
}
|
|
519
517
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
518
|
+
function getIdentifier(ast: SchemaAST.AST) {
|
|
519
|
+
return SchemaAST.getJSONIdentifier(ast).pipe(
|
|
520
|
+
Option.orElse(() => SchemaAST.getTitleAnnotation(ast)),
|
|
521
|
+
)
|
|
522
|
+
}
|
|
525
523
|
|
|
526
|
-
|
|
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
527
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
timestamp: Equivalence.number,
|
|
532
|
-
refreshing: optionLoadingEq
|
|
533
|
-
})
|
|
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
|
+
}
|
|
534
531
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
value: valueEq,
|
|
539
|
-
timestamp: Equivalence.number,
|
|
540
|
-
refreshing: optionLoadingEq
|
|
541
|
-
})
|
|
532
|
+
export function fromOption<A>(option: Option.Option<A>): AsyncData<A> {
|
|
533
|
+
return Option.match(option, { onSome: success<A>, onNone: noData<A> })
|
|
534
|
+
}
|
|
542
535
|
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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))
|
|
550
549
|
}
|
|
551
|
-
return previousEq
|
|
552
550
|
}
|
|
553
551
|
|
|
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
|
|
552
|
+
return data as AsyncData<B, E>
|
|
553
|
+
})
|
|
572
554
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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)
|
|
580
568
|
}
|
|
581
|
-
}
|
|
582
569
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
export function
|
|
587
|
-
return
|
|
588
|
-
onFailure: (cause) => failCause(cause),
|
|
589
|
-
onSuccess: (value) => success(value)
|
|
590
|
-
})
|
|
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'
|
|
591
575
|
}
|
|
592
576
|
|
|
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
|
-
})
|
|
577
|
+
export function isLoading<A, E>(data: AsyncData<A, E>): data is Loading {
|
|
578
|
+
return data._tag === 'Loading'
|
|
601
579
|
}
|
|
602
580
|
|
|
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
|
-
})
|
|
581
|
+
export function isSuccess<A, E>(data: AsyncData<A, E>): data is Success<A> {
|
|
582
|
+
return data._tag === 'Success'
|
|
583
|
+
}
|
|
629
584
|
|
|
630
|
-
function
|
|
631
|
-
|
|
585
|
+
export function isFailure<A, E>(data: AsyncData<A, E>): data is Failure<E> {
|
|
586
|
+
return data._tag === 'Failure'
|
|
587
|
+
}
|
|
632
588
|
|
|
633
|
-
|
|
589
|
+
export function isRefreshing<A, E>(data: AsyncData<A, E>): data is Refreshing<A, E> {
|
|
590
|
+
return data._tag === 'Refreshing'
|
|
634
591
|
}
|
|
635
592
|
|
|
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
|
-
})
|
|
593
|
+
export function isLoadingOrRefreshing<A, E>(
|
|
594
|
+
data: AsyncData<A, E>,
|
|
595
|
+
): data is Loading | Refreshing<A, E> {
|
|
596
|
+
return isLoading(data) || isRefreshing(data)
|
|
655
597
|
}
|
|
656
598
|
|
|
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
|
-
})
|
|
599
|
+
export function isOptimistic<A, E>(data: AsyncData<A, E>): data is Optimistic<A, E> {
|
|
600
|
+
return data._tag === 'Optimistic'
|
|
668
601
|
}
|
|
669
602
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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)
|
|
680
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
|
|
681
633
|
}
|
|
682
634
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
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
|
+
}
|
|
694
649
|
}
|
|
650
|
+
|
|
651
|
+
export const equals = makeEquivalence(Equal.equals, Equal.equals)
|