@typed/async-data 0.2.0 → 0.3.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/dist/cjs/AsyncData.js +99 -33
- package/dist/cjs/AsyncData.js.map +1 -1
- package/dist/cjs/Progress.js +24 -9
- package/dist/cjs/Progress.js.map +1 -1
- package/dist/cjs/Schema.js +221 -48
- package/dist/cjs/Schema.js.map +1 -1
- package/dist/cjs/internal/async-data.js +37 -25
- package/dist/cjs/internal/async-data.js.map +1 -1
- package/dist/dts/AsyncData.d.ts +50 -6
- package/dist/dts/AsyncData.d.ts.map +1 -1
- package/dist/dts/Progress.d.ts +9 -2
- package/dist/dts/Progress.d.ts.map +1 -1
- package/dist/dts/Schema.d.ts +125 -3
- package/dist/dts/Schema.d.ts.map +1 -1
- package/dist/dts/internal/async-data.d.ts +11 -3
- package/dist/dts/internal/async-data.d.ts.map +1 -1
- package/dist/esm/AsyncData.js +68 -3
- package/dist/esm/AsyncData.js.map +1 -1
- package/dist/esm/Progress.js +14 -3
- package/dist/esm/Progress.js.map +1 -1
- package/dist/esm/Schema.js +216 -45
- package/dist/esm/Schema.js.map +1 -1
- package/dist/esm/internal/async-data.js +16 -4
- package/dist/esm/internal/async-data.js.map +1 -1
- package/package.json +4 -4
- package/src/AsyncData.ts +106 -6
- package/src/Progress.ts +27 -7
- package/src/Schema.ts +434 -91
- package/src/internal/async-data.ts +12 -4
package/src/Schema.ts
CHANGED
|
@@ -4,136 +4,433 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Arbitrary from "@effect/schema/Arbitrary"
|
|
6
6
|
import * as AST from "@effect/schema/AST"
|
|
7
|
+
import * as Eq from "@effect/schema/Equivalence"
|
|
7
8
|
import * as ParseResult from "@effect/schema/ParseResult"
|
|
8
9
|
import * as Pretty from "@effect/schema/Pretty"
|
|
9
10
|
import * as Schema from "@effect/schema/Schema"
|
|
10
11
|
import * as AsyncData from "@typed/async-data/AsyncData"
|
|
11
|
-
import {
|
|
12
|
+
import { Equal } from "effect"
|
|
13
|
+
import type * as Cause from "effect/Cause"
|
|
14
|
+
import * as Effect from "effect/Effect"
|
|
12
15
|
import * as Option from "effect/Option"
|
|
16
|
+
import * as P from "./Progress.js"
|
|
13
17
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const NO_DATA_PRETTY = "AsyncData.NoData"
|
|
19
|
+
const LOADING_PRETTY = (loading: AsyncData.Loading) =>
|
|
20
|
+
Option.match(loading.progress, {
|
|
21
|
+
onNone: () => `AsyncData.Loading(timestamp=${loading.timestamp})`,
|
|
22
|
+
onSome: (progress) => `AsyncData.Loading(timestamp=${loading.timestamp}, progress=${P.pretty(progress)})`
|
|
23
|
+
})
|
|
24
|
+
const FAILURE_PRETTY = <E>(print: Pretty.Pretty<Cause.Cause<E>>) => (failure: AsyncData.Failure<E>) =>
|
|
25
|
+
Option.match(failure.refreshing, {
|
|
26
|
+
onNone: () => `AsyncData.Failure(timestamp=${failure.timestamp}, cause=${print(failure.cause)})`,
|
|
27
|
+
onSome: (loading) =>
|
|
28
|
+
`AsyncData.Failure(timestamp=${failure.timestamp}, refreshing=${LOADING_PRETTY(loading)}, cause=${
|
|
29
|
+
print(failure.cause)
|
|
30
|
+
})`
|
|
31
|
+
})
|
|
32
|
+
const SUCCESS_PRETTY = <A>(print: Pretty.Pretty<A>) => (success: AsyncData.Success<A>) =>
|
|
33
|
+
Option.match(success.refreshing, {
|
|
34
|
+
onNone: () => `AsyncData.Success(timestamp=${success.timestamp}, value=${print(success.value)})`,
|
|
35
|
+
onSome: (loading) =>
|
|
36
|
+
`AsyncData.Success(timestamp=${success.timestamp}, refreshing=${LOADING_PRETTY(loading)}, value=${
|
|
37
|
+
print(success.value)
|
|
38
|
+
})`
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const NoDataSchemaJson = Schema.struct({
|
|
42
|
+
_tag: Schema.literal("NoData")
|
|
43
|
+
})
|
|
44
|
+
const NoDataSchema = Schema.instanceOf(AsyncData.NoData)
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @since 1.0.0
|
|
48
|
+
*/
|
|
49
|
+
export const NoData: Schema.Schema<{ readonly _tag: "NoData" }, AsyncData.NoData> = NoDataSchemaJson
|
|
50
|
+
.pipe(
|
|
51
|
+
Schema.transform(
|
|
52
|
+
NoDataSchema,
|
|
53
|
+
(): AsyncData.NoData => AsyncData.noData(),
|
|
54
|
+
(): Schema.Schema.To<typeof NoDataSchemaJson> => ({
|
|
55
|
+
_tag: "NoData"
|
|
56
|
+
})
|
|
57
|
+
),
|
|
58
|
+
Schema.annotations({
|
|
59
|
+
[AST.IdentifierAnnotationId]: NO_DATA_PRETTY,
|
|
60
|
+
[Pretty.PrettyHookId]: () => NO_DATA_PRETTY,
|
|
61
|
+
[Arbitrary.ArbitraryHookId]: (): Arbitrary.Arbitrary<AsyncData.NoData> => (fc) => fc.constant(AsyncData.noData()),
|
|
62
|
+
[Eq.EquivalenceHookId]: () => Equal.equals
|
|
63
|
+
})
|
|
18
64
|
)
|
|
19
65
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (items.length === 0) return Cause.empty
|
|
25
|
-
if (items.length === 1) return Cause.fail(items[0])
|
|
26
|
-
return items.map(Cause.fail).reduce(join)
|
|
27
|
-
}
|
|
66
|
+
/**
|
|
67
|
+
* @since 1.0.0
|
|
68
|
+
*/
|
|
69
|
+
export type NoDataFrom = Schema.Schema.From<typeof NoData>
|
|
28
70
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
71
|
+
const ProgressSchemaJson = Schema.struct({
|
|
72
|
+
loaded: Schema.bigint,
|
|
73
|
+
total: Schema.optional(Schema.bigint)
|
|
74
|
+
})
|
|
75
|
+
type ProgressFromTo = Schema.Schema.To<typeof ProgressSchemaJson>
|
|
76
|
+
|
|
77
|
+
const ProgressSchema: Schema.Schema<
|
|
78
|
+
{
|
|
79
|
+
readonly loaded: bigint
|
|
80
|
+
readonly total: Option.Option<bigint>
|
|
81
|
+
},
|
|
82
|
+
P.Progress
|
|
83
|
+
> = Schema.data(Schema.struct({
|
|
84
|
+
loaded: Schema.bigintFromSelf,
|
|
85
|
+
total: Schema.optionFromSelf(Schema.bigintFromSelf)
|
|
86
|
+
}))
|
|
87
|
+
|
|
88
|
+
const progressArbitrary: Arbitrary.Arbitrary<P.Progress> = (fc) =>
|
|
89
|
+
fc.bigInt().chain((loaded) => fc.option(fc.bigInt({ min: loaded })).map((total) => P.make(loaded, total)))
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @since 1.0.0
|
|
93
|
+
*/
|
|
94
|
+
export const Progress: Schema.Schema<{ readonly loaded: string; readonly total?: string | undefined }, P.Progress> =
|
|
95
|
+
ProgressSchemaJson.pipe(
|
|
96
|
+
Schema.transform(
|
|
97
|
+
ProgressSchema,
|
|
98
|
+
(json): P.Progress => P.make(json.loaded, json.total),
|
|
99
|
+
(progress) => ({
|
|
100
|
+
loaded: progress.loaded,
|
|
101
|
+
total: Option.getOrUndefined(progress.total)
|
|
102
|
+
})
|
|
103
|
+
),
|
|
104
|
+
Schema.annotations({
|
|
105
|
+
[AST.IdentifierAnnotationId]: "Progress",
|
|
106
|
+
[Pretty.PrettyHookId]: () => "Progress",
|
|
107
|
+
[Arbitrary.ArbitraryHookId]: (): Arbitrary.Arbitrary<P.Progress> => progressArbitrary,
|
|
108
|
+
[Eq.EquivalenceHookId]: () => Equal.equals
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @since 1.0.0
|
|
114
|
+
*/
|
|
115
|
+
export type ProgressFrom = Schema.Schema.From<typeof Progress>
|
|
116
|
+
|
|
117
|
+
const LoadingSchemaJson = Schema.struct({
|
|
118
|
+
_tag: Schema.literal("Loading"),
|
|
119
|
+
timestamp: Schema.number,
|
|
120
|
+
progress: Schema.optional(ProgressSchemaJson)
|
|
121
|
+
})
|
|
122
|
+
const LoadingSchema = Schema.instanceOf(AsyncData.Loading)
|
|
123
|
+
|
|
124
|
+
const loadingFromJson = (json: Schema.Schema.To<typeof LoadingSchemaJson>): AsyncData.Loading =>
|
|
125
|
+
AsyncData.loading({ timestamp: json.timestamp, progress: progressFromJson(json.progress) })
|
|
126
|
+
|
|
127
|
+
const loadingToJson = (loading: AsyncData.Loading): Schema.Schema.To<typeof LoadingSchemaJson> => ({
|
|
128
|
+
_tag: "Loading",
|
|
129
|
+
timestamp: loading.timestamp,
|
|
130
|
+
progress: progressToJson(loading.progress)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const loadingArbitrary: Arbitrary.Arbitrary<AsyncData.Loading> = (fc) =>
|
|
134
|
+
fc.option(progressArbitrary(fc)).map((progress) =>
|
|
135
|
+
AsyncData.loading({
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
progress: progress || undefined
|
|
138
|
+
})
|
|
37
139
|
)
|
|
38
140
|
|
|
39
|
-
|
|
141
|
+
/**
|
|
142
|
+
* @since 1.0.0
|
|
143
|
+
*/
|
|
144
|
+
export const Loading: Schema.Schema<
|
|
145
|
+
{
|
|
146
|
+
readonly _tag: "Loading"
|
|
147
|
+
readonly timestamp: number
|
|
148
|
+
readonly progress?: ProgressFrom | undefined
|
|
149
|
+
},
|
|
150
|
+
AsyncData.Loading
|
|
151
|
+
> = LoadingSchemaJson
|
|
152
|
+
.pipe(
|
|
153
|
+
Schema.transform(
|
|
154
|
+
LoadingSchema,
|
|
155
|
+
loadingFromJson,
|
|
156
|
+
loadingToJson
|
|
157
|
+
),
|
|
158
|
+
Schema.annotations({
|
|
159
|
+
[AST.IdentifierAnnotationId]: "AsyncData.Loading",
|
|
160
|
+
[Pretty.PrettyHookId]: () => LOADING_PRETTY,
|
|
161
|
+
[Arbitrary.ArbitraryHookId]: () => loadingArbitrary,
|
|
162
|
+
[Eq.EquivalenceHookId]: () => Equal.equals
|
|
163
|
+
})
|
|
164
|
+
)
|
|
40
165
|
|
|
41
166
|
/**
|
|
42
167
|
* @since 1.0.0
|
|
43
168
|
*/
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Schema.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
169
|
+
export type LoadingFrom = Schema.Schema.From<typeof Loading>
|
|
170
|
+
|
|
171
|
+
const FailureSchemaJson = <EI, E>(cause: Schema.Schema<Schema.CauseFrom<EI>, Cause.Cause<E>>) =>
|
|
172
|
+
Schema.struct({
|
|
173
|
+
_tag: Schema.literal("Failure"),
|
|
174
|
+
cause,
|
|
175
|
+
timestamp: Schema.number,
|
|
176
|
+
refreshing: Schema.optional(LoadingSchemaJson)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const FailureSchema = <EI, E>(
|
|
180
|
+
error: Schema.Schema<EI, E>
|
|
181
|
+
): Schema.Schema<AsyncData.Failure<EI>, AsyncData.Failure<E>> =>
|
|
182
|
+
Schema.declare(
|
|
183
|
+
[Schema.cause(error)],
|
|
184
|
+
Schema.struct({}),
|
|
185
|
+
(isDecoding, causeSchema) => {
|
|
186
|
+
const parseCause = isDecoding ? Schema.parse(causeSchema) : Schema.encode(causeSchema)
|
|
187
|
+
|
|
188
|
+
return (input, options) => {
|
|
189
|
+
return Effect.gen(function*(_) {
|
|
190
|
+
if (!AsyncData.isAsyncData(input)) return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
191
|
+
|
|
192
|
+
switch (input._tag) {
|
|
193
|
+
case "Failure": {
|
|
194
|
+
const cause = yield* _(parseCause(input.cause, options))
|
|
195
|
+
|
|
196
|
+
return AsyncData.failCause(cause, {
|
|
197
|
+
timestamp: input.timestamp,
|
|
198
|
+
refreshing: Option.getOrUndefined(input.refreshing)
|
|
199
|
+
})
|
|
75
200
|
}
|
|
201
|
+
default:
|
|
202
|
+
return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
76
203
|
}
|
|
77
|
-
|
|
78
|
-
return output
|
|
79
|
-
}),
|
|
80
|
-
{
|
|
81
|
-
[AST.IdentifierAnnotationId]: "Cause",
|
|
82
|
-
[Arbitrary.ArbitraryHookId]: causePretty,
|
|
83
|
-
[Pretty.PrettyHookId]: causeArbitrary
|
|
204
|
+
})
|
|
84
205
|
}
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
const failureArbitrary = <E>(
|
|
210
|
+
cause: Arbitrary.Arbitrary<Cause.Cause<E>>
|
|
211
|
+
): Arbitrary.Arbitrary<AsyncData.Failure<E>> =>
|
|
212
|
+
(fc) =>
|
|
213
|
+
fc.option(loadingArbitrary(fc)).chain((refreshing) =>
|
|
214
|
+
cause(fc).chain((cause) =>
|
|
215
|
+
fc.date().map((date) =>
|
|
216
|
+
AsyncData.failCause(cause, {
|
|
217
|
+
timestamp: date.getTime(),
|
|
218
|
+
refreshing: refreshing || undefined
|
|
219
|
+
})
|
|
220
|
+
)
|
|
85
221
|
)
|
|
86
222
|
)
|
|
87
223
|
|
|
88
|
-
|
|
89
|
-
|
|
224
|
+
/**
|
|
225
|
+
* @since 1.0.0
|
|
226
|
+
*/
|
|
227
|
+
export const Failure = <EI, E>(
|
|
228
|
+
error: Schema.Schema<EI, E>
|
|
229
|
+
): Schema.Schema<
|
|
230
|
+
{
|
|
231
|
+
readonly _tag: "Failure"
|
|
232
|
+
readonly cause: Schema.CauseFrom<EI>
|
|
233
|
+
readonly timestamp: number
|
|
234
|
+
readonly refreshing?: {
|
|
235
|
+
readonly _tag: "Loading"
|
|
236
|
+
readonly timestamp: number
|
|
237
|
+
readonly progress?: { readonly loaded: string; readonly total?: string | undefined } | undefined
|
|
238
|
+
} | undefined
|
|
239
|
+
},
|
|
240
|
+
AsyncData.Failure<E>
|
|
241
|
+
> =>
|
|
242
|
+
FailureSchemaJson(Schema.cause(Schema.from(error)))
|
|
243
|
+
.pipe(
|
|
244
|
+
Schema.transform(
|
|
245
|
+
FailureSchema(error),
|
|
246
|
+
(json): AsyncData.Failure<EI> =>
|
|
247
|
+
AsyncData.failCause(json.cause, {
|
|
248
|
+
timestamp: json.timestamp,
|
|
249
|
+
refreshing: json.refreshing ? loadingFromJson(json.refreshing) : undefined
|
|
250
|
+
}),
|
|
251
|
+
(failure) => ({
|
|
252
|
+
_tag: "Failure",
|
|
253
|
+
cause: failure.cause,
|
|
254
|
+
timestamp: failure.timestamp,
|
|
255
|
+
refreshing: Option.getOrUndefined(Option.map(failure.refreshing, loadingToJson))
|
|
256
|
+
} as const)
|
|
257
|
+
),
|
|
258
|
+
Schema.annotations({
|
|
259
|
+
[AST.IdentifierAnnotationId]: "AsyncData.Failure",
|
|
260
|
+
[Pretty.PrettyHookId]: FAILURE_PRETTY,
|
|
261
|
+
[Arbitrary.ArbitraryHookId]: failureArbitrary,
|
|
262
|
+
[Eq.EquivalenceHookId]: () => Equal.equals
|
|
263
|
+
})
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @since 1.0.0
|
|
268
|
+
*/
|
|
269
|
+
export type FailureFrom<E> = Schema.Schema.From<ReturnType<typeof FailureSchemaJson<E, E>>>
|
|
90
270
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
271
|
+
const SuccessSchemaJson = <AI, A>(
|
|
272
|
+
value: Schema.Schema<AI, A>
|
|
273
|
+
): Schema.Schema<
|
|
274
|
+
{
|
|
275
|
+
readonly timestamp: number
|
|
276
|
+
readonly _tag: "Success"
|
|
277
|
+
readonly value: AI
|
|
278
|
+
readonly refreshing?: {
|
|
279
|
+
readonly timestamp: number
|
|
280
|
+
readonly _tag: "Loading"
|
|
281
|
+
readonly progress?: { readonly loaded: string; readonly total?: string | undefined } | undefined
|
|
282
|
+
} | undefined
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
readonly timestamp: number
|
|
286
|
+
readonly _tag: "Success"
|
|
287
|
+
readonly value: A
|
|
288
|
+
readonly refreshing?: {
|
|
289
|
+
readonly timestamp: number
|
|
290
|
+
readonly _tag: "Loading"
|
|
291
|
+
readonly progress?: { readonly loaded: bigint; readonly total?: bigint | undefined } | undefined
|
|
292
|
+
} | undefined
|
|
293
|
+
}
|
|
294
|
+
> =>
|
|
295
|
+
Schema.struct({
|
|
296
|
+
_tag: Schema.literal("Success"),
|
|
297
|
+
value,
|
|
298
|
+
timestamp: Schema.number,
|
|
299
|
+
refreshing: Schema.optional(LoadingSchemaJson)
|
|
102
300
|
})
|
|
103
301
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
302
|
+
const SuccessSchema = <AI, A>(
|
|
303
|
+
value: Schema.Schema<AI, A>
|
|
304
|
+
): Schema.Schema<AsyncData.Success<AI>, AsyncData.Success<A>> =>
|
|
305
|
+
Schema.declare(
|
|
306
|
+
[value],
|
|
307
|
+
Schema.struct({
|
|
308
|
+
_tag: Schema.literal("Success"),
|
|
309
|
+
timestamp: Schema.number
|
|
310
|
+
}),
|
|
311
|
+
(isDecoding, valueSchema) => {
|
|
312
|
+
const parseValue = isDecoding ? Schema.parse(valueSchema) : Schema.encode(valueSchema)
|
|
313
|
+
|
|
314
|
+
return (input, options) => {
|
|
315
|
+
return Effect.gen(function*(_) {
|
|
316
|
+
if (!AsyncData.isAsyncData(input)) return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
317
|
+
|
|
318
|
+
switch (input._tag) {
|
|
319
|
+
case "Success": {
|
|
320
|
+
const value = yield* _(parseValue(input.value, options))
|
|
321
|
+
|
|
322
|
+
return AsyncData.success(value, {
|
|
323
|
+
timestamp: input.timestamp,
|
|
324
|
+
refreshing: Option.getOrUndefined(input.refreshing)
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
default:
|
|
328
|
+
return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
const successArbitrary = <A>(
|
|
336
|
+
value: Arbitrary.Arbitrary<A>
|
|
337
|
+
): Arbitrary.Arbitrary<AsyncData.Success<A>> =>
|
|
108
338
|
(fc) =>
|
|
109
|
-
fc.
|
|
110
|
-
fc.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
339
|
+
fc.option(loadingArbitrary(fc)).chain((refreshing) =>
|
|
340
|
+
value(fc).chain((a) =>
|
|
341
|
+
fc.date().map((date) =>
|
|
342
|
+
AsyncData.success(a, {
|
|
343
|
+
timestamp: date.getTime(),
|
|
344
|
+
refreshing: refreshing || undefined
|
|
345
|
+
})
|
|
346
|
+
)
|
|
347
|
+
)
|
|
114
348
|
)
|
|
115
349
|
|
|
350
|
+
/**
|
|
351
|
+
* @since 1.0.0
|
|
352
|
+
*/
|
|
353
|
+
export const Success = <AI, A>(
|
|
354
|
+
value: Schema.Schema<AI, A>
|
|
355
|
+
): Schema.Schema<
|
|
356
|
+
{
|
|
357
|
+
readonly timestamp: number
|
|
358
|
+
readonly _tag: "Success"
|
|
359
|
+
readonly value: AI
|
|
360
|
+
readonly refreshing?: LoadingFrom | undefined
|
|
361
|
+
},
|
|
362
|
+
AsyncData.Success<A>
|
|
363
|
+
> =>
|
|
364
|
+
SuccessSchemaJson(Schema.from(value))
|
|
365
|
+
.pipe(
|
|
366
|
+
Schema.transform(
|
|
367
|
+
SuccessSchema(value),
|
|
368
|
+
(json): AsyncData.Success<AI> =>
|
|
369
|
+
AsyncData.success(json.value, {
|
|
370
|
+
timestamp: json.timestamp,
|
|
371
|
+
refreshing: json.refreshing ? loadingFromJson(json.refreshing) : undefined
|
|
372
|
+
}),
|
|
373
|
+
(success) => ({
|
|
374
|
+
_tag: "Success",
|
|
375
|
+
value: success.value,
|
|
376
|
+
timestamp: success.timestamp,
|
|
377
|
+
refreshing: Option.getOrUndefined(Option.map(success.refreshing, loadingToJson))
|
|
378
|
+
} as const)
|
|
379
|
+
),
|
|
380
|
+
Schema.annotations({
|
|
381
|
+
[AST.IdentifierAnnotationId]: "AsyncData.Success",
|
|
382
|
+
[Pretty.PrettyHookId]: SUCCESS_PRETTY,
|
|
383
|
+
[Arbitrary.ArbitraryHookId]: successArbitrary,
|
|
384
|
+
[Eq.EquivalenceHookId]: () => Equal.equals
|
|
385
|
+
})
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* @since 1.0.0
|
|
390
|
+
*/
|
|
391
|
+
export type SuccessFrom<A> = Schema.Schema.From<ReturnType<typeof SuccessSchemaJson<A, A>>>
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* @since 1.0.0
|
|
395
|
+
*/
|
|
396
|
+
export type AsyncDataFrom<E, A> = NoDataFrom | LoadingFrom | FailureFrom<E> | SuccessFrom<A>
|
|
397
|
+
|
|
116
398
|
/**
|
|
117
399
|
* @since 1.0.0
|
|
118
400
|
*/
|
|
119
401
|
export const asyncData = <EI, E, AI, A>(
|
|
120
402
|
error: Schema.Schema<EI, E>,
|
|
121
403
|
value: Schema.Schema<AI, A>
|
|
404
|
+
): Schema.Schema<AsyncDataFrom<EI, AI>, AsyncData.AsyncData<E, A>> => {
|
|
405
|
+
return Schema.union(
|
|
406
|
+
NoData,
|
|
407
|
+
Loading,
|
|
408
|
+
Failure(error),
|
|
409
|
+
Success(value)
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* @since 1.0.0
|
|
415
|
+
*/
|
|
416
|
+
export const asyncDataFromSelf = <EI, E, AI, A>(
|
|
417
|
+
error: Schema.Schema<EI, E>,
|
|
418
|
+
value: Schema.Schema<AI, A>
|
|
122
419
|
): Schema.Schema<AsyncData.AsyncData<EI, AI>, AsyncData.AsyncData<E, A>> => {
|
|
123
420
|
return Schema.declare(
|
|
124
|
-
[cause(error), value],
|
|
421
|
+
[Schema.cause(error), value],
|
|
125
422
|
Schema.struct({}),
|
|
126
|
-
(
|
|
423
|
+
(isDecoding, ...params) => {
|
|
127
424
|
const [causeSchema, valueSchema] = params as readonly [
|
|
128
|
-
Schema.Schema<Cause.Cause<
|
|
129
|
-
Schema.Schema<
|
|
425
|
+
Schema.Schema<Cause.Cause<any>, Cause.Cause<any>>,
|
|
426
|
+
Schema.Schema<any, any>
|
|
130
427
|
]
|
|
131
|
-
const parseCause = Schema.parse(causeSchema)
|
|
132
|
-
const parseValue = Schema.parse(valueSchema)
|
|
428
|
+
const parseCause = isDecoding ? Schema.parse(causeSchema) : Schema.encode(causeSchema)
|
|
429
|
+
const parseValue = isDecoding ? Schema.parse(valueSchema) : Schema.encode(valueSchema)
|
|
133
430
|
|
|
134
431
|
return (input, options) => {
|
|
135
432
|
return Effect.gen(function*(_) {
|
|
136
|
-
if (!AsyncData.isAsyncData
|
|
433
|
+
if (!AsyncData.isAsyncData(input)) return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
137
434
|
|
|
138
435
|
switch (input._tag) {
|
|
139
436
|
case "NoData":
|
|
@@ -143,6 +440,7 @@ export const asyncData = <EI, E, AI, A>(
|
|
|
143
440
|
const cause = yield* _(parseCause(input.cause, options))
|
|
144
441
|
|
|
145
442
|
return AsyncData.failCause(cause, {
|
|
443
|
+
timestamp: input.timestamp,
|
|
146
444
|
refreshing: Option.getOrUndefined(input.refreshing)
|
|
147
445
|
})
|
|
148
446
|
}
|
|
@@ -150,6 +448,7 @@ export const asyncData = <EI, E, AI, A>(
|
|
|
150
448
|
const a = yield* _(parseValue(input.value, options))
|
|
151
449
|
|
|
152
450
|
return AsyncData.success(a, {
|
|
451
|
+
timestamp: input.timestamp,
|
|
153
452
|
refreshing: Option.getOrUndefined(input.refreshing)
|
|
154
453
|
})
|
|
155
454
|
}
|
|
@@ -159,8 +458,52 @@ export const asyncData = <EI, E, AI, A>(
|
|
|
159
458
|
},
|
|
160
459
|
{
|
|
161
460
|
[AST.IdentifierAnnotationId]: "AsyncData",
|
|
162
|
-
[
|
|
163
|
-
[
|
|
461
|
+
[Pretty.PrettyHookId]: asyncDataPretty,
|
|
462
|
+
[Arbitrary.ArbitraryHookId]: asyncDataArbitrary,
|
|
463
|
+
[Eq.EquivalenceHookId]: () => Equal.equals
|
|
164
464
|
}
|
|
165
465
|
)
|
|
166
466
|
}
|
|
467
|
+
|
|
468
|
+
function asyncDataPretty<E, A>(
|
|
469
|
+
E: Pretty.Pretty<Cause.Cause<E>>,
|
|
470
|
+
A: Pretty.Pretty<A>
|
|
471
|
+
): Pretty.Pretty<AsyncData.AsyncData<E, A>> {
|
|
472
|
+
return AsyncData.match({
|
|
473
|
+
NoData: () => NO_DATA_PRETTY,
|
|
474
|
+
Loading: LOADING_PRETTY,
|
|
475
|
+
Failure: (_, data) => FAILURE_PRETTY(E)(data),
|
|
476
|
+
Success: (_, data) => SUCCESS_PRETTY(A)(data)
|
|
477
|
+
})
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function asyncDataArbitrary<E, A>(
|
|
481
|
+
E: Arbitrary.Arbitrary<Cause.Cause<E>>,
|
|
482
|
+
A: Arbitrary.Arbitrary<A>
|
|
483
|
+
): Arbitrary.Arbitrary<AsyncData.AsyncData<E, A>> {
|
|
484
|
+
const noDataArb = Arbitrary.make(NoData)
|
|
485
|
+
const loadingArb = Arbitrary.make(Loading)
|
|
486
|
+
const failureArb = failureArbitrary(E)
|
|
487
|
+
const successArb = successArbitrary(A)
|
|
488
|
+
|
|
489
|
+
return (fc) =>
|
|
490
|
+
fc.oneof(
|
|
491
|
+
noDataArb(fc),
|
|
492
|
+
loadingArb(fc),
|
|
493
|
+
failureArb(fc),
|
|
494
|
+
successArb(fc)
|
|
495
|
+
)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function progressFromJson(json: ProgressFromTo | undefined): P.Progress | undefined {
|
|
499
|
+
if (json === undefined) return
|
|
500
|
+
return P.make(json.loaded, json.total)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function progressToJson(progres: Option.Option<P.Progress>): ProgressFromTo | undefined {
|
|
504
|
+
if (Option.isNone(progres)) return
|
|
505
|
+
return {
|
|
506
|
+
loaded: progres.value.loaded,
|
|
507
|
+
total: Option.getOrUndefined(progres.value.total)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// Internal
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import * as Cause from "effect/Cause"
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
5
|
+
import * as Effectable from "effect/Effectable"
|
|
6
|
+
import * as Equal from "effect/Equal"
|
|
7
|
+
import { constant, pipe } from "effect/Function"
|
|
8
|
+
import * as Hash from "effect/Hash"
|
|
9
|
+
import * as Option from "effect/Option"
|
|
10
|
+
import * as Unify from "effect/Unify"
|
|
5
11
|
import { type AsyncData, type Failure, type Loading, type Success } from "../AsyncData.js"
|
|
6
12
|
import { FAILURE_TAG, LOADING_TAG, NO_DATA_TAG, SUCCESS_TAG } from "./tag.js"
|
|
7
13
|
|
|
@@ -14,7 +20,7 @@ export class FailureImpl<E> extends Effectable.Class<never, E, never> implements
|
|
|
14
20
|
[Unify.unifySymbol]!: AsyncData.Unify<this>;
|
|
15
21
|
[Unify.ignoreSymbol]!: AsyncData.IgnoreList
|
|
16
22
|
|
|
17
|
-
constructor(readonly cause: Cause.Cause<E>, readonly refreshing: Option.Option<Loading>) {
|
|
23
|
+
constructor(readonly cause: Cause.Cause<E>, readonly timestamp: number, readonly refreshing: Option.Option<Loading>) {
|
|
18
24
|
super()
|
|
19
25
|
|
|
20
26
|
this.commit = constant(Effect.failCause(cause))
|
|
@@ -23,6 +29,7 @@ export class FailureImpl<E> extends Effectable.Class<never, E, never> implements
|
|
|
23
29
|
[Equal.symbol] = (that: unknown) => {
|
|
24
30
|
return isAsyncData(that) && that._tag === "Failure"
|
|
25
31
|
&& Equal.equals(this.cause, that.cause)
|
|
32
|
+
&& Equal.equals(this.timestamp, that.timestamp)
|
|
26
33
|
&& Equal.equals(this.refreshing, that.refreshing)
|
|
27
34
|
};
|
|
28
35
|
|
|
@@ -44,7 +51,7 @@ export class SuccessImpl<A> extends Effectable.Class<never, never, A> implements
|
|
|
44
51
|
[Unify.unifySymbol]!: AsyncData.Unify<this>;
|
|
45
52
|
[Unify.ignoreSymbol]!: AsyncData.IgnoreList
|
|
46
53
|
|
|
47
|
-
constructor(readonly value: A, readonly refreshing: Option.Option<Loading>) {
|
|
54
|
+
constructor(readonly value: A, readonly timestamp: number, readonly refreshing: Option.Option<Loading>) {
|
|
48
55
|
super()
|
|
49
56
|
|
|
50
57
|
this.commit = constant(Effect.succeed(value))
|
|
@@ -53,6 +60,7 @@ export class SuccessImpl<A> extends Effectable.Class<never, never, A> implements
|
|
|
53
60
|
[Equal.symbol] = (that: unknown) => {
|
|
54
61
|
return isAsyncData(that) && that._tag === "Success"
|
|
55
62
|
&& Equal.equals(this.value, that.value)
|
|
63
|
+
&& Equal.equals(this.timestamp, that.timestamp)
|
|
56
64
|
&& Equal.equals(this.refreshing, that.refreshing)
|
|
57
65
|
};
|
|
58
66
|
|