@typed/fx 1.17.1 → 1.17.2

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 (41) hide show
  1. package/dist/RefRemoteData.d.ts +44 -0
  2. package/dist/RefRemoteData.d.ts.map +1 -0
  3. package/dist/RefRemoteData.js +61 -0
  4. package/dist/RefRemoteData.js.map +1 -0
  5. package/dist/RefSubject.d.ts +1 -0
  6. package/dist/RefSubject.d.ts.map +1 -1
  7. package/dist/RefSubject.js +12 -1
  8. package/dist/RefSubject.js.map +1 -1
  9. package/dist/cjs/RefRemoteData.d.ts +44 -0
  10. package/dist/cjs/RefRemoteData.d.ts.map +1 -0
  11. package/dist/cjs/RefRemoteData.js +91 -0
  12. package/dist/cjs/RefRemoteData.js.map +1 -0
  13. package/dist/cjs/RefSubject.d.ts +1 -0
  14. package/dist/cjs/RefSubject.d.ts.map +1 -1
  15. package/dist/cjs/RefSubject.js +12 -1
  16. package/dist/cjs/RefSubject.js.map +1 -1
  17. package/dist/cjs/data-first.d.ts +1 -0
  18. package/dist/cjs/data-first.d.ts.map +1 -1
  19. package/dist/cjs/data-first.js +1 -0
  20. package/dist/cjs/data-first.js.map +1 -1
  21. package/dist/cjs/index.d.ts +1 -0
  22. package/dist/cjs/index.d.ts.map +1 -1
  23. package/dist/cjs/index.js +1 -0
  24. package/dist/cjs/index.js.map +1 -1
  25. package/dist/data-first.d.ts +1 -0
  26. package/dist/data-first.d.ts.map +1 -1
  27. package/dist/data-first.js +1 -0
  28. package/dist/data-first.js.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/tsconfig.cjs.build.tsbuildinfo +1 -1
  34. package/package.json +3 -2
  35. package/src/RefRemoteData.ts +182 -0
  36. package/src/RefSubject.ts +30 -5
  37. package/src/data-first.ts +1 -0
  38. package/src/index.ts +1 -0
  39. package/tsconfig.build.tsbuildinfo +1 -1
  40. package/tsconfig.cjs.build.json +5 -1
  41. package/tsconfig.json +6 -2
@@ -0,0 +1,182 @@
1
+ import * as Either from '@effect/data/Either'
2
+ import * as Equivalence from '@effect/data/Equivalence'
3
+ import * as Option from '@effect/data/Option'
4
+ import * as Cause from '@effect/io/Cause'
5
+ import * as Effect from '@effect/io/Effect'
6
+ import * as Exit from '@effect/io/Exit'
7
+ import * as Fiber from '@effect/io/Fiber'
8
+ import * as SynchronizedRef from '@effect/io/Ref/Synchronized'
9
+ import * as Scope from '@effect/io/Scope'
10
+ import * as RemoteData from '@typed/remote-data'
11
+ import fastDeepEqual from 'fast-deep-equal'
12
+
13
+ import { Computed } from './Computed.js'
14
+ import { Fx } from './Fx.js'
15
+ import { RefSubject, makeRef } from './RefSubject.js'
16
+ import { drain } from './observe.js'
17
+ import { skipWhile } from './skipWhile.js'
18
+ import { take } from './slice.js'
19
+ import { switchMap } from './switchMap.js'
20
+
21
+ export interface RefRemoteData<E, A> extends RefSubject<never, RemoteData.RemoteData<E, A>> {
22
+ // Constructors/Setters
23
+ readonly succeed: (value: A) => Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
24
+ readonly failCause: (
25
+ error: Cause.Cause<E>,
26
+ ) => Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
27
+ readonly fail: (error: E) => Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
28
+ readonly fromExit: (
29
+ exit: Exit.Exit<E, A>,
30
+ ) => Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
31
+ readonly fromEither: (
32
+ either: Either.Either<E, A>,
33
+ ) => Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
34
+ readonly fromOption: (
35
+ option: Option.Option<A>,
36
+ ) => Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
37
+
38
+ // Computed
39
+ readonly mapValueEffect: <R2, E2, B>(
40
+ f: (a: A) => Effect.Effect<R2, E2, B>,
41
+ ) => Computed<R2, E2, RemoteData.RemoteData<E, B>>
42
+ readonly mapValue: <B>(f: (a: A) => B) => Computed<never, never, RemoteData.RemoteData<E, B>>
43
+
44
+ // Effects
45
+ readonly runEffect: <R>(
46
+ effect: Effect.Effect<R, E, A>,
47
+ ) => Effect.Effect<R | Scope.Scope, never, boolean>
48
+
49
+ readonly awaitNotLoading: Effect.Effect<Scope.Scope, never, void>
50
+
51
+ // Matching
52
+ readonly matchFx: <R2, E2, B, R3, E3, C, R4, E4, D, R5, E5, F>(options: {
53
+ onNoData: () => Fx<R2, E2, B>
54
+ onLoading: () => Fx<R3, E3, C>
55
+ onFailure: (cause: Cause.Cause<E>, refreshing: boolean) => Fx<R4, E4, D>
56
+ onSuccess: (value: A, refreshing: boolean) => Fx<R5, E5, F>
57
+ }) => Fx<R2 | R3 | R4 | R5, E2 | E3 | E4 | E5, B | C | D | F>
58
+
59
+ readonly matchEffect: <R2, E2, B, R3, E3, C, R4, E4, D, R5, E5, F>(options: {
60
+ onNoData: () => Effect.Effect<R2, E2, B>
61
+ onLoading: () => Effect.Effect<R3, E3, C>
62
+ onFailure: (cause: Cause.Cause<E>, refreshing: boolean) => Effect.Effect<R4, E4, D>
63
+ onSuccess: (value: A, refreshing: boolean) => Effect.Effect<R5, E5, F>
64
+ }) => Computed<R2 | R3 | R4 | R5, E2 | E3 | E4 | E5, B | C | D | F>
65
+
66
+ // States
67
+ readonly toLoading: Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
68
+ readonly stopLoading: Effect.Effect<never, never, RemoteData.RemoteData<E, A>>
69
+ readonly isLoading: Computed<never, never, boolean>
70
+ readonly isRefreshing: Computed<never, never, boolean>
71
+ readonly isLoadingOrRefreshing: Computed<never, never, boolean>
72
+ readonly isFailure: Computed<never, never, boolean>
73
+ readonly isSuccess: Computed<never, never, boolean>
74
+ }
75
+
76
+ export function makeRefRemoteData<E, A>(
77
+ E: Equivalence.Equivalence<E> = fastDeepEqual,
78
+ A: Equivalence.Equivalence<A> = fastDeepEqual,
79
+ ): Effect.Effect<Scope.Scope, never, RefRemoteData<E, A>> {
80
+ return Effect.gen(function* ($) {
81
+ const ref = yield* $(
82
+ makeRef(
83
+ Effect.succeed<RemoteData.RemoteData<E, A>>(RemoteData.noData),
84
+ RemoteData.getEquivalence(E, A),
85
+ ),
86
+ )
87
+ const succeed = (value: A) => ref.set(RemoteData.success(value))
88
+ const failCause = (error: Cause.Cause<E>) => ref.set(RemoteData.failCause(error))
89
+ const mapValueEffect = <R2, E2, B>(
90
+ f: (a: A, refreshing: boolean) => Effect.Effect<R2, E2, B>,
91
+ ): Computed<R2, E2, RemoteData.RemoteData<E, B>> =>
92
+ ref.mapEffect((data) =>
93
+ RemoteData.match(data, {
94
+ onNoData: () => Effect.succeed(RemoteData.noData as RemoteData.RemoteData<E, B>),
95
+ onLoading: () => Effect.succeed(RemoteData.loading),
96
+ onFailure: (cause, refreshing) => Effect.succeed(RemoteData.failCause(cause, refreshing)),
97
+ onSuccess: (a, refreshing) =>
98
+ f(a, refreshing).pipe(Effect.map((b) => RemoteData.success(b, refreshing))),
99
+ }),
100
+ )
101
+
102
+ const toLoading = ref.update(RemoteData.toLoading)
103
+ const stopLoading = ref.update(RemoteData.stopLoading)
104
+ const isLoading = ref.map(RemoteData.isLoading)
105
+ const isRefreshing = ref.map(RemoteData.isRefreshing)
106
+ const isLoadingOrRefreshing = ref.map(RemoteData.isLoadingOrRefreshing)
107
+
108
+ // Ensure there can only ever be one loading fiber
109
+ const currentFiber = yield* $(
110
+ SynchronizedRef.make<Fiber.Fiber<never, boolean>>(Fiber.succeed(false)),
111
+ )
112
+
113
+ const runEffect = <R>(effect: Effect.Effect<R, E, A>) =>
114
+ SynchronizedRef.updateAndGetEffect(currentFiber, (fiber) =>
115
+ Fiber.interrupt(fiber).pipe(
116
+ Effect.flatMap(() =>
117
+ ref.multiUpdate((current, set) =>
118
+ Effect.if(RemoteData.isLoadingOrRefreshing(current), {
119
+ onFalse: set(RemoteData.toLoading(current)).pipe(
120
+ Effect.zipRight(effect),
121
+ Effect.exit,
122
+ Effect.flatMap((exit) => set(RemoteData.fromExit(exit))),
123
+ Effect.as(true),
124
+ ),
125
+ onTrue: Effect.as(stopLoading, false),
126
+ }).pipe(Effect.onInterrupt(() => stopLoading)),
127
+ ),
128
+ ),
129
+ Effect.forkScoped,
130
+ ),
131
+ ).pipe(Effect.flatMap(Fiber.join))
132
+
133
+ const matchFx = <R2, E2, B, R3, E3, C, R4, E4, D, R5, E5, F>(options: {
134
+ onNoData: () => Fx<R2, E2, B>
135
+ onLoading: () => Fx<R3, E3, C>
136
+ onFailure: (cause: Cause.Cause<E>, refreshing: boolean) => Fx<R4, E4, D>
137
+ onSuccess: (value: A, refreshing: boolean) => Fx<R5, E5, F>
138
+ }) =>
139
+ switchMap(
140
+ ref,
141
+ (data): Fx<R2 | R3 | R4 | R5, E2 | E3 | E4 | E5, B | C | D | F> =>
142
+ RemoteData.match(data, options),
143
+ )
144
+
145
+ const matchEffect = <R2, E2, B, R3, E3, C, R4, E4, D, R5, E5, F>(options: {
146
+ onNoData: () => Effect.Effect<R2, E2, B>
147
+ onLoading: () => Effect.Effect<R3, E3, C>
148
+ onFailure: (cause: Cause.Cause<E>, refreshing: boolean) => Effect.Effect<R4, E4, D>
149
+ onSuccess: (value: A, refreshing: boolean) => Effect.Effect<R5, E5, F>
150
+ }) =>
151
+ ref.mapEffect(
152
+ (data): Effect.Effect<R2 | R3 | R4 | R5, E2 | E3 | E4 | E5, B | C | D | F> =>
153
+ RemoteData.match(data, options),
154
+ )
155
+
156
+ const awaitNotLoading = drain(take(skipWhile(ref, RemoteData.isLoadingOrRefreshing), 1))
157
+
158
+ const refRemoteData: RefRemoteData<E, A> = Object.assign(ref, {
159
+ awaitNotLoading,
160
+ fail: (error: E) => failCause(Cause.fail(error)),
161
+ failCause,
162
+ fromEither: (either: Either.Either<E, A>) => ref.set(RemoteData.fromEither(either)),
163
+ fromExit: (exit: Exit.Exit<E, A>) => ref.set(RemoteData.fromExit(exit)),
164
+ fromOption: (option: Option.Option<A>) => ref.set(RemoteData.fromOption(option)),
165
+ isFailure: ref.map(RemoteData.isFailure),
166
+ isLoading,
167
+ isLoadingOrRefreshing,
168
+ isRefreshing,
169
+ isSuccess: ref.map(RemoteData.isSuccess),
170
+ mapValue: <B>(f: (a: A) => B) => mapValueEffect((a) => Effect.sync(() => f(a))),
171
+ mapValueEffect,
172
+ matchEffect: matchEffect,
173
+ matchFx: matchFx,
174
+ runEffect,
175
+ stopLoading,
176
+ succeed,
177
+ toLoading,
178
+ })
179
+
180
+ return refRemoteData
181
+ })
182
+ }
package/src/RefSubject.ts CHANGED
@@ -61,6 +61,10 @@ export interface RefSubject<in out E, in out A>
61
61
 
62
62
  readonly delete: Effect.Effect<never, E, Option.Option<A>>
63
63
 
64
+ readonly multiUpdate: <R2, E2, B>(
65
+ f: (current: A, set: (a: A) => Effect.Effect<never, never, A>) => Effect.Effect<R2, E2, B>,
66
+ ) => Effect.Effect<R2, E | E2, B>
67
+
64
68
  /**
65
69
  * The current version of the RefSubject, starting with 0, 1 when initialized,
66
70
  * and incremented each time the value is updated.
@@ -252,6 +256,14 @@ function makeModifyEffectFromContext<E, A>(
252
256
  )
253
257
  }
254
258
 
259
+ function makeMultiUpdateFromContext<E, A>(
260
+ get: RefSubject<E, A>['get'],
261
+ set: RefSubject<E, A>['set'],
262
+ lock: RefSubjectContext<E, A>['lock'],
263
+ ): RefSubject<E, A>['multiUpdate'] {
264
+ return (f) => get.pipe(Effect.flatMap((current) => lock(f(current, set))))
265
+ }
266
+
255
267
  function makeModify<E, A>(
256
268
  modifyEffect: RefSubject<E, A>['modifyEffect'],
257
269
  ): RefSubject<E, A>['modify'] {
@@ -506,6 +518,7 @@ function unsafeMakeRefPrimitive<E, A>(
506
518
  const ctx = makeRefSubjectContext(initial, scope, eq)
507
519
  const get = makeGetFromContext(ctx)
508
520
  const set = makeSetFromContext(ctx)
521
+ const multiUpdate = makeMultiUpdateFromContext(get, set, ctx.lock)
509
522
 
510
523
  function run<R2>(sink: Sink<R2, E, A>) {
511
524
  return Effect.suspend(() => {
@@ -533,6 +546,7 @@ function unsafeMakeRefPrimitive<E, A>(
533
546
  modifyEffect: makeModifyEffectFromContext(get, ctx),
534
547
  run,
535
548
  set,
549
+ multiUpdate,
536
550
  version: ctx.version,
537
551
  }
538
552
 
@@ -575,6 +589,7 @@ interface RefPrimitive<E, A> {
575
589
  modifyEffect: RefSubject<E, A>['modifyEffect']
576
590
  set: RefSubject<E, A>['set']
577
591
  delete: RefSubject<E, A>['delete']
592
+ multiUpdate: RefSubject<E, A>['multiUpdate']
578
593
 
579
594
  // Primitive
580
595
  version: MutableRef.MutableRef<number>
@@ -594,8 +609,15 @@ function tupleRefPrimitive<const Refs extends ReadonlyArray<RefSubject.Any>>(
594
609
  refs.map((ref) => ref.get),
595
610
  unboundedConcurrency,
596
611
  ) as Effect.Effect<never, _E, _A>
612
+ const set = (value: { readonly [K in keyof Refs]: Fx.Success<Refs[K]> }) =>
613
+ Effect.all(
614
+ value.map((v, i) => refs[i].set(v)),
615
+ unboundedConcurrency,
616
+ ) as Effect.Effect<never, never, _A>
617
+ const multiUpdate = makeMultiUpdateFromContext(get, set, identity)
597
618
 
598
619
  const primitive: RefPrimitive<_E, _A> = {
620
+ multiUpdate,
599
621
  delete: Effect.map(
600
622
  Effect.all(
601
623
  refs.map((ref) => ref.delete),
@@ -618,11 +640,7 @@ function tupleRefPrimitive<const Refs extends ReadonlyArray<RefSubject.Any>>(
618
640
  get,
619
641
  modifyEffect: makeModifyEffectTuple(refs, get, eq),
620
642
  run: (sink) => hold.run(sink),
621
- set: (value) =>
622
- Effect.all(
623
- value.map((v, i) => refs[i].set(v)),
624
- unboundedConcurrency,
625
- ) as Effect.Effect<never, never, _A>,
643
+ set,
626
644
  version: MutableRef.make(0),
627
645
  }
628
646
 
@@ -671,8 +689,15 @@ function structRefPrimitive<const Refs extends Readonly<Record<string, RefSubjec
671
689
  mapRecord(refs, (ref) => ref.get),
672
690
  unboundedConcurrency,
673
691
  ) as Effect.Effect<never, _E, _A>
692
+ const set = (value: { readonly [K in keyof Refs]: Fx.Success<Refs[K]> }) =>
693
+ Effect.all(
694
+ mapRecord(value, (v, i) => refs[i].set(v)),
695
+ unboundedConcurrency,
696
+ ) as Effect.Effect<never, never, _A>
697
+ const multiUpdate = makeMultiUpdateFromContext(get, set, identity)
674
698
 
675
699
  const primitive: RefPrimitive<_E, _A> = {
700
+ multiUpdate,
676
701
  delete: Effect.map(
677
702
  Effect.all(
678
703
  mapRecord(refs, (ref) => ref.delete),
package/src/data-first.ts CHANGED
@@ -64,3 +64,4 @@ export * from './toArray.js'
64
64
  export * from './toChunk.js'
65
65
  export * from './toEnqueue.js'
66
66
  export * from './toReadonlyArray.js'
67
+ export * from './RefRemoteData.js'
package/src/index.ts CHANGED
@@ -1080,3 +1080,4 @@ export * from './Filtered.js'
1080
1080
  export * from './RefTransform.js'
1081
1081
  export * from './Subject.js'
1082
1082
  export * from './toStream.js'
1083
+ export * from './RefRemoteData.js'