@typed/async-data 0.4.1 → 0.5.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/src/Schema.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  import * as Arbitrary from "@effect/schema/Arbitrary"
6
6
  import * as AST from "@effect/schema/AST"
7
7
  import * as Eq from "@effect/schema/Equivalence"
8
+ import * as Parser from "@effect/schema/Parser"
8
9
  import * as ParseResult from "@effect/schema/ParseResult"
9
10
  import * as Pretty from "@effect/schema/Pretty"
10
11
  import * as Schema from "@effect/schema/Schema"
@@ -51,6 +52,7 @@ const ProgressSchemaJson = Schema.struct({
51
52
  })
52
53
 
53
54
  const ProgressSchema: Schema.Schema<
55
+ never,
54
56
  {
55
57
  readonly loaded: bigint
56
58
  readonly total: Option.Option<bigint>
@@ -67,23 +69,26 @@ const progressArbitrary: Arbitrary.Arbitrary<P.Progress> = (fc) =>
67
69
  /**
68
70
  * @since 1.0.0
69
71
  */
70
- export const Progress: Schema.Schema<{ readonly loaded: string; readonly total?: string | undefined }, P.Progress> =
71
- ProgressSchemaJson.pipe(
72
- Schema.transform(
73
- ProgressSchema,
74
- (json): P.Progress => P.make(json.loaded, json.total),
75
- (progress) => ({
76
- loaded: progress.loaded,
77
- total: Option.getOrUndefined(progress.total)
78
- })
79
- ),
80
- Schema.annotations({
81
- [AST.IdentifierAnnotationId]: "Progress",
82
- [Pretty.PrettyHookId]: () => "Progress",
83
- [Arbitrary.ArbitraryHookId]: (): Arbitrary.Arbitrary<P.Progress> => progressArbitrary,
84
- [Eq.EquivalenceHookId]: () => Equal.equals
72
+ export const Progress: Schema.Schema<
73
+ never,
74
+ { readonly loaded: string; readonly total?: string | undefined },
75
+ P.Progress
76
+ > = ProgressSchemaJson.pipe(
77
+ Schema.transform(
78
+ ProgressSchema,
79
+ (json): P.Progress => P.make(json.loaded, json.total),
80
+ (progress) => ({
81
+ loaded: progress.loaded,
82
+ total: Option.getOrUndefined(progress.total)
85
83
  })
86
- )
84
+ ),
85
+ Schema.annotations({
86
+ [AST.IdentifierAnnotationId]: "Progress",
87
+ [Pretty.PrettyHookId]: () => "Progress",
88
+ [Arbitrary.ArbitraryHookId]: (): Arbitrary.Arbitrary<P.Progress> => progressArbitrary,
89
+ [Eq.EquivalenceHookId]: () => Equal.equals
90
+ })
91
+ )
87
92
 
88
93
  /**
89
94
  * @since 1.0.0
@@ -297,7 +302,7 @@ function isFailureFrom(value: unknown): value is FailureFrom<any> {
297
302
  && isCauseFrom(value.cause)
298
303
  && hasProperty(value, "timestamp")
299
304
  && typeof value.timestamp === "number"
300
- && (hasProperty(value, "refreshing") ? isLoadingFrom(value.refreshing) : true)
305
+ && (hasProperty(value, "refreshing") ? value.refreshing === undefined || isLoadingFrom(value.refreshing) : true)
301
306
  }
302
307
 
303
308
  function isSuccessFrom(value: unknown): value is SuccessFrom<any> {
@@ -306,7 +311,7 @@ function isSuccessFrom(value: unknown): value is SuccessFrom<any> {
306
311
  && hasProperty(value, "value")
307
312
  && hasProperty(value, "timestamp")
308
313
  && typeof value.timestamp === "number"
309
- && (hasProperty(value, "refreshing") ? isLoadingFrom(value.refreshing) : true)
314
+ && (hasProperty(value, "refreshing") ? value.refreshing === undefined || isLoadingFrom(value.refreshing) : true)
310
315
  }
311
316
 
312
317
  function isOptimisticFrom(value: unknown): value is OptimisticFrom<any, any> {
@@ -319,7 +324,7 @@ function isOptimisticFrom(value: unknown): value is OptimisticFrom<any, any> {
319
324
  && typeof value.timestamp === "number"
320
325
  }
321
326
 
322
- function isAsyncDataFrom(value: unknown): value is AsyncDataFrom<any, any> {
327
+ function isAsyncDataFrom<E = unknown, A = unknown>(value: unknown): value is AsyncDataFrom<E, A> {
323
328
  return isNoDataFrom(value)
324
329
  || isLoadingFrom(value)
325
330
  || isFailureFrom(value)
@@ -330,33 +335,27 @@ function isAsyncDataFrom(value: unknown): value is AsyncDataFrom<any, any> {
330
335
  /**
331
336
  * @since 1.0.0
332
337
  */
333
- export const asyncDataFromJson = <EI, E, AI, A>(
334
- error: Schema.Schema<EI, E>,
335
- value: Schema.Schema<AI, A>
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)
338
+ export const asyncDataFromJson = <R1, EI, E, R2, AI, A>(
339
+ error: Schema.Schema<R1, EI, E>,
340
+ value: Schema.Schema<R2, AI, A>
341
+ ): Schema.Schema<R1 | R2, AsyncDataFrom<EI, AI>, AsyncDataFrom<E, A>> => {
342
+ return Schema.declare(
343
+ [Schema.cause(error, Schema.unknown), value],
344
+ (causeSchema, valueSchema) => {
345
+ const parseCause = Parser.decode(causeSchema)
346
+ const parseValue = Parser.decode(valueSchema)
348
347
 
349
348
  const parseAsyncData = (
350
- input: any,
351
- options?: AST.ParseOptions
349
+ input: unknown,
350
+ options?: AST.ParseOptions | undefined
352
351
  ): Effect.Effect<
353
- never,
354
- ParseResult.ParseError,
352
+ R1 | R2,
353
+ ParseResult.ParseIssue,
355
354
  AsyncDataFrom<E, A>
356
- > =>
357
- Effect.gen(function*(_) {
358
- if (!isAsyncDataFrom(input)) {
359
- return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
355
+ > => {
356
+ return Effect.gen(function*(_) {
357
+ if (!isAsyncDataFrom<EI, AI>(input)) {
358
+ return yield* _(Effect.fail<ParseResult.ParseIssue>(ParseResult.forbidden(input)))
360
359
  }
361
360
 
362
361
  switch (input._tag) {
@@ -364,132 +363,115 @@ export const asyncDataFromJson = <EI, E, AI, A>(
364
363
  case "Loading":
365
364
  return input
366
365
  case "Failure": {
367
- const cause = yield* _(parseCause(isDecoding ? input.cause : causeFromToCause(input.cause), options))
368
- return FailureFrom(causeToCauseFrom(cause), input.timestamp, input.refreshing)
366
+ const cause = yield* _(parseCause(input.cause, options))
367
+ return FailureFrom(cause, input.timestamp, input.refreshing)
369
368
  }
370
369
  case "Success": {
371
370
  const a = yield* _(parseValue(input.value, options))
372
371
  return SuccessFrom(a, input.timestamp, input.refreshing)
373
372
  }
374
373
  case "Optimistic": {
375
- const previous: AsyncDataFrom<any, any> = yield* _(parseAsyncData(input.previous, options))
374
+ const previous = yield* _(parseAsyncData(input.previous, options))
376
375
  const value = yield* _(parseValue(input.value, options))
376
+ return OptimisticFrom(value, input.timestamp, previous)
377
+ }
378
+ }
379
+ })
380
+ }
381
+
382
+ return parseAsyncData
383
+ },
384
+ (causeSchema, valueSchema) => {
385
+ const parseCause = Parser.encode(causeSchema)
386
+ const parseValue = Parser.encode(valueSchema)
387
+
388
+ const parseAsyncData = (
389
+ input: unknown,
390
+ options?: AST.ParseOptions
391
+ ): Effect.Effect<
392
+ R1 | R2,
393
+ ParseResult.ParseIssue,
394
+ AsyncDataFrom<EI, AI>
395
+ > => {
396
+ return Effect.gen(function*(_) {
397
+ if (!isAsyncDataFrom<E, A>(input)) {
398
+ return yield* _(Effect.fail<ParseResult.ParseIssue>(ParseResult.forbidden(input)))
399
+ }
377
400
 
401
+ switch (input._tag) {
402
+ case "NoData":
403
+ case "Loading":
404
+ return input
405
+ case "Failure": {
406
+ const cause = yield* _(parseCause(causeFromToCause(input.cause), options))
407
+ return FailureFrom(cause, input.timestamp, input.refreshing)
408
+ }
409
+ case "Success": {
410
+ const a = yield* _(parseValue(input.value, options))
411
+ return SuccessFrom(a, input.timestamp, input.refreshing)
412
+ }
413
+ case "Optimistic": {
414
+ const previous = yield* _(parseAsyncData(input.previous, options))
415
+ const value = yield* _(parseValue(input.value, options))
378
416
  return OptimisticFrom(value, input.timestamp, previous)
379
417
  }
380
418
  }
381
419
  })
420
+ }
382
421
 
383
422
  return parseAsyncData
384
423
  },
385
424
  {
386
- [AST.IdentifierAnnotationId]: "AsyncDataFrom",
387
- [Eq.EquivalenceHookId]: () => fromEq
425
+ title: "AsyncDataFrom",
426
+ equivalence: () => fromEq,
427
+ arbitrary: (causeArb, valueArb) => (fc) =>
428
+ asyncDataArbitrary(causeArb, valueArb)(fc).map(asyncDataToAsyncDataFrom),
429
+ pretty: (causePretty, valuePretty) => (from) =>
430
+ asyncDataPretty(causePretty, valuePretty)(asyncDataFromToAsyncData(from))
388
431
  }
389
432
  )
433
+ }
390
434
 
391
435
  /**
392
436
  * @since 1.0.0
393
437
  */
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
-
438
+ export const asyncData = <R1, EI, E, R2, AI, A>(
439
+ errorSchema: Schema.Schema<R1, EI, E>,
440
+ valueSchema: Schema.Schema<R2, AI, A>
441
+ ): Schema.Schema<R1 | R2, AsyncDataFrom<EI, AI>, AsyncData.AsyncData<E, A>> => {
400
442
  return asyncDataFromJson(errorSchema, valueSchema)
401
- .pipe(Schema.transformOrFail(
443
+ .pipe(Schema.transform(
402
444
  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
- }
445
+ asyncDataFromToAsyncData,
446
+ asyncDataToAsyncDataFrom
462
447
  ))
463
448
  }
464
449
 
465
450
  /**
466
451
  * @since 1.0.0
467
452
  */
468
- export const asyncDataFromSelf = <EI, E, AI, A>(
469
- error: Schema.Schema<EI, E>,
470
- value: Schema.Schema<AI, A>
471
- ): Schema.Schema<AsyncData.AsyncData<EI, AI>, AsyncData.AsyncData<E, A>> => {
453
+ export const asyncDataFromSelf = <R1, EI, E, R2, AI, A>(
454
+ error: Schema.Schema<R1, EI, E>,
455
+ value: Schema.Schema<R2, AI, A>
456
+ ): Schema.Schema<R1 | R2, AsyncData.AsyncData<EI, AI>, AsyncData.AsyncData<E, A>> => {
472
457
  return Schema.declare(
473
- [Schema.cause(error), value],
474
- Schema.struct({}),
475
- (isDecoding, ...params) => {
476
- const [causeSchema, valueSchema] = params as readonly [
477
- Schema.Schema<Cause.Cause<any>, Cause.Cause<any>>,
478
- Schema.Schema<any, any>
479
- ]
480
- const parseCause = isDecoding ? Schema.decode(causeSchema) : Schema.encode(causeSchema)
481
- const parseValue = isDecoding ? Schema.decode(valueSchema) : Schema.encode(valueSchema)
458
+ [Schema.causeFromSelf(error), value],
459
+ (causeSchema, valueSchema) => {
460
+ const parseCause = Parser.decode(causeSchema)
461
+ const parseValue = Parser.decode(valueSchema)
482
462
 
483
463
  const parseAsyncData = (
484
464
  input: unknown,
485
465
  options?: AST.ParseOptions
486
466
  ): Effect.Effect<
487
- never,
488
- ParseResult.ParseError,
489
- AsyncData.AsyncData<any, any>
467
+ R1 | R2,
468
+ ParseResult.ParseIssue,
469
+ AsyncData.AsyncData<E, A>
490
470
  > => {
491
471
  return Effect.gen(function*(_) {
492
- if (!AsyncData.isAsyncData(input)) return yield* _(ParseResult.fail(ParseResult.forbidden(input)))
472
+ if (!AsyncData.isAsyncData<EI, AI>(input)) {
473
+ return yield* _(Effect.fail<ParseResult.ParseIssue>(ParseResult.forbidden(input)))
474
+ }
493
475
 
494
476
  switch (input._tag) {
495
477
  case "NoData":
@@ -498,7 +480,56 @@ export const asyncDataFromSelf = <EI, E, AI, A>(
498
480
  case "Failure": {
499
481
  const cause = yield* _(parseCause(input.cause, options))
500
482
 
501
- return AsyncData.failCause(isDecoding ? cause : causeFromToCause(cause), {
483
+ return AsyncData.failCause(cause, {
484
+ timestamp: input.timestamp,
485
+ refreshing: Option.getOrUndefined(input.refreshing)
486
+ })
487
+ }
488
+ case "Success": {
489
+ const a = yield* _(parseValue(input.value, options))
490
+
491
+ return AsyncData.success(a, {
492
+ timestamp: input.timestamp,
493
+ refreshing: Option.getOrUndefined(input.refreshing)
494
+ })
495
+ }
496
+ case "Optimistic": {
497
+ const previous = yield* _(parseAsyncData(input.previous, options))
498
+ const value = yield* _(parseValue(input.value, options))
499
+
500
+ return AsyncData.optimistic(previous, value, {
501
+ timestamp: input.timestamp
502
+ })
503
+ }
504
+ }
505
+ })
506
+ }
507
+
508
+ return parseAsyncData
509
+ },
510
+ (causeSchema, valueSchema) => {
511
+ const parseCause = Parser.encode(causeSchema)
512
+ const parseValue = Parser.encode(valueSchema)
513
+
514
+ const parseAsyncData = (
515
+ input: unknown,
516
+ options?: AST.ParseOptions
517
+ ): Effect.Effect<
518
+ R1 | R2,
519
+ ParseResult.ParseIssue,
520
+ AsyncData.AsyncData<EI, AI>
521
+ > => {
522
+ return Effect.gen(function*(_) {
523
+ if (!AsyncData.isAsyncData<E, A>(input)) return yield* _(Effect.fail(ParseResult.forbidden(input)))
524
+
525
+ switch (input._tag) {
526
+ case "NoData":
527
+ case "Loading":
528
+ return input
529
+ case "Failure": {
530
+ const cause = yield* _(parseCause(input.cause, options))
531
+
532
+ return AsyncData.failCause(cause, {
502
533
  timestamp: input.timestamp,
503
534
  refreshing: Option.getOrUndefined(input.refreshing)
504
535
  })
@@ -526,10 +557,10 @@ export const asyncDataFromSelf = <EI, E, AI, A>(
526
557
  return parseAsyncData
527
558
  },
528
559
  {
529
- [AST.IdentifierAnnotationId]: "AsyncData",
530
- [Pretty.PrettyHookId]: asyncDataPretty,
531
- [Arbitrary.ArbitraryHookId]: asyncDataArbitrary,
532
- [Eq.EquivalenceHookId]: () => Equal.equals
560
+ title: "AsyncData",
561
+ pretty: asyncDataPretty,
562
+ arbitrary: asyncDataArbitrary,
563
+ equivalence: () => Equal.equals
533
564
  }
534
565
  )
535
566
  }
@@ -654,3 +685,51 @@ function fiberIdToFiberIdFrom(id: FiberId.FiberId): Schema.FiberIdFrom {
654
685
  return { _tag: "Composite", left: fiberIdToFiberIdFrom(id.left), right: fiberIdToFiberIdFrom(id.right) }
655
686
  }
656
687
  }
688
+
689
+ const NO_DATA_FROM: NoDataFrom = { _tag: "NoData" } as const
690
+
691
+ function asyncDataToAsyncDataFrom<E, A>(data: AsyncData.AsyncData<E, A>): AsyncDataFrom<E, A> {
692
+ switch (data._tag) {
693
+ case "NoData":
694
+ return NO_DATA_FROM
695
+ case "Loading":
696
+ return loadingToJson(data)
697
+ case "Failure":
698
+ return FailureFrom(
699
+ causeToCauseFrom(data.cause),
700
+ data.timestamp,
701
+ Option.getOrUndefined(Option.map(data.refreshing, loadingToJson))
702
+ )
703
+ case "Success":
704
+ return SuccessFrom(
705
+ data.value,
706
+ data.timestamp,
707
+ Option.getOrUndefined(Option.map(data.refreshing, loadingToJson))
708
+ )
709
+ case "Optimistic":
710
+ return OptimisticFrom(data.value, data.timestamp, asyncDataToAsyncDataFrom(data.previous))
711
+ }
712
+ }
713
+
714
+ function asyncDataFromToAsyncData<E, A>(data: AsyncDataFrom<E, A>): AsyncData.AsyncData<E, A> {
715
+ switch (data._tag) {
716
+ case "NoData":
717
+ return AsyncData.noData()
718
+ case "Loading":
719
+ return loadingFromJson(data)!
720
+ case "Failure":
721
+ return AsyncData.failCause(causeFromToCause(data.cause), {
722
+ timestamp: data.timestamp,
723
+ refreshing: loadingFromJson(data.refreshing)
724
+ })
725
+ case "Success":
726
+ return AsyncData.success(data.value, {
727
+ timestamp: data.timestamp,
728
+ refreshing: loadingFromJson(data.refreshing)
729
+ })
730
+ case "Optimistic":
731
+ return AsyncData.optimistic(asyncDataFromToAsyncData(data.previous), data.value, {
732
+ timestamp: data.timestamp
733
+ })
734
+ }
735
+ }
@@ -30,12 +30,6 @@ export class FailureImpl<E> extends Effectable.Class<never, E, never> implements
30
30
 
31
31
  if (!isAsyncData(that) || that._tag !== FAILURE_TAG) return false
32
32
 
33
- console.log(
34
- Equal.equals(this.cause, that.cause),
35
- this.timestamp === that.timestamp,
36
- Equal.equals(this.refreshing, that.refreshing)
37
- )
38
-
39
33
  return Equal.equals(this.cause, that.cause)
40
34
  && this.timestamp === that.timestamp
41
35
  && Equal.equals(this.refreshing, that.refreshing)