@typed/fx 0.0.16 → 0.0.18
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/.eslintcache +1 -1
- package/cjs/Subject.d.ts +15 -10
- package/cjs/Subject.d.ts.map +1 -1
- package/cjs/Subject.js +123 -21
- package/cjs/Subject.js.map +1 -1
- package/cjs/hold.d.ts +2 -2
- package/cjs/hold.d.ts.map +1 -1
- package/cjs/hold.js +5 -5
- package/cjs/hold.js.map +1 -1
- package/cjs/withEmitter.d.ts +2 -2
- package/cjs/withEmitter.d.ts.map +1 -1
- package/cjs/withEmitter.js +3 -3
- package/cjs/withEmitter.js.map +1 -1
- package/esm/Subject.d.ts +15 -10
- package/esm/Subject.d.ts.map +1 -1
- package/esm/Subject.js +123 -21
- package/esm/Subject.js.map +1 -1
- package/esm/hold.d.ts +2 -2
- package/esm/hold.d.ts.map +1 -1
- package/esm/hold.js +5 -5
- package/esm/hold.js.map +1 -1
- package/esm/withEmitter.d.ts +2 -2
- package/esm/withEmitter.d.ts.map +1 -1
- package/esm/withEmitter.js +3 -3
- package/esm/withEmitter.js.map +1 -1
- package/package.json +1 -1
- package/src/Subject.test.ts +72 -5
- package/src/Subject.ts +162 -32
- package/src/hold.ts +5 -6
- package/src/withEmitter.test.ts +4 -4
- package/src/withEmitter.ts +5 -5
package/src/Subject.ts
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { Cause } from '@effect/core/io/Cause'
|
|
2
2
|
import * as Effect from '@effect/core/io/Effect'
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import * as Ref from '@effect/core/io/Ref'
|
|
4
|
+
import { AtomicInternal, UnsafeAPI } from '@effect/core/io/Ref/operations/_internal/AtomicInternal'
|
|
5
|
+
import { SynchronizedInternal } from '@effect/core/io/Ref/operations/_internal/SynchronizedInternal'
|
|
6
|
+
import * as TSemaphore from '@effect/core/stm/TSemaphore'
|
|
7
|
+
import { flow } from '@fp-ts/data/Function'
|
|
8
|
+
import { LazyArg, pipe } from '@tsplus/stdlib/data/Function'
|
|
5
9
|
import * as Maybe from '@tsplus/stdlib/data/Maybe'
|
|
6
10
|
|
|
7
|
-
import { Fx } from './Fx.js'
|
|
11
|
+
import { Emitter, Fx } from './Fx.js'
|
|
8
12
|
import { never } from './fromEffect.js'
|
|
9
13
|
import { Hold } from './hold.js'
|
|
10
14
|
import { Multicast } from './multicast.js'
|
|
11
15
|
|
|
12
|
-
export interface Subject<E, A> extends
|
|
16
|
+
export interface Subject<E, A> extends Fx<never, E, A>, Emitter<never, E, A>, UnsafeEmitter<E, A> {}
|
|
13
17
|
|
|
14
|
-
export interface
|
|
15
|
-
readonly
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
+
export interface UnsafeEmitter<E, A> {
|
|
19
|
+
readonly unsafeEmit: (a: A) => void
|
|
20
|
+
readonly unsafeFailCause: (cause: Cause<E>) => void
|
|
21
|
+
readonly unsafeEnd: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const FX_BRANDING = {
|
|
25
|
+
_R: () => void 0 as never,
|
|
26
|
+
_E: () => void 0 as any,
|
|
27
|
+
_A: () => void 0 as any,
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
export namespace Subject {
|
|
@@ -22,11 +32,15 @@ export namespace Subject {
|
|
|
22
32
|
const m = new Multicast<never, E, A>(never)
|
|
23
33
|
|
|
24
34
|
return {
|
|
35
|
+
...FX_BRANDING,
|
|
25
36
|
run: m.run.bind(m),
|
|
26
|
-
emit:
|
|
27
|
-
failCause:
|
|
28
|
-
end:
|
|
29
|
-
|
|
37
|
+
emit: m.emit.bind(m),
|
|
38
|
+
failCause: m.failCause.bind(m),
|
|
39
|
+
end: m.end,
|
|
40
|
+
unsafeEmit: (a) => Effect.unsafeRunAsync(m.emit(a)),
|
|
41
|
+
unsafeFailCause: (c) => Effect.unsafeRunAsync(m.failCause(c)),
|
|
42
|
+
unsafeEnd: () => Effect.unsafeRunAsync(m.end),
|
|
43
|
+
}
|
|
30
44
|
}
|
|
31
45
|
}
|
|
32
46
|
|
|
@@ -39,34 +53,150 @@ export namespace HoldSubject {
|
|
|
39
53
|
const h = new Hold<never, E, A>(never)
|
|
40
54
|
|
|
41
55
|
return {
|
|
42
|
-
|
|
43
|
-
get: h.get,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
...FX_BRANDING,
|
|
57
|
+
get: Effect.sync(() => h.value.get),
|
|
58
|
+
run: (e) => h.run(e),
|
|
59
|
+
emit: (a) => h.emit(a),
|
|
60
|
+
failCause: (e) => h.failCause(e),
|
|
61
|
+
end: h.end,
|
|
62
|
+
unsafeEmit: (a) => Effect.unsafeRunAsync(h.emit(a)),
|
|
63
|
+
unsafeFailCause: (c) => Effect.unsafeRunAsync(h.failCause(c)),
|
|
64
|
+
unsafeEnd: () => Effect.unsafeRunAsync(h.end),
|
|
65
|
+
}
|
|
48
66
|
}
|
|
49
67
|
}
|
|
50
68
|
|
|
51
|
-
export interface
|
|
52
|
-
readonly get: Effect.Effect<never, never, A>
|
|
53
|
-
}
|
|
69
|
+
export interface RefSubject<E, A> extends Subject<E, A>, Ref.Ref<A> {}
|
|
54
70
|
|
|
55
|
-
export namespace
|
|
56
|
-
export const unsafeMake = <E, A>(initial: LazyArg<A>):
|
|
71
|
+
export namespace RefSubject {
|
|
72
|
+
export const unsafeMake = <E, A>(initial: LazyArg<A>): RefSubject<E, A> => {
|
|
57
73
|
const h = new Hold<never, E, A>(never)
|
|
74
|
+
const maybeRef: Ref.Ref<Maybe.Maybe<A>> = new AtomicInternal(new UnsafeAPI(h.value))
|
|
75
|
+
const ref = emitRefChanges(invmapRef(maybeRef, Maybe.getOrElse(initial), Maybe.some), h)
|
|
58
76
|
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
// @ts-expect-error
|
|
62
|
-
h._value = Maybe.some(initial())
|
|
77
|
+
// Ensure there is always a value in the Ref
|
|
78
|
+
h.value.set(Maybe.some(initial()))
|
|
63
79
|
|
|
64
80
|
return {
|
|
81
|
+
...FX_BRANDING,
|
|
82
|
+
[Ref.RefSym]: Ref.RefSym,
|
|
83
|
+
[Ref._A]: (_) => _,
|
|
84
|
+
get: ref.get,
|
|
85
|
+
modify: (f) => ref.modify(f),
|
|
86
|
+
set: (a) => ref.set(a),
|
|
87
|
+
getAndSet: (a) => ref.getAndSet(a),
|
|
88
|
+
getAndUpdate: (f) => ref.getAndUpdate(f),
|
|
89
|
+
getAndUpdateSome: (f) => ref.getAndUpdateSome(f),
|
|
90
|
+
modifySome: (fallback, f) => ref.modifySome(fallback, f),
|
|
91
|
+
update: (f) => ref.update(f),
|
|
92
|
+
updateAndGet: (f) => ref.updateAndGet(f),
|
|
93
|
+
updateSome: (f) => ref.updateSome(f),
|
|
94
|
+
updateSomeAndGet: (f) => ref.updateSomeAndGet(f),
|
|
65
95
|
run: h.run.bind(h),
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
96
|
+
emit: h.emit.bind(h),
|
|
97
|
+
failCause: h.failCause.bind(h),
|
|
98
|
+
end: h.end,
|
|
99
|
+
unsafeEmit: (a) => Effect.unsafeRunAsync(h.emit(a)),
|
|
100
|
+
unsafeFailCause: (c) => Effect.unsafeRunAsync(h.failCause(c)),
|
|
101
|
+
unsafeEnd: () => Effect.unsafeRunAsync(h.end),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface SynchronizedSubject<E, A> extends RefSubject<E, A>, Ref.Ref.Synchronized<A> {}
|
|
107
|
+
|
|
108
|
+
export namespace SynchronizedSubject {
|
|
109
|
+
export const unsafeMake = <E, A>(initial: LazyArg<A>): SynchronizedSubject<E, A> => {
|
|
110
|
+
const subject = RefSubject.unsafeMake<E, A>(initial)
|
|
111
|
+
const synchronizedRef = new SynchronizedInternal(subject, TSemaphore.unsafeMake(1))
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
...subject,
|
|
115
|
+
[Ref.SynchronizedSym]: Ref.SynchronizedSym,
|
|
116
|
+
modifyEffect: (f) => synchronizedRef.modifyEffect(f),
|
|
117
|
+
modifySomeEffect: (fallback, f) => synchronizedRef.modifySomeEffect(fallback, f),
|
|
118
|
+
getAndUpdateEffect: (f) => synchronizedRef.getAndUpdateEffect(f),
|
|
119
|
+
getAndUpdateSomeEffect: (f) => synchronizedRef.getAndUpdateSomeEffect(f),
|
|
120
|
+
updateEffect: (f) => synchronizedRef.updateEffect(f),
|
|
121
|
+
updateAndGetEffect: (f) => synchronizedRef.updateAndGetEffect(f),
|
|
122
|
+
updateSomeEffect: (f) => synchronizedRef.updateSomeEffect(f),
|
|
123
|
+
updateSomeAndGetEffect: (f) => synchronizedRef.updateSomeAndGetEffect(f),
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function invmapRef<A, B>(ref: Ref.Ref<A>, to: (a: A) => B, from: (b: B) => A): Ref.Ref<B> {
|
|
129
|
+
const get: Ref.Ref<B>['get'] = pipe(ref.get, Effect.map(to))
|
|
130
|
+
|
|
131
|
+
const modify: Ref.Ref<B>['modify'] = (f) =>
|
|
132
|
+
ref.modify((a) => {
|
|
133
|
+
const [b, c] = f(to(a))
|
|
134
|
+
return [b, from(c)]
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const set: Ref.Ref<B>['set'] = flow(from, ref.set)
|
|
138
|
+
|
|
139
|
+
const getAndSet: Ref.Ref<B>['getAndSet'] = flow(from, ref.getAndSet, Effect.map(to))
|
|
140
|
+
|
|
141
|
+
const getAndUpdate: Ref.Ref<B>['getAndUpdate'] = (f) =>
|
|
142
|
+
pipe(ref.getAndUpdate(flow(to, f, from)), Effect.map(to))
|
|
143
|
+
|
|
144
|
+
const getAndUpdateSome: Ref.Ref<B>['getAndUpdateSome'] = (f) =>
|
|
145
|
+
pipe(ref.getAndUpdateSome(flow(to, f, Maybe.map(from))), Effect.map(to))
|
|
146
|
+
|
|
147
|
+
const modifySome: Ref.Ref<B>['modifySome'] = (fallback, f) =>
|
|
148
|
+
ref.modifySome(fallback, (a) =>
|
|
149
|
+
pipe(
|
|
150
|
+
a,
|
|
151
|
+
to,
|
|
152
|
+
f,
|
|
153
|
+
Maybe.map(([c, b]) => [c, from(b)]),
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
const update: Ref.Ref<B>['update'] = (f) => ref.update(flow(to, f, from))
|
|
158
|
+
|
|
159
|
+
const updateAndGet: Ref.Ref<B>['updateAndGet'] = (f) =>
|
|
160
|
+
pipe(ref.updateAndGet(flow(to, f, from)), Effect.map(to))
|
|
161
|
+
|
|
162
|
+
const updateSome: Ref.Ref<B>['updateSome'] = (f) => ref.updateSome(flow(to, f, Maybe.map(from)))
|
|
163
|
+
|
|
164
|
+
const updateSomeAndGet: Ref.Ref<B>['updateSomeAndGet'] = (f) =>
|
|
165
|
+
pipe(ref.updateSomeAndGet(flow(to, f, Maybe.map(from))), Effect.map(to))
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
[Ref.RefSym]: Ref.RefSym,
|
|
169
|
+
[Ref._A]: (_) => _,
|
|
170
|
+
get,
|
|
171
|
+
modify,
|
|
172
|
+
set,
|
|
173
|
+
getAndSet,
|
|
174
|
+
getAndUpdate,
|
|
175
|
+
getAndUpdateSome,
|
|
176
|
+
modifySome,
|
|
177
|
+
update,
|
|
178
|
+
updateAndGet,
|
|
179
|
+
updateSome,
|
|
180
|
+
updateSomeAndGet,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function emitRefChanges<A, E>(ref: Ref.Ref<A>, subject: Emitter<never, E, A>): Ref.Ref<A> {
|
|
185
|
+
const andEmitLatestValue = Effect.zipLeft(pipe(ref.get, Effect.flatMap(subject.emit)))
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
[Ref.RefSym]: Ref.RefSym,
|
|
189
|
+
[Ref._A]: (_) => _,
|
|
190
|
+
get: ref.get,
|
|
191
|
+
modify: (f) => pipe(ref.modify(f), andEmitLatestValue),
|
|
192
|
+
set: (a) => pipe(ref.set(a), andEmitLatestValue),
|
|
193
|
+
getAndSet: (a) => pipe(ref.getAndSet(a), andEmitLatestValue),
|
|
194
|
+
getAndUpdate: (f) => pipe(ref.getAndUpdate(f), andEmitLatestValue),
|
|
195
|
+
getAndUpdateSome: (f) => pipe(ref.getAndUpdateSome(f), andEmitLatestValue),
|
|
196
|
+
modifySome: (fallback, f) => pipe(ref.modifySome(fallback, f), andEmitLatestValue),
|
|
197
|
+
update: (f) => pipe(ref.update(f), andEmitLatestValue),
|
|
198
|
+
updateAndGet: (f) => pipe(ref.updateAndGet(f), andEmitLatestValue),
|
|
199
|
+
updateSome: (f) => pipe(ref.updateSome(f), andEmitLatestValue),
|
|
200
|
+
updateSomeAndGet: (f) => pipe(ref.updateSomeAndGet(f), andEmitLatestValue),
|
|
71
201
|
}
|
|
72
202
|
}
|
package/src/hold.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as Fiber from '@effect/core/io/Fiber'
|
|
|
4
4
|
import * as FiberId from '@effect/core/io/FiberId'
|
|
5
5
|
import { Scope } from '@effect/core/io/Scope'
|
|
6
6
|
import { pipe } from '@fp-ts/data/Function'
|
|
7
|
+
import { AtomicReference } from '@tsplus/stdlib/data/AtomicReference'
|
|
7
8
|
import * as Duration from '@tsplus/stdlib/data/Duration'
|
|
8
9
|
import * as Maybe from '@tsplus/stdlib/data/Maybe'
|
|
9
10
|
|
|
@@ -15,7 +16,7 @@ export function hold<R, E, A>(fx: Fx<R, E, A>): Fx<R, E, A> {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export class Hold<R, E, A> extends Multicast<R, E, A> {
|
|
18
|
-
|
|
19
|
+
readonly value: AtomicReference<Maybe.Maybe<A>> = new AtomicReference(Maybe.none)
|
|
19
20
|
protected _pendingEmitters: Array<readonly [Emitter<unknown, E, A>, A[]]> = []
|
|
20
21
|
protected _scheduledFiber: Fiber.RealFiber<any, any> | undefined
|
|
21
22
|
|
|
@@ -23,8 +24,6 @@ export class Hold<R, E, A> extends Multicast<R, E, A> {
|
|
|
23
24
|
super(fx)
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
readonly get = Effect.sync(() => this._value)
|
|
27
|
-
|
|
28
27
|
run<R2>(emitter: Emitter<R2, E, A>): Effect.Effect<R | R2 | Scope, never, unknown> {
|
|
29
28
|
if (this.shouldScheduleFlush()) {
|
|
30
29
|
return pipe(
|
|
@@ -66,14 +65,14 @@ export class Hold<R, E, A> extends Multicast<R, E, A> {
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
protected shouldScheduleFlush() {
|
|
69
|
-
return Maybe.isSome(this.
|
|
68
|
+
return Maybe.isSome(this.value.get) && this.observers.length > 0
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
protected scheduleFlush<R>(observer: Emitter<R, E, A>) {
|
|
73
72
|
this._pendingEmitters.push([
|
|
74
73
|
observer,
|
|
75
74
|
pipe(
|
|
76
|
-
this.
|
|
75
|
+
this.value.get,
|
|
77
76
|
Maybe.fold(
|
|
78
77
|
() => [],
|
|
79
78
|
(a) => [a],
|
|
@@ -121,7 +120,7 @@ export class Hold<R, E, A> extends Multicast<R, E, A> {
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
protected addValue(value: A) {
|
|
124
|
-
this.
|
|
123
|
+
this.value.set(Maybe.some(value))
|
|
125
124
|
|
|
126
125
|
this._pendingEmitters.forEach(([, values]) => {
|
|
127
126
|
values.push(value)
|
package/src/withEmitter.test.ts
CHANGED
|
@@ -10,10 +10,10 @@ describe(import.meta.url, () => {
|
|
|
10
10
|
it('should allow converting sync callbacks into an Fx', async () => {
|
|
11
11
|
const test = pipe(
|
|
12
12
|
Fx.withEmitter((emitter) => {
|
|
13
|
-
emitter.
|
|
14
|
-
emitter.
|
|
15
|
-
emitter.
|
|
16
|
-
emitter.
|
|
13
|
+
emitter.unsafeEmit(1)
|
|
14
|
+
emitter.unsafeEmit(2)
|
|
15
|
+
emitter.unsafeEmit(3)
|
|
16
|
+
emitter.unsafeEnd()
|
|
17
17
|
|
|
18
18
|
return Effect.unit
|
|
19
19
|
}),
|
package/src/withEmitter.ts
CHANGED
|
@@ -5,10 +5,10 @@ import { Scope } from '@effect/core/io/Scope'
|
|
|
5
5
|
import { flow, pipe } from '@fp-ts/data/Function'
|
|
6
6
|
|
|
7
7
|
import { Emitter, Fx } from './Fx.js'
|
|
8
|
-
import type {
|
|
8
|
+
import type { UnsafeEmitter } from './Subject.js'
|
|
9
9
|
|
|
10
10
|
export function withEmitter<R, E, A>(
|
|
11
|
-
f: (emitter:
|
|
11
|
+
f: (emitter: UnsafeEmitter<E, A>) => Effect.Canceler<R>,
|
|
12
12
|
): Fx<R, E, A> {
|
|
13
13
|
return Fx<R, E, A>(<R2>(sink: Emitter<R2, E, A>) => {
|
|
14
14
|
return Effect.withFiberRuntime<R | R2 | Scope, never, unknown>((fiber, status) => {
|
|
@@ -28,12 +28,12 @@ export function withEmitter<R, E, A>(
|
|
|
28
28
|
Effect.zipRight(
|
|
29
29
|
Effect.asyncEffect<R | R2, never, unknown, R | R2, never, any>((cb) => {
|
|
30
30
|
canceler = f({
|
|
31
|
-
|
|
31
|
+
unsafeEmit: (a) =>
|
|
32
32
|
unsafeRun(sink.emit(a), (exit) =>
|
|
33
33
|
Exit.isFailure(exit) ? cb(Effect.failCause(exit.cause)) : undefined,
|
|
34
34
|
),
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
unsafeFailCause: (e) => unsafeRun(sink.failCause(e), flow(Effect.done, cb)),
|
|
36
|
+
unsafeEnd: () => unsafeRun(sink.end, flow(Effect.done, cb)),
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
return canceler
|