@@ -1,4 +1,4 @@
|
|||||||
import { type Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Option, Pipeable, Predicate, type Scope, Semaphore, Stream, SubscriptionRef } from "effect"
|
import { type Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Option, Pipeable, Predicate, PubSub, Ref, type Scope, Semaphore, Stream, SubscriptionRef } from "effect"
|
||||||
import { AsyncResult } from "effect/unstable/reactivity"
|
import { AsyncResult } from "effect/unstable/reactivity"
|
||||||
import * as Lens from "./Lens.js"
|
import * as Lens from "./Lens.js"
|
||||||
import * as QueryClient from "./QueryClient.js"
|
import * as QueryClient from "./QueryClient.js"
|
||||||
@@ -231,10 +231,12 @@ extends Pipeable.Class implements Query<K, A, E, R> {
|
|||||||
Scope.Scope | R
|
Scope.Scope | R
|
||||||
> {
|
> {
|
||||||
return Effect.gen({ self: this }, function*() {
|
return Effect.gen({ self: this }, function*() {
|
||||||
const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous))
|
const state = yield* makeQueryStateLens(previous)
|
||||||
|
|
||||||
const fiber = yield* Effect.forkScoped(Effect.andThen(
|
const fiber = yield* Effect.forkScoped(Effect.andThen(
|
||||||
Lens.update(state, previous => AsyncResult.match(previous.result, {
|
Lens.update<QueryState<K, A, E>, never, never, never, never>(
|
||||||
|
state,
|
||||||
|
previous => AsyncResult.match(previous.result, {
|
||||||
onInitial: () => ({
|
onInitial: () => ({
|
||||||
key: previous.key,
|
key: previous.key,
|
||||||
result: AsyncResult.initial(true),
|
result: AsyncResult.initial(true),
|
||||||
@@ -252,19 +254,17 @@ extends Pipeable.Class implements Query<K, A, E, R> {
|
|||||||
previousSuccess: result.previousSuccess,
|
previousSuccess: result.previousSuccess,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
})),
|
}
|
||||||
|
)),
|
||||||
|
|
||||||
Effect.onExit(this.f(previous.key), exit => Effect.all([
|
Effect.onExit(this.f(previous.key), exit => Effect.gen({ self: this }, function*() {
|
||||||
Effect.fiberId,
|
const fiberId = yield* Effect.fiberId
|
||||||
Lens.get(this.fiber),
|
const fiber = yield* Lens.get(this.fiber)
|
||||||
]).pipe(
|
|
||||||
Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, {
|
if (Option.isSome(fiber) && fiberId === fiber.value.id)
|
||||||
onSome: v => fiberId === v.id
|
yield* Lens.set(this.fiber, Option.none())
|
||||||
? Lens.set(this.fiber, Option.none())
|
|
||||||
: Effect.void,
|
const finalState = (yield* Lens.updateAndGet<QueryState<K, A, E>, never, never, never, never>(
|
||||||
onNone: () => Effect.void,
|
|
||||||
})),
|
|
||||||
Effect.andThen(Lens.updateAndGet(
|
|
||||||
state,
|
state,
|
||||||
previous => Exit.match(exit, {
|
previous => Exit.match(exit, {
|
||||||
onSuccess: v => ({
|
onSuccess: v => ({
|
||||||
@@ -290,9 +290,11 @@ extends Pipeable.Class implements Query<K, A, E, R> {
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
)),
|
)) as FinalQueryState<K, A, E>
|
||||||
Effect.flatMap(finalState => Lens.set(this.latestFinalState, Option.some(finalState as FinalQueryState<K, A, E>))),
|
|
||||||
)),
|
yield* Lens.set(this.latestFinalState, Option.some(finalState))
|
||||||
|
yield* PubSub.shutdown(state.pubsub)
|
||||||
|
}))
|
||||||
))
|
))
|
||||||
|
|
||||||
yield* Lens.set(this.fiber, Option.some(fiber))
|
yield* Lens.set(this.fiber, Option.some(fiber))
|
||||||
@@ -416,3 +418,44 @@ export const service = <K, A, E = never, R = never>(
|
|||||||
make(options),
|
make(options),
|
||||||
query => Effect.forkScoped(query.run),
|
query => Effect.forkScoped(query.run),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
export class QueryStateLens<in out K, in out A, in out E = never>
|
||||||
|
extends Lens.LensImpl<QueryState<K, A, E>, never, never, never, never> {
|
||||||
|
constructor(
|
||||||
|
readonly ref: Ref.Ref<QueryState<K, A, E>>,
|
||||||
|
readonly pubsub: PubSub.PubSub<QueryState<K, A, E>>,
|
||||||
|
readonly semaphore: Semaphore.Semaphore,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
get resolve(): Effect.Effect<Lens.LensImpl.Resolved<QueryState<K, A, E>>, never, never> {
|
||||||
|
return Effect.map(
|
||||||
|
Ref.get(this.ref),
|
||||||
|
value => ({
|
||||||
|
value,
|
||||||
|
commit: next => Effect.flatMap(
|
||||||
|
next,
|
||||||
|
value => Effect.andThen(
|
||||||
|
Ref.set(this.ref, value),
|
||||||
|
PubSub.publish(this.pubsub, value),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
get changes() { return Stream.fromPubSub(this.pubsub) }
|
||||||
|
get lock() { return Effect.succeed(this.semaphore.withPermit) }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeQueryStateLens = <K, A, E = never>(
|
||||||
|
initial: QueryState<K, A, E>,
|
||||||
|
) => Effect.all([
|
||||||
|
Ref.make(initial),
|
||||||
|
PubSub.unbounded<QueryState<K, A, E>>({ replay: 1 }),
|
||||||
|
Semaphore.make(1),
|
||||||
|
]).pipe(
|
||||||
|
Effect.tap(([, pubsub]) => PubSub.publish(pubsub, initial)),
|
||||||
|
Effect.map(([ref, pubsub, semaphore]) => new QueryStateLens(ref, pubsub, semaphore)),
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user