@typed/async-data 0.1.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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/AsyncData.js +225 -0
  4. package/dist/cjs/AsyncData.js.map +1 -0
  5. package/dist/cjs/Progress.js +47 -0
  6. package/dist/cjs/Progress.js.map +1 -0
  7. package/dist/cjs/Schema.js +104 -0
  8. package/dist/cjs/Schema.js.map +1 -0
  9. package/dist/cjs/internal/async-data.js +91 -0
  10. package/dist/cjs/internal/async-data.js.map +1 -0
  11. package/dist/cjs/internal/tag.js +11 -0
  12. package/dist/cjs/internal/tag.js.map +1 -0
  13. package/dist/dts/AsyncData.d.ts +288 -0
  14. package/dist/dts/AsyncData.d.ts.map +1 -0
  15. package/dist/dts/Progress.d.ts +38 -0
  16. package/dist/dts/Progress.d.ts.map +1 -0
  17. package/dist/dts/Schema.d.ts +15 -0
  18. package/dist/dts/Schema.d.ts.map +1 -0
  19. package/dist/dts/internal/async-data.d.ts +34 -0
  20. package/dist/dts/internal/async-data.d.ts.map +1 -0
  21. package/dist/dts/internal/tag.d.ts +5 -0
  22. package/dist/dts/internal/tag.d.ts.map +1 -0
  23. package/dist/esm/AsyncData.js +203 -0
  24. package/dist/esm/AsyncData.js.map +1 -0
  25. package/dist/esm/Progress.js +38 -0
  26. package/dist/esm/Progress.js.map +1 -0
  27. package/dist/esm/Schema.js +93 -0
  28. package/dist/esm/Schema.js.map +1 -0
  29. package/dist/esm/internal/async-data.js +85 -0
  30. package/dist/esm/internal/async-data.js.map +1 -0
  31. package/dist/esm/internal/tag.js +5 -0
  32. package/dist/esm/internal/tag.js.map +1 -0
  33. package/dist/esm/package.json +4 -0
  34. package/package.json +48 -0
  35. package/src/AsyncData.ts +434 -0
  36. package/src/Progress.ts +65 -0
  37. package/src/Schema.ts +166 -0
  38. package/src/internal/async-data.ts +101 -0
  39. package/src/internal/tag.ts +7 -0
@@ -0,0 +1,434 @@
1
+ /**
2
+ * AsyncData represents a piece of data which is acquired asynchronously with loading, failure, and progress states
3
+ * in addition to Option-like states of NoData and Success.
4
+ *
5
+ * @since 1.0.0
6
+ */
7
+
8
+ import type { Effect } from "effect"
9
+ import { Cause, Data, Equal, Equivalence, Exit, Option, Unify } from "effect"
10
+ import { dual } from "effect/Function"
11
+ import * as internal from "./internal/async-data"
12
+ import { FAILURE_TAG, LOADING_TAG, NO_DATA_TAG, SUCCESS_TAG } from "./internal/tag"
13
+ import * as Progress from "./Progress"
14
+
15
+ /**
16
+ * AsyncData represents a piece of data which is acquired asynchronously with loading, failure, and progress states
17
+ * in addition to Option-like states of NoData and Success.
18
+ *
19
+ * @since 1.0.0
20
+ */
21
+ export type AsyncData<E, A> = NoData | Loading | Failure<E> | Success<A>
22
+
23
+ /**
24
+ * @since 1.0.0
25
+ */
26
+ export namespace AsyncData {
27
+ /**
28
+ * @since 1.0.0
29
+ */
30
+ export type Error<T> = [T] extends [AsyncData<infer E, infer _>] ? E : never
31
+
32
+ /**
33
+ * @since 1.0.0
34
+ */
35
+ export type Success<T> = [T] extends [AsyncData<infer _, infer A>] ? A : never
36
+
37
+ /**
38
+ * @category models
39
+ * @since 1.0.0
40
+ */
41
+ export interface Unify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
42
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
43
+ AsyncData: () => Unify_<A[Unify.typeSymbol]> extends AsyncData<infer E0, infer A0> | infer _ ? AsyncData<E0, A0>
44
+ : never
45
+ }
46
+
47
+ type Unify_<T extends AsyncData<any, any>> = T extends NoData ? AsyncData<never, never> :
48
+ T extends Loading ? AsyncData<never, never> :
49
+ T extends Failure<infer E> ? AsyncData<E, never>
50
+ : T extends Success<infer A> ? AsyncData<never, A>
51
+ : never
52
+
53
+ /**
54
+ * @category models
55
+ * @since 1.0.0
56
+ */
57
+ export interface IgnoreList extends Effect.EffectUnifyIgnore {
58
+ Effect: true
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @since 1.0.0
64
+ */
65
+ export class NoData extends Data.TaggedError(NO_DATA_TAG)<{}> {
66
+ /**
67
+ * @since 1.0.0
68
+ */
69
+ readonly [Unify.typeSymbol]!: unknown
70
+ /**
71
+ * @since 1.0.0
72
+ */
73
+ readonly [Unify.unifySymbol]!: AsyncData.Unify<this>
74
+ /**
75
+ * @since 1.0.0
76
+ */
77
+ readonly [Unify.ignoreSymbol]!: AsyncData.IgnoreList
78
+ }
79
+
80
+ /**
81
+ * @since 1.0.0
82
+ */
83
+ export const noData: {
84
+ (): NoData
85
+ <E, A>(): AsyncData<E, A>
86
+ } = (): NoData => new NoData()
87
+
88
+ /**
89
+ * @since 1.0.0
90
+ */
91
+ export class Loading extends Data.TaggedError(LOADING_TAG)<LoadingOptions> {
92
+ /**
93
+ * @since 1.0.0
94
+ */
95
+ readonly [Unify.typeSymbol]!: unknown
96
+ /**
97
+ * @since 1.0.0
98
+ */
99
+ readonly [Unify.unifySymbol]!: AsyncData.Unify<this>
100
+ /**
101
+ * @since 1.0.0
102
+ */
103
+ readonly [Unify.ignoreSymbol]!: AsyncData.IgnoreList
104
+ }
105
+
106
+ /**
107
+ * @since 1.0.0
108
+ */
109
+ export type LoadingOptions = {
110
+ readonly progress: Option.Option<Progress.Progress>
111
+ }
112
+
113
+ /**
114
+ * @since 1.0.0
115
+ */
116
+ export type OptionalPartial<A> = {
117
+ [K in keyof A]+?: [A[K]] extends [Option.Option<infer R>] ? R | undefined : A[K]
118
+ }
119
+
120
+ /**
121
+ * @since 1.0.0
122
+ */
123
+ export const loading: {
124
+ (options?: OptionalPartial<LoadingOptions>): Loading
125
+ <E, A>(options?: OptionalPartial<LoadingOptions>): AsyncData<E, A>
126
+ } = (options?: OptionalPartial<LoadingOptions>): Loading =>
127
+ new Loading({
128
+ progress: Option.fromNullable(options?.progress)
129
+ })
130
+
131
+ /**
132
+ * @since 1.0.0
133
+ */
134
+ export interface Failure<E> extends Effect.Effect<never, E, never> {
135
+ /**
136
+ * @since 1.18.0
137
+ */
138
+ readonly _tag: typeof FAILURE_TAG
139
+
140
+ /**
141
+ * @since 1.18.0
142
+ */
143
+ readonly cause: Cause.Cause<E>
144
+
145
+ /**
146
+ * @since 1.18.0
147
+ */
148
+ readonly refreshing: Option.Option<Loading>
149
+
150
+ /**
151
+ * @since 1.18.0
152
+ */
153
+ readonly [Unify.typeSymbol]: unknown
154
+
155
+ /**
156
+ * @since 1.18.0
157
+ */
158
+ readonly [Unify.unifySymbol]: AsyncData.Unify<this>
159
+
160
+ /**
161
+ * @since 1.18.0
162
+ */
163
+ readonly [Unify.ignoreSymbol]: AsyncData.IgnoreList
164
+ }
165
+
166
+ /**
167
+ * @since 1.0.0
168
+ */
169
+ export type FailureOptions = {
170
+ readonly refreshing: Option.Option<Loading>
171
+ }
172
+
173
+ /**
174
+ * @since 1.0.0
175
+ */
176
+ export const failCause: {
177
+ <E>(cause: Cause.Cause<E>, options?: OptionalPartial<FailureOptions>): Failure<E>
178
+ <E, A>(cause: Cause.Cause<E>, options?: OptionalPartial<FailureOptions>): AsyncData<E, A>
179
+ } = <E>(cause: Cause.Cause<E>, options?: OptionalPartial<FailureOptions>): Failure<E> =>
180
+ new internal.FailureImpl(
181
+ cause,
182
+ Option.fromNullable(options?.refreshing)
183
+ )
184
+
185
+ /**
186
+ * @since 1.0.0
187
+ */
188
+ export const fail: {
189
+ <E>(error: E, options?: OptionalPartial<FailureOptions>): Failure<E>
190
+ <E, A>(error: E, options?: OptionalPartial<FailureOptions>): AsyncData<E, A>
191
+ } = <E>(error: E, options?: OptionalPartial<FailureOptions>): Failure<E> => failCause<E>(Cause.fail(error), options)
192
+
193
+ /**
194
+ * @since 1.0.0
195
+ */
196
+ export interface Success<A> extends Effect.Effect<never, never, A> {
197
+ readonly _tag: typeof SUCCESS_TAG
198
+ readonly value: A
199
+ readonly refreshing: Option.Option<Loading>
200
+
201
+ readonly [Unify.typeSymbol]: unknown
202
+ readonly [Unify.unifySymbol]: AsyncData.Unify<this>
203
+ readonly [Unify.ignoreSymbol]: AsyncData.IgnoreList
204
+ }
205
+
206
+ /**
207
+ * @since 1.0.0
208
+ */
209
+ export type SuccessOptions = {
210
+ readonly refreshing: Option.Option<Loading>
211
+ }
212
+
213
+ /**
214
+ * @since 1.0.0
215
+ */
216
+ export const success: {
217
+ <A>(value: A, options?: OptionalPartial<SuccessOptions>): Success<A>
218
+ <E, A>(value: A, options?: OptionalPartial<SuccessOptions>): AsyncData<E, A>
219
+ } = <A>(value: A, options?: OptionalPartial<SuccessOptions>): Success<A> =>
220
+ new internal.SuccessImpl(
221
+ value,
222
+ Option.fromNullable(options?.refreshing)
223
+ )
224
+
225
+ /**
226
+ * @since 1.0.0
227
+ */
228
+ export const isSuccess = <E, A>(data: AsyncData<E, A>): data is Success<A> => data._tag === SUCCESS_TAG
229
+
230
+ /**
231
+ * @since 1.0.0
232
+ */
233
+ export const isFailure = <E, A>(data: AsyncData<E, A>): data is Failure<E> => data._tag === FAILURE_TAG
234
+
235
+ /**
236
+ * @since 1.0.0
237
+ */
238
+ export const isLoading = <E, A>(data: AsyncData<E, A>): data is Loading => data._tag === LOADING_TAG
239
+
240
+ /**
241
+ * @since 1.0.0
242
+ */
243
+ export const isNoData = <E, A>(data: AsyncData<E, A>): data is NoData => data._tag === NO_DATA_TAG
244
+
245
+ /**
246
+ * @since 1.0.0
247
+ */
248
+ export type Refreshing<E, A> = RefreshingFailure<E> | RefreshingSuccess<A>
249
+
250
+ /**
251
+ * @since 1.0.0
252
+ */
253
+ export interface RefreshingFailure<E> extends Failure<E> {
254
+ readonly refreshing: Option.Some<Loading>
255
+ }
256
+
257
+ /**
258
+ * @since 1.0.0
259
+ */
260
+ export interface RefreshingSuccess<A> extends Success<A> {
261
+ readonly refreshing: Option.Some<Loading>
262
+ }
263
+
264
+ /**
265
+ * @since 1.0.0
266
+ */
267
+ export const isRefreshing = <E, A>(data: AsyncData<E, A>): data is Refreshing<E, A> =>
268
+ isSuccess(data) || isFailure(data) ? Option.isSome(data.refreshing) : false
269
+
270
+ /**
271
+ * @since 1.0.0
272
+ */
273
+ export const isLoadingOrRefreshing = <E, A>(data: AsyncData<E, A>): data is Loading | Refreshing<E, A> =>
274
+ isLoading(data) || isRefreshing(data)
275
+
276
+ /**
277
+ * @since 1.0.0
278
+ */
279
+ export const match: {
280
+ <E, A, R1, R2, R3, R4>(
281
+ matchers: {
282
+ NoData: (data: NoData) => R1
283
+ Loading: (data: Loading) => R2
284
+ Failure: (cause: Cause.Cause<E>, data: Failure<E>) => R3
285
+ Success: (value: A, data: Success<A>) => R4
286
+ }
287
+ ): (data: AsyncData<E, A>) => Unify.Unify<R1 | R2 | R3 | R4>
288
+
289
+ <E, A, R1, R2, R3, R4>(
290
+ data: AsyncData<E, A>,
291
+ matchers: {
292
+ NoData: (data: NoData) => R1
293
+ Loading: (data: Loading) => R2
294
+ Failure: (cause: Cause.Cause<E>, data: Failure<E>) => R3
295
+ Success: (value: A, data: Success<A>) => R4
296
+ }
297
+ ): Unify.Unify<R1 | R2 | R3 | R4>
298
+ } = dual(2, <E, A, R1, R2, R3, R4>(data: AsyncData<E, A>, matchers: {
299
+ NoData: (data: NoData) => R1
300
+ Loading: (data: Loading) => R2
301
+ Failure: (cause: Cause.Cause<E>, data: Failure<E>) => R3
302
+ Success: (value: A, data: Success<A>) => R4
303
+ }): Unify.Unify<R1 | R2 | R3 | R4> => {
304
+ if (isSuccess(data)) {
305
+ return matchers.Success(data.value, data) as Unify.Unify<R1 | R2 | R3 | R4>
306
+ } else if (isFailure(data)) {
307
+ return matchers.Failure(data.cause, data) as Unify.Unify<R1 | R2 | R3 | R4>
308
+ } else if (isLoading(data)) {
309
+ return matchers.Loading(data) as Unify.Unify<R1 | R2 | R3 | R4>
310
+ } else {
311
+ return matchers.NoData(data) as Unify.Unify<R1 | R2 | R3 | R4>
312
+ }
313
+ })
314
+
315
+ /**
316
+ * @since 1.0.0
317
+ */
318
+ export const map: {
319
+ <A, B>(f: (a: A) => B): <E>(data: AsyncData<E, A>) => AsyncData<E, B>
320
+ <E, A, B>(data: AsyncData<E, A>, f: (a: A) => B): AsyncData<E, B>
321
+ } = dual(2, function<E, A, B>(data: AsyncData<E, A>, f: (a: A) => B): AsyncData<E, B> {
322
+ return isSuccess(data) ?
323
+ success(f(data.value), {
324
+ refreshing: Option.getOrUndefined(data.refreshing)
325
+ }) :
326
+ data
327
+ })
328
+
329
+ /**
330
+ * @since 1.0.0
331
+ */
332
+ export const flatMap: {
333
+ <A, E2, B>(f: (a: A, options: SuccessOptions) => AsyncData<E2, B>): <E>(data: AsyncData<E, A>) => AsyncData<E | E2, B>
334
+ <E, A, E2, B>(data: AsyncData<E, A>, f: (a: A, options: SuccessOptions) => AsyncData<E, B>): AsyncData<E | E2, B>
335
+ } = dual(
336
+ 2,
337
+ function<E, A, E2, B>(
338
+ data: AsyncData<E, A>,
339
+ f: (a: A, options: SuccessOptions) => AsyncData<E2, B>
340
+ ): AsyncData<E | E2, B> {
341
+ return isSuccess(data) ? f(data.value, data) : data
342
+ }
343
+ )
344
+
345
+ /**
346
+ * @since 1.0.0
347
+ */
348
+ export const startLoading = <E, A>(data: AsyncData<E, A>): AsyncData<E, A> => {
349
+ if (isSuccess(data)) {
350
+ return Option.isSome(data.refreshing) ? data : success(data.value, { ...data, refreshing: loading() })
351
+ } else if (isFailure(data)) {
352
+ return Option.isSome(data.refreshing)
353
+ ? data
354
+ : failCause(data.cause, { ...data, refreshing: loading() })
355
+ } else {
356
+ return loading()
357
+ }
358
+ }
359
+
360
+ /**
361
+ * @since 1.0.0
362
+ */
363
+ export const stopLoading = <E, A>(data: AsyncData<E, A>): AsyncData<E, A> => {
364
+ if (isSuccess(data)) {
365
+ return Option.isSome(data.refreshing) ? success(data.value) : data
366
+ } else if (isFailure(data)) {
367
+ return Option.isSome(data.refreshing) ? failCause(data.cause) : data
368
+ } else {
369
+ return noData()
370
+ }
371
+ }
372
+
373
+ /**
374
+ * @since 1.0.0
375
+ */
376
+ export const isAsyncData: <E, A>(u: unknown) => u is AsyncData<E, A> = internal.isAsyncData
377
+
378
+ /**
379
+ * @since 1.0.0
380
+ */
381
+ export const done = <E, A>(exit: Exit.Exit<E, A>): AsyncData<E, A> =>
382
+ Exit.match(exit, {
383
+ onFailure: (cause) => failCause(cause),
384
+ onSuccess: (value) => success(value)
385
+ })
386
+
387
+ /**
388
+ * @since 1.0.0
389
+ */
390
+ export const getFailure = <E, A>(data: AsyncData<E, A>): Option.Option<E> =>
391
+ isFailure(data) ? Cause.failureOption(data.cause) : Option.none()
392
+
393
+ /**
394
+ * @since 1.0.0
395
+ */
396
+ export const getSuccess = <E, A>(data: AsyncData<E, A>): Option.Option<A> =>
397
+ isSuccess(data) ? Option.some(data.value) : Option.none()
398
+
399
+ const optionProgressEq = Option.getEquivalence(Progress.equals)
400
+
401
+ const loadingEquivalence: Equivalence.Equivalence<Loading> = Equivalence.struct({
402
+ _tag: Equivalence.string,
403
+ progress: optionProgressEq
404
+ })
405
+
406
+ const optionLoadingEq = Option.getEquivalence(loadingEquivalence)
407
+
408
+ const failureEquivalence: Equivalence.Equivalence<Failure<any>> = Equivalence.struct({
409
+ _tag: Equivalence.string,
410
+ cause: Equal.equals,
411
+ refreshing: optionLoadingEq
412
+ })
413
+
414
+ const successEquivalence = <A>(valueEq: Equivalence.Equivalence<A>): Equivalence.Equivalence<Success<A>> =>
415
+ Equivalence.struct({
416
+ _tag: Equivalence.string,
417
+ value: valueEq,
418
+ refreshing: optionLoadingEq
419
+ })
420
+
421
+ /**
422
+ * @since 1.0.0
423
+ */
424
+ export const getEquivalence =
425
+ <E, A>(valueEq: Equivalence.Equivalence<A> = Equal.equals): Equivalence.Equivalence<AsyncData<E, A>> => (a, b) => {
426
+ if (a === b) return true
427
+
428
+ return match(a, {
429
+ NoData: () => isNoData(b),
430
+ Loading: (l1) => isLoading(b) ? loadingEquivalence(l1, b) : false,
431
+ Failure: (_, f1) => isFailure(b) ? failureEquivalence(f1, b) : false,
432
+ Success: (_, s1) => isSuccess(b) ? successEquivalence(valueEq)(s1, b) : false
433
+ })
434
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import { Equivalence, Option } from "effect"
6
+ import { dual } from "effect/Function"
7
+
8
+ /**
9
+ * @since 1.0.0
10
+ */
11
+ export interface Progress {
12
+ readonly loaded: bigint
13
+ readonly total: Option.Option<bigint>
14
+ }
15
+
16
+ /**
17
+ * @since 1.0.0
18
+ */
19
+ export function Progress(loaded: bigint, total: Option.Option<bigint> = Option.none()): Progress {
20
+ return {
21
+ loaded,
22
+ total
23
+ }
24
+ }
25
+
26
+ /**
27
+ * @since 1.0.0
28
+ */
29
+ export const make = (loaded: bigint, total?: bigint | null): Progress => Progress(loaded, Option.fromNullable(total))
30
+
31
+ /**
32
+ * @since 1.0.0
33
+ */
34
+ export const setLoaded: {
35
+ (loaded: bigint): (progress: Progress) => Progress
36
+ (progress: Progress, loaded: bigint): Progress
37
+ } = dual(2, function setLoaded(progress: Progress, loaded: bigint): Progress {
38
+ return Progress(
39
+ loaded,
40
+ progress.total
41
+ )
42
+ })
43
+
44
+ /**
45
+ * @since 1.0.0
46
+ */
47
+ export const setTotal: {
48
+ (total: bigint): (progress: Progress) => Progress
49
+ (progress: Progress, total: bigint): Progress
50
+ } = dual(2, function setTotal(progress: Progress, total: bigint): Progress {
51
+ return Progress(
52
+ progress.loaded,
53
+ Option.some(total)
54
+ )
55
+ })
56
+
57
+ /**
58
+ * @since 1.0.0
59
+ */
60
+ export const equals: Equivalence.Equivalence<Progress> = Equivalence.struct<
61
+ { readonly [K in keyof Progress]: Equivalence.Equivalence<Progress[K]> }
62
+ >({
63
+ loaded: Equivalence.bigint,
64
+ total: Option.getEquivalence(Equivalence.bigint)
65
+ })
package/src/Schema.ts ADDED
@@ -0,0 +1,166 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import * as Arbitrary from "@effect/schema/Arbitrary"
6
+ import * as AST from "@effect/schema/AST"
7
+ import * as ParseResult from "@effect/schema/ParseResult"
8
+ import * as Pretty from "@effect/schema/Pretty"
9
+ import * as Schema from "@effect/schema/Schema"
10
+ import * as AsyncData from "@typed/async-data/AsyncData"
11
+ import { Cause, Chunk, Effect, FiberId, HashSet } from "effect"
12
+ import * as Option from "effect/Option"
13
+
14
+ const fiberIdArbitrary: Arbitrary.Arbitrary<FiberId.FiberId> = (fc) =>
15
+ fc.oneof(
16
+ fc.constant(FiberId.none),
17
+ fc.integer().chain((i) => fc.date().map((date) => FiberId.make(i, date.getTime() / 1000)))
18
+ )
19
+
20
+ const causeFromItems = <A>(
21
+ items: Array<A>,
22
+ join: (first: Cause.Cause<A>, second: Cause.Cause<A>) => Cause.Cause<A>
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
+ }
28
+
29
+ const causeArbitrary = <A>(item: Arbitrary.Arbitrary<A>): Arbitrary.Arbitrary<Cause.Cause<A>> => (fc) =>
30
+ fc.oneof(
31
+ fc.constant(Cause.empty),
32
+ fc.anything().map(Cause.die),
33
+ fiberIdArbitrary(fc).map((id) => Cause.interrupt(id)),
34
+ fc.array(item(fc)).chain((items) =>
35
+ fc.integer({ min: 0, max: 1 }).map((i) => causeFromItems(items, i > 0.5 ? Cause.sequential : Cause.parallel))
36
+ )
37
+ )
38
+
39
+ const causePretty = <A>(): Pretty.Pretty<Cause.Cause<A>> => Cause.pretty
40
+
41
+ /**
42
+ * @since 1.0.0
43
+ */
44
+ export const cause = <EI, E>(error: Schema.Schema<EI, E>): Schema.Schema<Cause.Cause<EI>, Cause.Cause<E>> => {
45
+ const parseE = Schema.parse(Schema.chunkFromSelf(error))
46
+
47
+ const self: Schema.Schema<Cause.Cause<EI>, Cause.Cause<E>> = Schema.lazy(() =>
48
+ Schema.declare(
49
+ [error],
50
+ Schema.struct({}),
51
+ () => (input, options) =>
52
+ Effect.gen(function*(_) {
53
+ if (!Cause.isCause(input)) return yield* _(ParseResult.failure(ParseResult.unexpected(input)))
54
+
55
+ let output: Cause.Cause<E> = Cause.empty
56
+ for (const cause of Cause.linearize<E>(input)) {
57
+ const parrallelCauses = Cause.linearize(cause)
58
+
59
+ if (HashSet.size(parrallelCauses) === 1) {
60
+ const failures = Cause.failures(cause)
61
+
62
+ output = Cause.parallel(
63
+ output,
64
+ Chunk.isEmpty(failures) ? cause : Chunk.reduce(
65
+ yield* _(parseE(failures, options)),
66
+ Cause.empty as Cause.Cause<E>,
67
+ (cause, e) => Cause.sequential(cause, Cause.fail(e))
68
+ )
69
+ )
70
+ } else {
71
+ output = Cause.parallel(
72
+ output,
73
+ yield* _(Schema.parse(self)(cause, options))
74
+ )
75
+ }
76
+ }
77
+
78
+ return output
79
+ }),
80
+ {
81
+ [AST.IdentifierAnnotationId]: "Cause",
82
+ [Arbitrary.ArbitraryHookId]: causePretty,
83
+ [Pretty.PrettyHookId]: causeArbitrary
84
+ }
85
+ )
86
+ )
87
+
88
+ return self
89
+ }
90
+
91
+ const asyncDataPretty = <E, A>(
92
+ prettyCause: Pretty.Pretty<Cause.Cause<E>>,
93
+ prettyValue: Pretty.Pretty<A>
94
+ ): Pretty.Pretty<AsyncData.AsyncData<E, A>> =>
95
+ AsyncData.match({
96
+ NoData: () => `AsyncData.NoData`,
97
+ Loading: () => `AsyncData.Loading`,
98
+ Failure: (cause, data) =>
99
+ `AsyncData.Failure(refreshing=${Option.isSome(data.refreshing)}, cause=${prettyCause(cause)})`,
100
+ Success: (value, data) =>
101
+ `AsyncData.Success(refreshing=${Option.isSome(data.refreshing)}, value=${prettyValue(value)})`
102
+ })
103
+
104
+ const asyncDataArbitrary = <E, A>(
105
+ causeArbitrary: Arbitrary.Arbitrary<Cause.Cause<E>>,
106
+ valueArbitrary: Arbitrary.Arbitrary<A>
107
+ ): Arbitrary.Arbitrary<AsyncData.AsyncData<E, A>> =>
108
+ (fc) =>
109
+ fc.oneof(
110
+ fc.constant(AsyncData.noData()),
111
+ fc.constant(AsyncData.loading()),
112
+ causeArbitrary(fc).map((cause) => AsyncData.failCause(cause)),
113
+ valueArbitrary(fc).map((a) => AsyncData.success(a))
114
+ )
115
+
116
+ /**
117
+ * @since 1.0.0
118
+ */
119
+ export const asyncData = <EI, E, AI, A>(
120
+ error: Schema.Schema<EI, E>,
121
+ value: Schema.Schema<AI, A>
122
+ ): Schema.Schema<AsyncData.AsyncData<EI, AI>, AsyncData.AsyncData<E, A>> => {
123
+ return Schema.declare(
124
+ [cause(error), value],
125
+ Schema.struct({}),
126
+ (_, ...params) => {
127
+ const [causeSchema, valueSchema] = params as readonly [
128
+ Schema.Schema<Cause.Cause<EI>, Cause.Cause<E>>,
129
+ Schema.Schema<AI, A>
130
+ ]
131
+ const parseCause = Schema.parse(causeSchema)
132
+ const parseValue = Schema.parse(valueSchema)
133
+
134
+ return (input, options) => {
135
+ return Effect.gen(function*(_) {
136
+ if (!AsyncData.isAsyncData<EI, AI>(input)) return yield* _(ParseResult.failure(ParseResult.unexpected(input)))
137
+
138
+ switch (input._tag) {
139
+ case "NoData":
140
+ case "Loading":
141
+ return input
142
+ case "Failure": {
143
+ const cause = yield* _(parseCause(input.cause, options))
144
+
145
+ return AsyncData.failCause(cause, {
146
+ refreshing: Option.getOrUndefined(input.refreshing)
147
+ })
148
+ }
149
+ case "Success": {
150
+ const a = yield* _(parseValue(input.value, options))
151
+
152
+ return AsyncData.success(a, {
153
+ refreshing: Option.getOrUndefined(input.refreshing)
154
+ })
155
+ }
156
+ }
157
+ })
158
+ }
159
+ },
160
+ {
161
+ [AST.IdentifierAnnotationId]: "AsyncData",
162
+ [Arbitrary.ArbitraryHookId]: asyncDataPretty,
163
+ [Pretty.PrettyHookId]: asyncDataArbitrary
164
+ }
165
+ )
166
+ }