@typed/async-data 0.3.3 → 0.3.4
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/TypeId/package.json +6 -0
- package/dist/cjs/AsyncData.js +88 -24
- package/dist/cjs/AsyncData.js.map +1 -1
- package/dist/cjs/Schema.js +308 -143
- package/dist/cjs/Schema.js.map +1 -1
- package/dist/cjs/TypeId.js +14 -0
- package/dist/cjs/TypeId.js.map +1 -0
- package/dist/cjs/internal/async-data.js +47 -55
- package/dist/cjs/internal/async-data.js.map +1 -1
- package/dist/cjs/internal/tag.js +2 -1
- package/dist/cjs/internal/tag.js.map +1 -1
- package/dist/dts/AsyncData.d.ts +52 -9
- package/dist/dts/AsyncData.d.ts.map +1 -1
- package/dist/dts/Schema.d.ts +23 -82
- package/dist/dts/Schema.d.ts.map +1 -1
- package/dist/dts/TypeId.d.ts +12 -0
- package/dist/dts/TypeId.d.ts.map +1 -0
- package/dist/dts/internal/async-data.d.ts +23 -22
- package/dist/dts/internal/async-data.d.ts.map +1 -1
- package/dist/dts/internal/tag.d.ts +1 -0
- package/dist/dts/internal/tag.d.ts.map +1 -1
- package/dist/esm/AsyncData.js +92 -25
- package/dist/esm/AsyncData.js.map +1 -1
- package/dist/esm/Schema.js +294 -142
- package/dist/esm/Schema.js.map +1 -1
- package/dist/esm/TypeId.js +8 -0
- package/dist/esm/TypeId.js.map +1 -0
- package/dist/esm/internal/async-data.js +51 -56
- package/dist/esm/internal/async-data.js.map +1 -1
- package/dist/esm/internal/tag.js +1 -0
- package/dist/esm/internal/tag.js.map +1 -1
- package/package.json +9 -1
- package/src/AsyncData.ts +148 -32
- package/src/Schema.ts +427 -280
- package/src/TypeId.ts +13 -0
- package/src/internal/async-data.ts +65 -54
- package/src/internal/tag.ts +2 -0
package/src/Schema.ts
CHANGED
|
@@ -9,10 +9,11 @@ import * as ParseResult from "@effect/schema/ParseResult"
|
|
|
9
9
|
import * as Pretty from "@effect/schema/Pretty"
|
|
10
10
|
import * as Schema from "@effect/schema/Schema"
|
|
11
11
|
import * as AsyncData from "@typed/async-data/AsyncData"
|
|
12
|
-
import { Equal } from "effect"
|
|
13
|
-
import
|
|
12
|
+
import { Data, Equal, FiberId } from "effect"
|
|
13
|
+
import * as Cause from "effect/Cause"
|
|
14
14
|
import * as Effect from "effect/Effect"
|
|
15
15
|
import * as Option from "effect/Option"
|
|
16
|
+
import { hasProperty } from "effect/Predicate"
|
|
16
17
|
import * as P from "./Progress.js"
|
|
17
18
|
|
|
18
19
|
const NO_DATA_PRETTY = "AsyncData.NoData"
|
|
@@ -24,55 +25,30 @@ const LOADING_PRETTY = (loading: AsyncData.Loading) =>
|
|
|
24
25
|
const FAILURE_PRETTY = <E>(print: Pretty.Pretty<Cause.Cause<E>>) => (failure: AsyncData.Failure<E>) =>
|
|
25
26
|
Option.match(failure.refreshing, {
|
|
26
27
|
onNone: () => `AsyncData.Failure(timestamp=${failure.timestamp}, cause=${print(failure.cause)})`,
|
|
27
|
-
onSome: (
|
|
28
|
-
`AsyncData.Failure(timestamp=${failure.timestamp}, refreshing=${LOADING_PRETTY(loading)}, cause=${
|
|
29
|
-
print(failure.cause)
|
|
30
|
-
})`
|
|
28
|
+
onSome: () => `AsyncData.Failure(timestamp=${failure.timestamp}, refreshing=true, cause=${print(failure.cause)})`
|
|
31
29
|
})
|
|
32
30
|
const SUCCESS_PRETTY = <A>(print: Pretty.Pretty<A>) => (success: AsyncData.Success<A>) =>
|
|
33
31
|
Option.match(success.refreshing, {
|
|
34
32
|
onNone: () => `AsyncData.Success(timestamp=${success.timestamp}, value=${print(success.value)})`,
|
|
35
|
-
onSome: (
|
|
36
|
-
`AsyncData.Success(timestamp=${success.timestamp}, refreshing=${LOADING_PRETTY(loading)}, value=${
|
|
37
|
-
print(success.value)
|
|
38
|
-
})`
|
|
33
|
+
onSome: () => `AsyncData.Success(timestamp=${success.timestamp}, refreshing=true, value=${print(success.value)})`
|
|
39
34
|
})
|
|
40
35
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
const OPTIMISTIC_PRETTY =
|
|
37
|
+
<E, A>(printError: Pretty.Pretty<Cause.Cause<E>>, printValue: Pretty.Pretty<A>) =>
|
|
38
|
+
(optimistic: AsyncData.Optimistic<E, A>) =>
|
|
39
|
+
`AsyncData.Optimistic(timestamp=${optimistic.timestamp}, value=${printValue(optimistic.value)}, previous=${
|
|
40
|
+
asyncDataPretty(printError, printValue)(optimistic.previous)
|
|
41
|
+
})`
|
|
45
42
|
|
|
46
43
|
/**
|
|
47
44
|
* @since 1.0.0
|
|
48
45
|
*/
|
|
49
|
-
export
|
|
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
|
-
})
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @since 1.0.0
|
|
68
|
-
*/
|
|
69
|
-
export type NoDataFrom = Schema.Schema.From<typeof NoData>
|
|
46
|
+
export type NoDataFrom = { readonly _tag: "NoData" }
|
|
70
47
|
|
|
71
48
|
const ProgressSchemaJson = Schema.struct({
|
|
72
49
|
loaded: Schema.bigint,
|
|
73
50
|
total: Schema.optional(Schema.bigint)
|
|
74
51
|
})
|
|
75
|
-
type ProgressFromTo = Schema.Schema.To<typeof ProgressSchemaJson>
|
|
76
52
|
|
|
77
53
|
const ProgressSchema: Schema.Schema<
|
|
78
54
|
{
|
|
@@ -112,23 +88,10 @@ export const Progress: Schema.Schema<{ readonly loaded: string; readonly total?:
|
|
|
112
88
|
/**
|
|
113
89
|
* @since 1.0.0
|
|
114
90
|
*/
|
|
115
|
-
export type ProgressFrom =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
})
|
|
91
|
+
export type ProgressFrom = {
|
|
92
|
+
readonly loaded: string
|
|
93
|
+
readonly total?: string | undefined
|
|
94
|
+
}
|
|
132
95
|
|
|
133
96
|
const loadingArbitrary: Arbitrary.Arbitrary<AsyncData.Loading> = (fc) =>
|
|
134
97
|
fc.option(progressArbitrary(fc)).map((progress) =>
|
|
@@ -141,70 +104,11 @@ const loadingArbitrary: Arbitrary.Arbitrary<AsyncData.Loading> = (fc) =>
|
|
|
141
104
|
/**
|
|
142
105
|
* @since 1.0.0
|
|
143
106
|
*/
|
|
144
|
-
export
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
)
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @since 1.0.0
|
|
168
|
-
*/
|
|
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
|
-
})
|
|
200
|
-
}
|
|
201
|
-
default:
|
|
202
|
-
return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
)
|
|
107
|
+
export type LoadingFrom = {
|
|
108
|
+
readonly _tag: "Loading"
|
|
109
|
+
readonly timestamp: number
|
|
110
|
+
readonly progress?: ProgressFrom | undefined
|
|
111
|
+
}
|
|
208
112
|
|
|
209
113
|
const failureArbitrary = <E>(
|
|
210
114
|
cause: Arbitrary.Arbitrary<Cause.Cause<E>>
|
|
@@ -224,113 +128,26 @@ const failureArbitrary = <E>(
|
|
|
224
128
|
/**
|
|
225
129
|
* @since 1.0.0
|
|
226
130
|
*/
|
|
227
|
-
export
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
)
|
|
131
|
+
export type FailureFrom<E> = {
|
|
132
|
+
readonly _tag: "Failure"
|
|
133
|
+
readonly cause: Schema.CauseFrom<E>
|
|
134
|
+
readonly timestamp: number
|
|
135
|
+
readonly refreshing?: LoadingFrom | undefined
|
|
136
|
+
}
|
|
265
137
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
138
|
+
const FailureFrom = <E>(cause: Schema.CauseFrom<E>, timestamp: number, refreshing?: LoadingFrom): FailureFrom<E> => {
|
|
139
|
+
const base = {
|
|
140
|
+
_tag: "Failure",
|
|
141
|
+
cause,
|
|
142
|
+
timestamp
|
|
143
|
+
} as const
|
|
270
144
|
|
|
271
|
-
|
|
272
|
-
|
|
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
|
|
145
|
+
if (refreshing !== undefined) {
|
|
146
|
+
return { ...base, refreshing }
|
|
293
147
|
}
|
|
294
|
-
> =>
|
|
295
|
-
Schema.struct({
|
|
296
|
-
_tag: Schema.literal("Success"),
|
|
297
|
-
value,
|
|
298
|
-
timestamp: Schema.number,
|
|
299
|
-
refreshing: Schema.optional(LoadingSchemaJson)
|
|
300
|
-
})
|
|
301
148
|
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
)
|
|
149
|
+
return base
|
|
150
|
+
}
|
|
334
151
|
|
|
335
152
|
const successArbitrary = <A>(
|
|
336
153
|
value: Arbitrary.Arbitrary<A>
|
|
@@ -350,64 +167,299 @@ const successArbitrary = <A>(
|
|
|
350
167
|
/**
|
|
351
168
|
* @since 1.0.0
|
|
352
169
|
*/
|
|
353
|
-
export
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
)
|
|
170
|
+
export type SuccessFrom<A> = {
|
|
171
|
+
readonly timestamp: number
|
|
172
|
+
readonly _tag: "Success"
|
|
173
|
+
readonly value: A
|
|
174
|
+
readonly refreshing?: LoadingFrom | undefined
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const SuccessFrom = <A>(value: A, timestamp: number, refreshing?: LoadingFrom): SuccessFrom<A> => {
|
|
178
|
+
const base = {
|
|
179
|
+
_tag: "Success",
|
|
180
|
+
value,
|
|
181
|
+
timestamp
|
|
182
|
+
} as const
|
|
183
|
+
|
|
184
|
+
if (refreshing !== undefined) {
|
|
185
|
+
return { ...base, refreshing }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return base
|
|
189
|
+
}
|
|
387
190
|
|
|
388
191
|
/**
|
|
389
192
|
* @since 1.0.0
|
|
390
193
|
*/
|
|
391
|
-
export type
|
|
194
|
+
export type OptimisticFrom<E, A> = {
|
|
195
|
+
readonly timestamp: number
|
|
196
|
+
readonly _tag: "Optimistic"
|
|
197
|
+
readonly value: A
|
|
198
|
+
readonly previous: AsyncDataFrom<E, A>
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const OptimisticFrom = <E, A>(value: A, timestamp: number, previous: AsyncDataFrom<E, A>): OptimisticFrom<E, A> => ({
|
|
202
|
+
_tag: "Optimistic",
|
|
203
|
+
value,
|
|
204
|
+
timestamp,
|
|
205
|
+
previous
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
const optimisticArbitrary = <E, A>(
|
|
209
|
+
causeArb: Arbitrary.Arbitrary<Cause.Cause<E>>,
|
|
210
|
+
valueArb: Arbitrary.Arbitrary<A>
|
|
211
|
+
): Arbitrary.Arbitrary<AsyncData.Optimistic<E, A>> =>
|
|
212
|
+
(fc) =>
|
|
213
|
+
asyncDataArbitrary(causeArb, valueArb)(fc).chain((previous) =>
|
|
214
|
+
valueArb(fc).chain((value) =>
|
|
215
|
+
fc.date().map((date) => AsyncData.optimistic(previous, value, { timestamp: date.getTime() }))
|
|
216
|
+
)
|
|
217
|
+
)
|
|
392
218
|
|
|
393
219
|
/**
|
|
394
220
|
* @since 1.0.0
|
|
395
221
|
*/
|
|
396
|
-
export type AsyncDataFrom<E, A> = NoDataFrom | LoadingFrom | FailureFrom<E> | SuccessFrom<A>
|
|
222
|
+
export type AsyncDataFrom<E, A> = NoDataFrom | LoadingFrom | FailureFrom<E> | SuccessFrom<A> | OptimisticFrom<E, A>
|
|
223
|
+
|
|
224
|
+
const fromEq = (a: AsyncDataFrom<any, any>, b: AsyncDataFrom<any, any>): boolean => {
|
|
225
|
+
if (a._tag !== b._tag) return false
|
|
226
|
+
|
|
227
|
+
switch (a._tag) {
|
|
228
|
+
case "NoData":
|
|
229
|
+
return true
|
|
230
|
+
case "Loading": {
|
|
231
|
+
const loadingB = b as LoadingFrom
|
|
232
|
+
|
|
233
|
+
if (a.timestamp !== loadingB.timestamp) return false
|
|
234
|
+
|
|
235
|
+
if (a.progress === undefined && loadingB.progress === undefined) return true
|
|
236
|
+
if (a.progress === undefined || loadingB.progress === undefined) return false
|
|
237
|
+
|
|
238
|
+
return Equal.equals(Data.struct(a.progress), Data.struct(loadingB.progress))
|
|
239
|
+
}
|
|
240
|
+
case "Failure": {
|
|
241
|
+
const failureB = b as FailureFrom<any>
|
|
242
|
+
|
|
243
|
+
if (
|
|
244
|
+
!(
|
|
245
|
+
Equal.equals(Data.struct(a.cause), Data.struct(failureB.cause)) && a.timestamp === failureB.timestamp
|
|
246
|
+
)
|
|
247
|
+
) return false
|
|
248
|
+
|
|
249
|
+
if (a.refreshing === undefined && failureB.refreshing === undefined) return true
|
|
250
|
+
if (a.refreshing === undefined || failureB.refreshing === undefined) return false
|
|
251
|
+
|
|
252
|
+
return Equal.equals(Data.struct(a.refreshing), Data.struct(failureB.refreshing))
|
|
253
|
+
}
|
|
254
|
+
case "Success": {
|
|
255
|
+
const successB = b as SuccessFrom<any>
|
|
256
|
+
return Equal.equals(a.value, successB.value) &&
|
|
257
|
+
a.timestamp === successB.timestamp &&
|
|
258
|
+
Equal.equals(a.refreshing, successB.refreshing)
|
|
259
|
+
}
|
|
260
|
+
case "Optimistic": {
|
|
261
|
+
const optimisticB = b as OptimisticFrom<any, any>
|
|
262
|
+
return Equal.equals(a.value, optimisticB.value) &&
|
|
263
|
+
a.timestamp === optimisticB.timestamp &&
|
|
264
|
+
fromEq(a.previous, optimisticB.previous)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function isNoDataFrom(value: unknown): value is NoDataFrom {
|
|
270
|
+
return hasProperty(value, "_tag") && value._tag === "NoData"
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function isProgressFrom(value: unknown): value is ProgressFrom {
|
|
274
|
+
if (!(hasProperty(value, "loaded") && typeof value.loaded === "string")) return false
|
|
275
|
+
|
|
276
|
+
if (hasProperty(value, "total")) {
|
|
277
|
+
if (typeof value.total !== "string") return false
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return true
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function isLoadingFrom(value: unknown): value is LoadingFrom {
|
|
284
|
+
return hasProperty(value, "_tag")
|
|
285
|
+
&& value._tag === "Loading"
|
|
286
|
+
&& hasProperty(value, "timestamp")
|
|
287
|
+
&& typeof value.timestamp === "number"
|
|
288
|
+
&& (hasProperty(value, "progress") ? isProgressFrom(value.progress) : true)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const isCauseFrom = Schema.is(Schema.from(Schema.cause(Schema.unknown)))
|
|
292
|
+
|
|
293
|
+
function isFailureFrom(value: unknown): value is FailureFrom<any> {
|
|
294
|
+
return hasProperty(value, "_tag")
|
|
295
|
+
&& value._tag === "Failure"
|
|
296
|
+
&& hasProperty(value, "cause")
|
|
297
|
+
&& isCauseFrom(value.cause)
|
|
298
|
+
&& hasProperty(value, "timestamp")
|
|
299
|
+
&& typeof value.timestamp === "number"
|
|
300
|
+
&& (hasProperty(value, "refreshing") ? isLoadingFrom(value.refreshing) : true)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function isSuccessFrom(value: unknown): value is SuccessFrom<any> {
|
|
304
|
+
return hasProperty(value, "_tag")
|
|
305
|
+
&& value._tag === "Success"
|
|
306
|
+
&& hasProperty(value, "value")
|
|
307
|
+
&& hasProperty(value, "timestamp")
|
|
308
|
+
&& typeof value.timestamp === "number"
|
|
309
|
+
&& (hasProperty(value, "refreshing") ? isLoadingFrom(value.refreshing) : true)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function isOptimisticFrom(value: unknown): value is OptimisticFrom<any, any> {
|
|
313
|
+
return hasProperty(value, "_tag")
|
|
314
|
+
&& value._tag === "Optimistic"
|
|
315
|
+
&& hasProperty(value, "value")
|
|
316
|
+
&& hasProperty(value, "previous")
|
|
317
|
+
&& isAsyncDataFrom(value.previous)
|
|
318
|
+
&& hasProperty(value, "timestamp")
|
|
319
|
+
&& typeof value.timestamp === "number"
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function isAsyncDataFrom(value: unknown): value is AsyncDataFrom<any, any> {
|
|
323
|
+
return isNoDataFrom(value)
|
|
324
|
+
|| isLoadingFrom(value)
|
|
325
|
+
|| isFailureFrom(value)
|
|
326
|
+
|| isSuccessFrom(value)
|
|
327
|
+
|| isOptimisticFrom(value)
|
|
328
|
+
}
|
|
397
329
|
|
|
398
330
|
/**
|
|
399
331
|
* @since 1.0.0
|
|
400
332
|
*/
|
|
401
|
-
export const
|
|
333
|
+
export const asyncDataFromJson = <EI, E, AI, A>(
|
|
402
334
|
error: Schema.Schema<EI, E>,
|
|
403
335
|
value: Schema.Schema<AI, A>
|
|
404
|
-
): Schema.Schema<AsyncDataFrom<EI, AI>,
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
336
|
+
): Schema.Schema<AsyncDataFrom<EI, AI>, AsyncDataFrom<E, A>> =>
|
|
337
|
+
Schema.declare(
|
|
338
|
+
[
|
|
339
|
+
Schema.cause(error),
|
|
340
|
+
value
|
|
341
|
+
],
|
|
342
|
+
Schema.annotations({
|
|
343
|
+
[Eq.EquivalenceHookId]: () => fromEq
|
|
344
|
+
})(Schema.struct({})),
|
|
345
|
+
(isDecoding, causeSchema, valueSchema) => {
|
|
346
|
+
const parseCause = isDecoding ? Schema.decode(causeSchema) : Schema.encode(causeSchema)
|
|
347
|
+
const parseValue = isDecoding ? Schema.decode(valueSchema) : Schema.encode(valueSchema)
|
|
348
|
+
|
|
349
|
+
const parseAsyncData = (
|
|
350
|
+
input: any,
|
|
351
|
+
options?: AST.ParseOptions
|
|
352
|
+
): Effect.Effect<
|
|
353
|
+
never,
|
|
354
|
+
ParseResult.ParseError,
|
|
355
|
+
AsyncDataFrom<E, A>
|
|
356
|
+
> =>
|
|
357
|
+
Effect.gen(function*(_) {
|
|
358
|
+
if (!isAsyncDataFrom(input)) {
|
|
359
|
+
return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
switch (input._tag) {
|
|
363
|
+
case "NoData":
|
|
364
|
+
case "Loading":
|
|
365
|
+
return input
|
|
366
|
+
case "Failure": {
|
|
367
|
+
const cause = yield* _(parseCause(isDecoding ? input.cause : causeFromToCause(input.cause), options))
|
|
368
|
+
return FailureFrom(causeToCauseFrom(cause), input.timestamp, input.refreshing)
|
|
369
|
+
}
|
|
370
|
+
case "Success": {
|
|
371
|
+
const a = yield* _(parseValue(input.value, options))
|
|
372
|
+
return SuccessFrom(a, input.timestamp, input.refreshing)
|
|
373
|
+
}
|
|
374
|
+
case "Optimistic": {
|
|
375
|
+
const previous: AsyncDataFrom<any, any> = yield* _(parseAsyncData(input.previous, options))
|
|
376
|
+
const value = yield* _(parseValue(input.value, options))
|
|
377
|
+
|
|
378
|
+
return OptimisticFrom(value, input.timestamp, previous)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
return parseAsyncData
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
[AST.IdentifierAnnotationId]: "AsyncDataFrom",
|
|
387
|
+
[Eq.EquivalenceHookId]: () => fromEq
|
|
388
|
+
}
|
|
410
389
|
)
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* @since 1.0.0
|
|
393
|
+
*/
|
|
394
|
+
export const asyncData = <EI, E, AI, A>(
|
|
395
|
+
errorSchema: Schema.Schema<EI, E>,
|
|
396
|
+
valueSchema: Schema.Schema<AI, A>
|
|
397
|
+
): Schema.Schema<AsyncDataFrom<EI, AI>, AsyncData.AsyncData<E, A>> => {
|
|
398
|
+
const encodeCause = Schema.encode(Schema.cause(Schema.to(errorSchema)))
|
|
399
|
+
|
|
400
|
+
return asyncDataFromJson(errorSchema, valueSchema)
|
|
401
|
+
.pipe(Schema.transformOrFail(
|
|
402
|
+
asyncDataFromSelf(Schema.to(errorSchema), Schema.to(valueSchema)),
|
|
403
|
+
function decodeAsyncDataFrom(
|
|
404
|
+
c: AsyncDataFrom<E, A>,
|
|
405
|
+
options?: AST.ParseOptions
|
|
406
|
+
): Effect.Effect<never, ParseResult.ParseError, AsyncData.AsyncData<E, A>> {
|
|
407
|
+
switch (c._tag) {
|
|
408
|
+
case "NoData":
|
|
409
|
+
return Effect.succeed(AsyncData.noData())
|
|
410
|
+
case "Loading":
|
|
411
|
+
return Effect.succeed(loadingFromJson(c)!)
|
|
412
|
+
case "Failure": {
|
|
413
|
+
console.log(causeFromToCause(c.cause))
|
|
414
|
+
|
|
415
|
+
return Effect.succeed(
|
|
416
|
+
AsyncData.failCause(causeFromToCause(c.cause), {
|
|
417
|
+
timestamp: c.timestamp,
|
|
418
|
+
refreshing: loadingFromJson(c.refreshing)
|
|
419
|
+
})
|
|
420
|
+
)
|
|
421
|
+
}
|
|
422
|
+
case "Success": {
|
|
423
|
+
return Effect.succeed(AsyncData.success(c.value, {
|
|
424
|
+
timestamp: c.timestamp,
|
|
425
|
+
refreshing: loadingFromJson(c.refreshing)
|
|
426
|
+
}))
|
|
427
|
+
}
|
|
428
|
+
case "Optimistic": {
|
|
429
|
+
return Effect.map(
|
|
430
|
+
decodeAsyncDataFrom(c.previous, options),
|
|
431
|
+
(previous) => AsyncData.optimistic(previous, c.value, { timestamp: c.timestamp })
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
function encodeAsyncDataFrom(
|
|
437
|
+
a: AsyncData.AsyncData<E, A>,
|
|
438
|
+
options?: AST.ParseOptions
|
|
439
|
+
): Effect.Effect<never, ParseResult.ParseError, AsyncDataFrom<E, A>> {
|
|
440
|
+
switch (a._tag) {
|
|
441
|
+
case "NoData":
|
|
442
|
+
return Effect.succeed({ _tag: "NoData" })
|
|
443
|
+
case "Loading":
|
|
444
|
+
return Effect.succeed(loadingToJson(a))
|
|
445
|
+
case "Failure":
|
|
446
|
+
return Effect.map(
|
|
447
|
+
encodeCause(a.cause, options),
|
|
448
|
+
(cause) => FailureFrom(cause, a.timestamp, Option.getOrUndefined(Option.map(a.refreshing, loadingToJson)))
|
|
449
|
+
)
|
|
450
|
+
case "Success":
|
|
451
|
+
return Effect.succeed(
|
|
452
|
+
SuccessFrom(a.value, a.timestamp, Option.getOrUndefined(Option.map(a.refreshing, loadingToJson)))
|
|
453
|
+
)
|
|
454
|
+
case "Optimistic": {
|
|
455
|
+
return Effect.map(
|
|
456
|
+
encodeAsyncDataFrom(a.previous, options),
|
|
457
|
+
(previous) => OptimisticFrom(a.value, a.timestamp, previous)
|
|
458
|
+
)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
))
|
|
411
463
|
}
|
|
412
464
|
|
|
413
465
|
/**
|
|
@@ -425,10 +477,17 @@ export const asyncDataFromSelf = <EI, E, AI, A>(
|
|
|
425
477
|
Schema.Schema<Cause.Cause<any>, Cause.Cause<any>>,
|
|
426
478
|
Schema.Schema<any, any>
|
|
427
479
|
]
|
|
428
|
-
const parseCause = isDecoding ? Schema.
|
|
429
|
-
const parseValue = isDecoding ? Schema.
|
|
430
|
-
|
|
431
|
-
|
|
480
|
+
const parseCause = isDecoding ? Schema.decode(causeSchema) : Schema.encode(causeSchema)
|
|
481
|
+
const parseValue = isDecoding ? Schema.decode(valueSchema) : Schema.encode(valueSchema)
|
|
482
|
+
|
|
483
|
+
const parseAsyncData = (
|
|
484
|
+
input: unknown,
|
|
485
|
+
options?: AST.ParseOptions
|
|
486
|
+
): Effect.Effect<
|
|
487
|
+
never,
|
|
488
|
+
ParseResult.ParseError,
|
|
489
|
+
AsyncData.AsyncData<any, any>
|
|
490
|
+
> => {
|
|
432
491
|
return Effect.gen(function*(_) {
|
|
433
492
|
if (!AsyncData.isAsyncData(input)) return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
|
|
434
493
|
|
|
@@ -439,7 +498,7 @@ export const asyncDataFromSelf = <EI, E, AI, A>(
|
|
|
439
498
|
case "Failure": {
|
|
440
499
|
const cause = yield* _(parseCause(input.cause, options))
|
|
441
500
|
|
|
442
|
-
return AsyncData.failCause(cause, {
|
|
501
|
+
return AsyncData.failCause(isDecoding ? cause : causeFromToCause(cause), {
|
|
443
502
|
timestamp: input.timestamp,
|
|
444
503
|
refreshing: Option.getOrUndefined(input.refreshing)
|
|
445
504
|
})
|
|
@@ -452,9 +511,19 @@ export const asyncDataFromSelf = <EI, E, AI, A>(
|
|
|
452
511
|
refreshing: Option.getOrUndefined(input.refreshing)
|
|
453
512
|
})
|
|
454
513
|
}
|
|
514
|
+
case "Optimistic": {
|
|
515
|
+
const previous = yield* _(parseAsyncData(input.previous, options))
|
|
516
|
+
const value = yield* _(parseValue(input.value, options))
|
|
517
|
+
|
|
518
|
+
return AsyncData.optimistic(previous, value, {
|
|
519
|
+
timestamp: input.timestamp
|
|
520
|
+
})
|
|
521
|
+
}
|
|
455
522
|
}
|
|
456
523
|
})
|
|
457
524
|
}
|
|
525
|
+
|
|
526
|
+
return parseAsyncData
|
|
458
527
|
},
|
|
459
528
|
{
|
|
460
529
|
[AST.IdentifierAnnotationId]: "AsyncData",
|
|
@@ -473,7 +542,8 @@ function asyncDataPretty<E, A>(
|
|
|
473
542
|
NoData: () => NO_DATA_PRETTY,
|
|
474
543
|
Loading: LOADING_PRETTY,
|
|
475
544
|
Failure: (_, data) => FAILURE_PRETTY(E)(data),
|
|
476
|
-
Success: (_, data) => SUCCESS_PRETTY(A)(data)
|
|
545
|
+
Success: (_, data) => SUCCESS_PRETTY(A)(data),
|
|
546
|
+
Optimistic: (_, data) => OPTIMISTIC_PRETTY(E, A)(data)
|
|
477
547
|
})
|
|
478
548
|
}
|
|
479
549
|
|
|
@@ -481,29 +551,106 @@ function asyncDataArbitrary<E, A>(
|
|
|
481
551
|
E: Arbitrary.Arbitrary<Cause.Cause<E>>,
|
|
482
552
|
A: Arbitrary.Arbitrary<A>
|
|
483
553
|
): Arbitrary.Arbitrary<AsyncData.AsyncData<E, A>> {
|
|
484
|
-
const noDataArb = Arbitrary.make(NoData)
|
|
485
|
-
const loadingArb = Arbitrary.make(Loading)
|
|
486
554
|
const failureArb = failureArbitrary(E)
|
|
487
555
|
const successArb = successArbitrary(A)
|
|
556
|
+
const optimisticArb = optimisticArbitrary(E, A)
|
|
488
557
|
|
|
489
558
|
return (fc) =>
|
|
490
559
|
fc.oneof(
|
|
491
|
-
|
|
492
|
-
|
|
560
|
+
fc.constant(AsyncData.noData()),
|
|
561
|
+
fc.constant(AsyncData.loading()),
|
|
493
562
|
failureArb(fc),
|
|
494
|
-
successArb(fc)
|
|
563
|
+
successArb(fc),
|
|
564
|
+
optimisticArb(fc)
|
|
495
565
|
)
|
|
496
566
|
}
|
|
497
567
|
|
|
498
|
-
function progressFromJson(json:
|
|
499
|
-
if (json === undefined) return
|
|
500
|
-
return P.make(json.loaded, json.total)
|
|
568
|
+
function progressFromJson(json: ProgressFrom | undefined): Option.Option<P.Progress> {
|
|
569
|
+
if (json === undefined) return Option.none()
|
|
570
|
+
return Option.some(P.make(BigInt(json.loaded), json.total === undefined ? undefined : BigInt(json.total)))
|
|
501
571
|
}
|
|
502
572
|
|
|
503
|
-
function progressToJson(progres: Option.Option<P.Progress>):
|
|
573
|
+
function progressToJson(progres: Option.Option<P.Progress>): ProgressFrom | undefined {
|
|
504
574
|
if (Option.isNone(progres)) return
|
|
505
575
|
return {
|
|
506
|
-
loaded: progres.value.loaded,
|
|
507
|
-
total: Option.getOrUndefined(progres.value.total)
|
|
576
|
+
loaded: progres.value.loaded.toString(),
|
|
577
|
+
total: Option.getOrUndefined(progres.value.total)?.toString()
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function loadingFromJson(json: LoadingFrom | undefined): AsyncData.Loading | undefined {
|
|
582
|
+
if (json === undefined) return
|
|
583
|
+
return AsyncData.loading({
|
|
584
|
+
timestamp: json.timestamp,
|
|
585
|
+
progress: Option.getOrUndefined(progressFromJson(json.progress))
|
|
586
|
+
})
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function loadingToJson(loading: AsyncData.Loading): LoadingFrom {
|
|
590
|
+
const from: LoadingFrom = {
|
|
591
|
+
_tag: "Loading",
|
|
592
|
+
timestamp: loading.timestamp
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (Option.isSome(loading.progress)) {
|
|
596
|
+
return { ...from, progress: progressToJson(loading.progress) }
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return from
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function causeFromToCause<E>(from: Schema.CauseFrom<E>): Cause.Cause<E> {
|
|
603
|
+
switch (from._tag) {
|
|
604
|
+
case "Die":
|
|
605
|
+
return Cause.die(from.defect)
|
|
606
|
+
case "Empty":
|
|
607
|
+
return Cause.empty
|
|
608
|
+
case "Fail":
|
|
609
|
+
return Cause.fail(from.error)
|
|
610
|
+
case "Interrupt":
|
|
611
|
+
return Cause.interrupt(fiberIdFromToFiberId(from.fiberId))
|
|
612
|
+
case "Parallel":
|
|
613
|
+
return Cause.parallel(causeFromToCause(from.left), causeFromToCause(from.right))
|
|
614
|
+
case "Sequential":
|
|
615
|
+
return Cause.sequential(causeFromToCause(from.left), causeFromToCause(from.right))
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function fiberIdFromToFiberId(id: Schema.FiberIdFrom): FiberId.FiberId {
|
|
620
|
+
switch (id._tag) {
|
|
621
|
+
case "None":
|
|
622
|
+
return FiberId.none
|
|
623
|
+
case "Runtime":
|
|
624
|
+
return FiberId.runtime(id.id, id.startTimeMillis)
|
|
625
|
+
case "Composite":
|
|
626
|
+
return FiberId.composite(fiberIdFromToFiberId(id.left), fiberIdFromToFiberId(id.right))
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function causeToCauseFrom<E>(cause: Cause.Cause<E>): Schema.CauseFrom<E> {
|
|
631
|
+
switch (cause._tag) {
|
|
632
|
+
case "Die":
|
|
633
|
+
return { _tag: "Die", defect: cause.defect }
|
|
634
|
+
case "Empty":
|
|
635
|
+
return { _tag: "Empty" }
|
|
636
|
+
case "Fail":
|
|
637
|
+
return { _tag: "Fail", error: cause.error }
|
|
638
|
+
case "Interrupt":
|
|
639
|
+
return { _tag: "Interrupt", fiberId: fiberIdToFiberIdFrom(cause.fiberId) }
|
|
640
|
+
case "Parallel":
|
|
641
|
+
return { _tag: "Parallel", left: causeToCauseFrom(cause.left), right: causeToCauseFrom(cause.right) }
|
|
642
|
+
case "Sequential":
|
|
643
|
+
return { _tag: "Sequential", left: causeToCauseFrom(cause.left), right: causeToCauseFrom(cause.right) }
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function fiberIdToFiberIdFrom(id: FiberId.FiberId): Schema.FiberIdFrom {
|
|
648
|
+
switch (id._tag) {
|
|
649
|
+
case "None":
|
|
650
|
+
return { _tag: "None" }
|
|
651
|
+
case "Runtime":
|
|
652
|
+
return { _tag: "Runtime", id: id.id, startTimeMillis: id.startTimeMillis }
|
|
653
|
+
case "Composite":
|
|
654
|
+
return { _tag: "Composite", left: fiberIdToFiberIdFrom(id.left), right: fiberIdToFiberIdFrom(id.right) }
|
|
508
655
|
}
|
|
509
656
|
}
|