@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/AsyncData.ts
CHANGED
|
@@ -6,12 +6,22 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Effect } from "effect"
|
|
9
|
-
import
|
|
9
|
+
import * as Cause from "effect/Cause"
|
|
10
|
+
import * as Data from "effect/Data"
|
|
11
|
+
import * as Duration from "effect/Duration"
|
|
12
|
+
import * as Either from "effect/Either"
|
|
13
|
+
import * as Equal from "effect/Equal"
|
|
14
|
+
import * as Equivalence from "effect/Equivalence"
|
|
15
|
+
import * as Exit from "effect/Exit"
|
|
10
16
|
import { dual } from "effect/Function"
|
|
17
|
+
import * as Option from "effect/Option"
|
|
18
|
+
import * as Unify from "effect/Unify"
|
|
11
19
|
import * as internal from "./internal/async-data.js"
|
|
12
20
|
import { FAILURE_TAG, LOADING_TAG, NO_DATA_TAG, SUCCESS_TAG } from "./internal/tag.js"
|
|
13
21
|
import * as Progress from "./Progress.js"
|
|
14
22
|
|
|
23
|
+
const getCurrentTimestamp = () => Date.now()
|
|
24
|
+
|
|
15
25
|
/**
|
|
16
26
|
* AsyncData represents a piece of data which is acquired asynchronously with loading, failure, and progress states
|
|
17
27
|
* in addition to Option-like states of NoData and Success.
|
|
@@ -107,15 +117,18 @@ export class Loading extends Data.TaggedError(LOADING_TAG)<LoadingOptions> {
|
|
|
107
117
|
* @since 1.0.0
|
|
108
118
|
*/
|
|
109
119
|
export type LoadingOptions = {
|
|
120
|
+
readonly timestamp: number // Date.now()
|
|
110
121
|
readonly progress: Option.Option<Progress.Progress>
|
|
111
122
|
}
|
|
112
123
|
|
|
113
124
|
/**
|
|
114
125
|
* @since 1.0.0
|
|
115
126
|
*/
|
|
116
|
-
export type OptionalPartial<A> =
|
|
117
|
-
|
|
118
|
-
|
|
127
|
+
export type OptionalPartial<A> = [
|
|
128
|
+
{
|
|
129
|
+
[K in keyof A]+?: [A[K]] extends [Option.Option<infer R>] ? R | undefined : A[K]
|
|
130
|
+
}
|
|
131
|
+
] extends [infer R] ? { readonly [K in keyof R]: R[K] } : never
|
|
119
132
|
|
|
120
133
|
/**
|
|
121
134
|
* @since 1.0.0
|
|
@@ -125,13 +138,14 @@ export const loading: {
|
|
|
125
138
|
<E, A>(options?: OptionalPartial<LoadingOptions>): AsyncData<E, A>
|
|
126
139
|
} = (options?: OptionalPartial<LoadingOptions>): Loading =>
|
|
127
140
|
new Loading({
|
|
141
|
+
timestamp: options?.timestamp ?? getCurrentTimestamp(),
|
|
128
142
|
progress: Option.fromNullable(options?.progress)
|
|
129
143
|
})
|
|
130
144
|
|
|
131
145
|
/**
|
|
132
146
|
* @since 1.0.0
|
|
133
147
|
*/
|
|
134
|
-
export interface Failure<E> extends Effect.Effect<never, E, never> {
|
|
148
|
+
export interface Failure<out E> extends Effect.Effect<never, E, never> {
|
|
135
149
|
/**
|
|
136
150
|
* @since 1.18.0
|
|
137
151
|
*/
|
|
@@ -142,6 +156,11 @@ export interface Failure<E> extends Effect.Effect<never, E, never> {
|
|
|
142
156
|
*/
|
|
143
157
|
readonly cause: Cause.Cause<E>
|
|
144
158
|
|
|
159
|
+
/**
|
|
160
|
+
* @since 1.20.0
|
|
161
|
+
*/
|
|
162
|
+
readonly timestamp: number // Date.now()
|
|
163
|
+
|
|
145
164
|
/**
|
|
146
165
|
* @since 1.18.0
|
|
147
166
|
*/
|
|
@@ -167,6 +186,7 @@ export interface Failure<E> extends Effect.Effect<never, E, never> {
|
|
|
167
186
|
* @since 1.0.0
|
|
168
187
|
*/
|
|
169
188
|
export type FailureOptions = {
|
|
189
|
+
readonly timestamp: number // Date.now()
|
|
170
190
|
readonly refreshing: Option.Option<Loading>
|
|
171
191
|
}
|
|
172
192
|
|
|
@@ -179,6 +199,7 @@ export const failCause: {
|
|
|
179
199
|
} = <E>(cause: Cause.Cause<E>, options?: OptionalPartial<FailureOptions>): Failure<E> =>
|
|
180
200
|
new internal.FailureImpl(
|
|
181
201
|
cause,
|
|
202
|
+
options?.timestamp ?? getCurrentTimestamp(),
|
|
182
203
|
Option.fromNullable(options?.refreshing)
|
|
183
204
|
)
|
|
184
205
|
|
|
@@ -193,9 +214,13 @@ export const fail: {
|
|
|
193
214
|
/**
|
|
194
215
|
* @since 1.0.0
|
|
195
216
|
*/
|
|
196
|
-
export interface Success<A> extends Effect.Effect<never, never, A> {
|
|
217
|
+
export interface Success<out A> extends Effect.Effect<never, never, A> {
|
|
197
218
|
readonly _tag: typeof SUCCESS_TAG
|
|
198
219
|
readonly value: A
|
|
220
|
+
/**
|
|
221
|
+
* @since 1.20.0
|
|
222
|
+
*/
|
|
223
|
+
readonly timestamp: number // Date.now()
|
|
199
224
|
readonly refreshing: Option.Option<Loading>
|
|
200
225
|
|
|
201
226
|
readonly [Unify.typeSymbol]: unknown
|
|
@@ -207,6 +232,7 @@ export interface Success<A> extends Effect.Effect<never, never, A> {
|
|
|
207
232
|
* @since 1.0.0
|
|
208
233
|
*/
|
|
209
234
|
export type SuccessOptions = {
|
|
235
|
+
readonly timestamp: number // Date.now()
|
|
210
236
|
readonly refreshing: Option.Option<Loading>
|
|
211
237
|
}
|
|
212
238
|
|
|
@@ -219,6 +245,7 @@ export const success: {
|
|
|
219
245
|
} = <A>(value: A, options?: OptionalPartial<SuccessOptions>): Success<A> =>
|
|
220
246
|
new internal.SuccessImpl(
|
|
221
247
|
value,
|
|
248
|
+
options?.timestamp ?? getCurrentTimestamp(),
|
|
222
249
|
Option.fromNullable(options?.refreshing)
|
|
223
250
|
)
|
|
224
251
|
|
|
@@ -400,6 +427,7 @@ const optionProgressEq = Option.getEquivalence(Progress.equals)
|
|
|
400
427
|
|
|
401
428
|
const loadingEquivalence: Equivalence.Equivalence<Loading> = Equivalence.struct({
|
|
402
429
|
_tag: Equivalence.string,
|
|
430
|
+
timestamp: Equivalence.number,
|
|
403
431
|
progress: optionProgressEq
|
|
404
432
|
})
|
|
405
433
|
|
|
@@ -408,6 +436,7 @@ const optionLoadingEq = Option.getEquivalence(loadingEquivalence)
|
|
|
408
436
|
const failureEquivalence: Equivalence.Equivalence<Failure<any>> = Equivalence.struct({
|
|
409
437
|
_tag: Equivalence.string,
|
|
410
438
|
cause: Equal.equals,
|
|
439
|
+
timestamp: Equivalence.number,
|
|
411
440
|
refreshing: optionLoadingEq
|
|
412
441
|
})
|
|
413
442
|
|
|
@@ -415,6 +444,7 @@ const successEquivalence = <A>(valueEq: Equivalence.Equivalence<A>): Equivalence
|
|
|
415
444
|
Equivalence.struct({
|
|
416
445
|
_tag: Equivalence.string,
|
|
417
446
|
value: valueEq,
|
|
447
|
+
timestamp: Equivalence.number,
|
|
418
448
|
refreshing: optionLoadingEq
|
|
419
449
|
})
|
|
420
450
|
|
|
@@ -432,3 +462,73 @@ export const getEquivalence =
|
|
|
432
462
|
Success: (_, s1) => isSuccess(b) ? successEquivalence(valueEq)(s1, b) : false
|
|
433
463
|
})
|
|
434
464
|
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* @since 1.0.0
|
|
468
|
+
*/
|
|
469
|
+
export function fromExit<E, A>(exit: Exit.Exit<E, A>): AsyncData<E, A> {
|
|
470
|
+
return Exit.match(exit, {
|
|
471
|
+
onFailure: (cause) => failCause(cause),
|
|
472
|
+
onSuccess: (value) => success(value)
|
|
473
|
+
})
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* @since 1.0.0
|
|
478
|
+
*/
|
|
479
|
+
export function fromEither<E, A>(either: Either.Either<E, A>): AsyncData<E, A> {
|
|
480
|
+
return Either.match(either, {
|
|
481
|
+
onLeft: (e) => fail(e),
|
|
482
|
+
onRight: (a) => success(a)
|
|
483
|
+
})
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const isAsyncDataFirst = (args: IArguments) => args.length === 3 || isAsyncData(args[0])
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* @since 1.0.0
|
|
490
|
+
*/
|
|
491
|
+
export const isExpired: {
|
|
492
|
+
(ttl: Duration.DurationInput, now?: number): <E, A>(data: AsyncData<E, A>) => boolean
|
|
493
|
+
<E, A>(data: AsyncData<E, A>, ttl: Duration.DurationInput, now?: number): boolean
|
|
494
|
+
} = dual(isAsyncDataFirst, function isExpired<E, A>(
|
|
495
|
+
data: AsyncData<E, A>,
|
|
496
|
+
ttl: Duration.DurationInput,
|
|
497
|
+
now: number = getCurrentTimestamp()
|
|
498
|
+
): boolean {
|
|
499
|
+
return match(data, {
|
|
500
|
+
NoData: () => true,
|
|
501
|
+
Loading: () => false,
|
|
502
|
+
Failure: (_, f) =>
|
|
503
|
+
Option.isNone(f.refreshing)
|
|
504
|
+
? isPastTTL(f.timestamp, ttl, now)
|
|
505
|
+
: isPastTTL(f.refreshing.value.timestamp, ttl, now),
|
|
506
|
+
Success: (_, s) =>
|
|
507
|
+
Option.isNone(s.refreshing)
|
|
508
|
+
? isPastTTL(s.timestamp, ttl, now) :
|
|
509
|
+
isPastTTL(s.refreshing.value.timestamp, ttl, now)
|
|
510
|
+
})
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
function isPastTTL(timestamp: number, ttl: Duration.DurationInput, now: number): boolean {
|
|
514
|
+
const millis = Duration.toMillis(ttl)
|
|
515
|
+
|
|
516
|
+
return now - timestamp > millis
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Checks if two AsyncData are equal, disregarding the timestamps associated with them. Useful for testing
|
|
521
|
+
* without needing to manage timestamps.
|
|
522
|
+
*
|
|
523
|
+
* @since 1.0.0
|
|
524
|
+
*/
|
|
525
|
+
export function dataEqual<E, A>(first: AsyncData<E, A>, second: AsyncData<E, A>): boolean {
|
|
526
|
+
return match(first, {
|
|
527
|
+
NoData: () => isNoData(second),
|
|
528
|
+
Loading: (l) => isLoading(second) && Equal.equals(l.progress, second.progress),
|
|
529
|
+
Failure: (_, f1) =>
|
|
530
|
+
isFailure(second) && Equal.equals(f1.cause, second.cause) && Equal.equals(f1.refreshing, second.refreshing),
|
|
531
|
+
Success: (_, s1) =>
|
|
532
|
+
isSuccess(second) && Equal.equals(s1.value, second.value) && Equal.equals(s1.refreshing, second.refreshing)
|
|
533
|
+
})
|
|
534
|
+
}
|
package/src/Progress.ts
CHANGED
|
@@ -2,25 +2,30 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Data } from "effect"
|
|
6
|
+
import * as Equivalence from "effect/Equivalence"
|
|
6
7
|
import { dual } from "effect/Function"
|
|
8
|
+
import * as Option from "effect/Option"
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* @since 1.0.0
|
|
10
12
|
*/
|
|
11
|
-
export interface Progress
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
export interface Progress extends
|
|
14
|
+
Data.Data<{
|
|
15
|
+
readonly loaded: bigint
|
|
16
|
+
readonly total: Option.Option<bigint>
|
|
17
|
+
}>
|
|
18
|
+
{
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* @since 1.0.0
|
|
18
23
|
*/
|
|
19
24
|
export function Progress(loaded: bigint, total: Option.Option<bigint> = Option.none()): Progress {
|
|
20
|
-
return {
|
|
25
|
+
return Data.struct({
|
|
21
26
|
loaded,
|
|
22
27
|
total
|
|
23
|
-
}
|
|
28
|
+
})
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
/**
|
|
@@ -58,8 +63,23 @@ export const setTotal: {
|
|
|
58
63
|
* @since 1.0.0
|
|
59
64
|
*/
|
|
60
65
|
export const equals: Equivalence.Equivalence<Progress> = Equivalence.struct<
|
|
61
|
-
{ readonly [K in keyof Progress]: Equivalence.Equivalence<Progress[K]> }
|
|
66
|
+
{ readonly [K in Exclude<keyof Progress, keyof Data.Data<any>>]: Equivalence.Equivalence<Progress[K]> }
|
|
62
67
|
>({
|
|
63
68
|
loaded: Equivalence.bigint,
|
|
64
69
|
total: Option.getEquivalence(Equivalence.bigint)
|
|
65
70
|
})
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @since 1.0.0
|
|
74
|
+
*/
|
|
75
|
+
export function pretty(progress: Progress): string {
|
|
76
|
+
return `${progress.loaded}${
|
|
77
|
+
Option.match(
|
|
78
|
+
progress.total,
|
|
79
|
+
{
|
|
80
|
+
onNone: () => "",
|
|
81
|
+
onSome: (total) => `/${total}`
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
}`
|
|
85
|
+
}
|