Add Effect v4 support, Fast Refresh tooling, and revamped docs #56
@@ -1,4 +1,4 @@
|
|||||||
import { type Cause, type Context, Duration, Effect, Equal, 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, 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"
|
||||||
@@ -8,51 +8,61 @@ import * as Subscribable from "./Subscribable.js"
|
|||||||
export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query")
|
export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query")
|
||||||
export type QueryTypeId = typeof QueryTypeId
|
export type QueryTypeId = typeof QueryTypeId
|
||||||
|
|
||||||
export interface Query<in out K, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never>
|
export interface Query<in out K, in out A, in out E = never, in out R = never>
|
||||||
extends Pipeable.Pipeable {
|
extends Pipeable.Pipeable {
|
||||||
readonly [QueryTypeId]: QueryTypeId
|
readonly [QueryTypeId]: QueryTypeId
|
||||||
|
|
||||||
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R>
|
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | R>
|
||||||
readonly key: Stream.Stream<K, KE, KR>
|
|
||||||
readonly f: (key: K) => Effect.Effect<A, E, R>
|
readonly f: (key: K) => Effect.Effect<A, E, R>
|
||||||
|
readonly keyEquivalence: Equivalence.Equivalence<K>
|
||||||
|
|
||||||
readonly staleTime: Duration.Duration
|
readonly staleTime: Duration.Duration
|
||||||
readonly refreshOnWindowFocus: boolean
|
readonly refreshOnWindowFocus: boolean
|
||||||
|
|
||||||
readonly latestKey: Subscribable.Subscribable<Option.Option<K>>
|
readonly latestKey: Subscribable.Subscribable<Option.Option<K>>
|
||||||
readonly fiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, E>>>
|
readonly fiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, E>>>
|
||||||
readonly result: Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>
|
readonly state: Subscribable.Subscribable<QueryState<K, A, E>>
|
||||||
readonly latestFinalResult: Subscribable.Subscribable<Option.Option<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>>
|
readonly latestFinalState: Subscribable.Subscribable<Option.Option<FinalQueryState<K, A, E>>>
|
||||||
|
|
||||||
readonly run: Effect.Effect<void>
|
readonly run: Effect.Effect<void>
|
||||||
fetch(key: K): Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, Cause.NoSuchElementError>
|
fetch(key: K): Effect.Effect<FinalQueryState<K, A, E>, Cause.NoSuchElementError>
|
||||||
fetchSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>, Cause.NoSuchElementError>
|
fetchSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<QueryState<K, A, E>>, Cause.NoSuchElementError>
|
||||||
readonly refresh: Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, Cause.NoSuchElementError>
|
readonly refresh: Effect.Effect<FinalQueryState<K, A, E>, Cause.NoSuchElementError>
|
||||||
readonly refreshSubscribable: Effect.Effect<Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>, Cause.NoSuchElementError>
|
readonly refreshSubscribable: Effect.Effect<Subscribable.Subscribable<QueryState<K, A, E>>, Cause.NoSuchElementError>
|
||||||
|
|
||||||
readonly invalidateCache: Effect.Effect<void>
|
readonly invalidateCache: Effect.Effect<void>
|
||||||
invalidateCacheEntry(key: K): Effect.Effect<void>
|
invalidateCacheEntry(key: K): Effect.Effect<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isQuery = (u: unknown): u is Query<unknown, unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, QueryTypeId)
|
export interface QueryState<in out K, in out A, in out E = never> {
|
||||||
|
readonly key: K
|
||||||
|
readonly result: AsyncResult.AsyncResult<A, E>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinalQueryState<in out K, in out A, in out E = never> {
|
||||||
|
readonly key: K
|
||||||
|
readonly result: AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isQuery = (u: unknown): u is Query<unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, QueryTypeId)
|
||||||
|
|
||||||
|
|
||||||
export class QueryImpl<in out K, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never>
|
export class QueryImpl<in out K, in out A, in out E = never, in out R = never>
|
||||||
extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
|
extends Pipeable.Class implements Query<K, A, E, R> {
|
||||||
readonly [QueryTypeId]: QueryTypeId = QueryTypeId
|
readonly [QueryTypeId]: QueryTypeId = QueryTypeId
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R>,
|
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | R>,
|
||||||
readonly key: Stream.Stream<K, KE, KR>,
|
|
||||||
readonly f: (key: K) => Effect.Effect<A, E, R>,
|
readonly f: (key: K) => Effect.Effect<A, E, R>,
|
||||||
|
readonly keyEquivalence: Equivalence.Equivalence<K>,
|
||||||
|
|
||||||
readonly staleTime: Duration.Duration,
|
readonly staleTime: Duration.Duration,
|
||||||
readonly refreshOnWindowFocus: boolean,
|
readonly refreshOnWindowFocus: boolean,
|
||||||
|
|
||||||
readonly latestKey: Lens.Lens<Option.Option<K>>,
|
readonly latestKey: Lens.Lens<Option.Option<K>>,
|
||||||
readonly fiber: Lens.Lens<Option.Option<Fiber.Fiber<A, E>>>,
|
readonly fiber: Lens.Lens<Option.Option<Fiber.Fiber<A, E>>>,
|
||||||
readonly result: Lens.Lens<AsyncResult.AsyncResult<A, E>>,
|
readonly state: Lens.Lens<QueryState<K, A, E>>,
|
||||||
readonly latestFinalResult: Lens.Lens<Option.Option<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>>,
|
readonly latestFinalState: Lens.Lens<Option.Option<FinalQueryState<K, A, E>>>,
|
||||||
|
|
||||||
readonly runSemaphore: Semaphore.Semaphore,
|
readonly runSemaphore: Semaphore.Semaphore,
|
||||||
) {
|
) {
|
||||||
@@ -60,10 +70,7 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get run(): Effect.Effect<void> {
|
get run(): Effect.Effect<void> {
|
||||||
return Effect.all([
|
return Effect.promise(() => import("@effect/platform-browser")).pipe(
|
||||||
Stream.runForEach(this.key, key => this.fetchSubscribable(key)),
|
|
||||||
|
|
||||||
Effect.promise(() => import("@effect/platform-browser")).pipe(
|
|
||||||
Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus
|
Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus
|
||||||
? Stream.runForEach(
|
? Stream.runForEach(
|
||||||
BrowserStream.fromEventListenerWindow("focus"),
|
BrowserStream.fromEventListenerWindow("focus"),
|
||||||
@@ -72,8 +79,6 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
|
|||||||
: Effect.void
|
: Effect.void
|
||||||
),
|
),
|
||||||
Effect.catchDefect(() => Effect.void),
|
Effect.catchDefect(() => Effect.void),
|
||||||
),
|
|
||||||
], { concurrency: "unbounded" }).pipe(
|
|
||||||
Effect.ignore,
|
Effect.ignore,
|
||||||
this.runSemaphore.withPermits(1),
|
this.runSemaphore.withPermits(1),
|
||||||
Effect.provide(this.context),
|
Effect.provide(this.context),
|
||||||
@@ -87,117 +92,156 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(key: K): Effect.Effect<
|
fetch(key: K): Effect.Effect<FinalQueryState<K, A, E>, Cause.NoSuchElementError> {
|
||||||
AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>,
|
|
||||||
Cause.NoSuchElementError
|
|
||||||
> {
|
|
||||||
return this.interrupt.pipe(
|
return this.interrupt.pipe(
|
||||||
Effect.andThen(Lens.set(this.latestKey, Option.some(key))),
|
Effect.andThen(Lens.set(this.latestKey, Option.some(key))),
|
||||||
Effect.andThen(this.startCached(key, AsyncResult.initial())),
|
Effect.andThen(this.startCached({
|
||||||
Effect.flatMap(state => this.watch(key, state)),
|
key,
|
||||||
|
result: AsyncResult.initial(false)
|
||||||
|
})),
|
||||||
|
Effect.flatMap(state => this.watch(state)),
|
||||||
Effect.provide(this.context),
|
Effect.provide(this.context),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSubscribable(key: K): Effect.Effect<
|
fetchSubscribable(key: K): Effect.Effect<
|
||||||
Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
|
Subscribable.Subscribable<QueryState<K, A, E>>,
|
||||||
Cause.NoSuchElementError
|
Cause.NoSuchElementError
|
||||||
> {
|
> {
|
||||||
return this.interrupt.pipe(
|
return this.interrupt.pipe(
|
||||||
Effect.andThen(Lens.set(this.latestKey, Option.some(key))),
|
Effect.andThen(Lens.set(this.latestKey, Option.some(key))),
|
||||||
Effect.andThen(this.startCached(key, AsyncResult.initial(false))),
|
Effect.andThen(this.startCached({
|
||||||
Effect.tap(state => Effect.forkScoped(this.watch(key, state))),
|
key,
|
||||||
|
result: AsyncResult.initial(false)
|
||||||
|
})),
|
||||||
|
Effect.tap(state => Effect.forkScoped(this.watch(state))),
|
||||||
Effect.provide(this.context),
|
Effect.provide(this.context),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get refresh(): Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, Cause.NoSuchElementError> {
|
get refresh(): Effect.Effect<FinalQueryState<K, A, E>, Cause.NoSuchElementError> {
|
||||||
return this.interrupt.pipe(
|
return this.interrupt.pipe(
|
||||||
Effect.andThen(Effect.Do),
|
Effect.andThen(Effect.Do),
|
||||||
Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)),
|
Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)),
|
||||||
Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)),
|
Effect.bind("latestFinalState", () => Lens.get(this.latestFinalState)),
|
||||||
Effect.bind("subscribable", ({ latestKey, latestFinalResult }) =>
|
Effect.bind("subscribable", ({ latestKey, latestFinalState }) =>
|
||||||
this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true)))
|
this.startCached(Option.getOrElse(latestFinalState, () => ({
|
||||||
|
key: latestKey,
|
||||||
|
result: AsyncResult.initial(true),
|
||||||
|
})))
|
||||||
),
|
),
|
||||||
Effect.flatMap(({ latestKey, subscribable }) => this.watch(latestKey, subscribable)),
|
Effect.flatMap(({ subscribable }) => this.watch(subscribable)),
|
||||||
Effect.provide(this.context),
|
Effect.provide(this.context),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get refreshSubscribable(): Effect.Effect<
|
get refreshSubscribable(): Effect.Effect<
|
||||||
Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
|
Subscribable.Subscribable<QueryState<K, A, E>>,
|
||||||
Cause.NoSuchElementError
|
Cause.NoSuchElementError
|
||||||
> {
|
> {
|
||||||
return this.interrupt.pipe(
|
return this.interrupt.pipe(
|
||||||
Effect.andThen(Effect.Do),
|
Effect.andThen(Effect.Do),
|
||||||
Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)),
|
Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)),
|
||||||
Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)),
|
Effect.bind("latestFinalState", () => Lens.get(this.latestFinalState)),
|
||||||
Effect.bind("subscribable", ({ latestKey, latestFinalResult }) =>
|
Effect.bind("subscribable", ({ latestKey, latestFinalState }) =>
|
||||||
this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true)))
|
this.startCached(Option.getOrElse(latestFinalState, () => ({
|
||||||
|
key: latestKey,
|
||||||
|
result: AsyncResult.initial(true),
|
||||||
|
})))
|
||||||
),
|
),
|
||||||
Effect.tap(({ latestKey, subscribable }) => Effect.forkScoped(this.watch(latestKey, subscribable))),
|
Effect.tap(({ subscribable }) => Effect.forkScoped(this.watch(subscribable))),
|
||||||
Effect.map(({ subscribable }) => subscribable),
|
Effect.map(({ subscribable }) => subscribable),
|
||||||
Effect.provide(this.context),
|
Effect.provide(this.context),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
startCached(
|
startCached(
|
||||||
key: K,
|
previous: QueryState<K, A, E>,
|
||||||
previous: AsyncResult.AsyncResult<A, E>,
|
|
||||||
): Effect.Effect<
|
): Effect.Effect<
|
||||||
Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
|
Subscribable.Subscribable<QueryState<K, A, E>>,
|
||||||
Cause.NoSuchElementError,
|
Cause.NoSuchElementError,
|
||||||
Scope.Scope | QueryClient.QueryClient | R
|
Scope.Scope | QueryClient.QueryClient | R
|
||||||
> {
|
> {
|
||||||
return Effect.flatMap(this.getCacheEntry(key), Option.match({
|
return Effect.flatMap(this.getCacheEntry(previous.key), Option.match({
|
||||||
onSome: entry => Effect.flatMap(
|
onSome: entry => Effect.flatMap(
|
||||||
QueryClient.isQueryClientCacheEntryStale(entry),
|
QueryClient.isQueryClientCacheEntryStale(entry),
|
||||||
isStale => isStale
|
isStale => isStale
|
||||||
? this.start(key, entry.result as AsyncResult.AsyncResult<A, E>)
|
? this.start({
|
||||||
|
key: previous.key,
|
||||||
|
result: entry.result as AsyncResult.AsyncResult<A, E>,
|
||||||
|
})
|
||||||
: Effect.succeed(Subscribable.make({
|
: Effect.succeed(Subscribable.make({
|
||||||
get: Effect.succeed(entry.result as AsyncResult.AsyncResult<A, E>),
|
get: Effect.succeed({
|
||||||
get changes() { return Stream.make(entry.result as AsyncResult.AsyncResult<A, E>) },
|
key: previous.key,
|
||||||
|
result: entry.result as AsyncResult.AsyncResult<A, E>,
|
||||||
|
}),
|
||||||
|
get changes() {
|
||||||
|
return Stream.make({
|
||||||
|
key: previous.key,
|
||||||
|
result: entry.result as AsyncResult.AsyncResult<A, E>,
|
||||||
|
})
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
onNone: () => this.start(key, previous),
|
onNone: () => this.start(previous),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
start(
|
start(
|
||||||
key: K,
|
previous: QueryState<K, A, E>,
|
||||||
previous: AsyncResult.AsyncResult<A, E>,
|
|
||||||
): Effect.Effect<
|
): Effect.Effect<
|
||||||
Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
|
Subscribable.Subscribable<QueryState<K, A, E>>,
|
||||||
never,
|
never,
|
||||||
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 subscribable = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous))
|
||||||
|
|
||||||
const fiber = yield* Effect.forkScoped(Effect.andThen(
|
const fiber = yield* Effect.forkScoped(Effect.andThen(
|
||||||
Lens.update(state, AsyncResult.match({
|
Lens.update(subscribable, previous => AsyncResult.match(previous.result, {
|
||||||
onInitial: () => AsyncResult.initial(true),
|
onInitial: () => ({
|
||||||
onSuccess: v => AsyncResult.success(v.value, {
|
key: previous.key,
|
||||||
|
result: AsyncResult.initial(true),
|
||||||
|
}),
|
||||||
|
onSuccess: result => ({
|
||||||
|
key: previous.key,
|
||||||
|
result: AsyncResult.success(result.value, {
|
||||||
waiting: true,
|
waiting: true,
|
||||||
}),
|
}),
|
||||||
onFailure: v => AsyncResult.failure(v.cause, {
|
}),
|
||||||
|
onFailure: result => ({
|
||||||
|
key: previous.key,
|
||||||
|
result: AsyncResult.failure(result.cause, {
|
||||||
waiting: true,
|
waiting: true,
|
||||||
previousSuccess: v.previousSuccess,
|
previousSuccess: result.previousSuccess,
|
||||||
})
|
}),
|
||||||
|
}),
|
||||||
})),
|
})),
|
||||||
|
|
||||||
Effect.onExit(this.f(key), exit => Lens.update(
|
Effect.onExit(this.f(previous.key), exit => Lens.update(
|
||||||
state,
|
subscribable,
|
||||||
previous => Exit.match(exit, {
|
previous => Exit.match(exit, {
|
||||||
onSuccess: v => AsyncResult.success(v),
|
onSuccess: v => ({
|
||||||
onFailure: c => AsyncResult.match(previous, {
|
key: previous.key,
|
||||||
onInitial: () => AsyncResult.failure(c),
|
result: AsyncResult.success(v),
|
||||||
onSuccess: v => AsyncResult.failure(c, {
|
}),
|
||||||
|
onFailure: c => AsyncResult.match(previous.result, {
|
||||||
|
onInitial: () => ({
|
||||||
|
key: previous.key,
|
||||||
|
result: AsyncResult.failure(c),
|
||||||
|
}),
|
||||||
|
onSuccess: v => ({
|
||||||
|
key: previous.key,
|
||||||
|
result: AsyncResult.failure(c, {
|
||||||
previousSuccess: Option.some(v),
|
previousSuccess: Option.some(v),
|
||||||
}),
|
}),
|
||||||
onFailure: v => AsyncResult.failure(c, {
|
}),
|
||||||
|
onFailure: v => ({
|
||||||
|
key: previous.key,
|
||||||
|
result: AsyncResult.failure(c, {
|
||||||
previousSuccess: v.previousSuccess,
|
previousSuccess: v.previousSuccess,
|
||||||
})
|
}),
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
).pipe(
|
).pipe(
|
||||||
@@ -206,7 +250,7 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
|
|||||||
Lens.get(this.fiber),
|
Lens.get(this.fiber),
|
||||||
])),
|
])),
|
||||||
Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, {
|
Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, {
|
||||||
onSome: v => Equal.equals(fiberId, v.id)
|
onSome: v => fiberId === v.id
|
||||||
? Lens.set(this.fiber, Option.none())
|
? Lens.set(this.fiber, Option.none())
|
||||||
: Effect.void,
|
: Effect.void,
|
||||||
onNone: () => Effect.void,
|
onNone: () => Effect.void,
|
||||||
@@ -215,23 +259,22 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
|
|||||||
))
|
))
|
||||||
|
|
||||||
yield* Lens.set(this.fiber, Option.some(fiber))
|
yield* Lens.set(this.fiber, Option.some(fiber))
|
||||||
return state
|
return subscribable
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
key: K,
|
subscribable: Subscribable.Subscribable<QueryState<K, A, E>>
|
||||||
state: Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>
|
): Effect.Effect<FinalQueryState<K, A, E>, never, QueryClient.QueryClient> {
|
||||||
): Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, never, QueryClient.QueryClient> {
|
return subscribable.get.pipe(
|
||||||
return state.get.pipe(
|
|
||||||
Effect.flatMap(initial => Stream.runFoldEffect(
|
Effect.flatMap(initial => Stream.runFoldEffect(
|
||||||
state.changes,
|
subscribable.changes,
|
||||||
() => initial,
|
() => initial,
|
||||||
(_, result) => Effect.as(Lens.set(this.result, result), result),
|
(_, state) => Effect.as(Lens.set(this.state, state), state),
|
||||||
) as Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>),
|
) as Effect.Effect<FinalQueryState<K, A, E>>),
|
||||||
Effect.tap(result => Lens.set(this.latestFinalResult, Option.some(result))),
|
Effect.tap(state => Lens.set(this.latestFinalState, Option.some(state))),
|
||||||
Effect.tap(result => AsyncResult.isSuccess(result)
|
Effect.tap(state => AsyncResult.isSuccess(state.result)
|
||||||
? this.setCacheEntry(key, result)
|
? this.setCacheEntry(state.key, state.result)
|
||||||
: Effect.void
|
: Effect.void
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -285,27 +328,27 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export declare namespace make {
|
export declare namespace make {
|
||||||
export interface Options<K, A, KE = never, KR = never, E = never, R = never> {
|
export interface Options<K, A, E = never, R = never> {
|
||||||
readonly key: Stream.Stream<K, KE, KR>
|
readonly f: (key: K) => Effect.Effect<A, E, R>
|
||||||
readonly f: (key: NoInfer<K>) => Effect.Effect<A, E, R>
|
readonly keyEquivalence?: Equivalence.Equivalence<K>,
|
||||||
readonly staleTime?: Duration.Input
|
readonly staleTime?: Duration.Input
|
||||||
readonly refreshOnWindowFocus?: boolean
|
readonly refreshOnWindowFocus?: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const make = Effect.fnUntraced(function* <K, A, KE = never, KR = never, E = never, R = never>(
|
export const make = Effect.fnUntraced(function* <K, A, E = never, R = never>(
|
||||||
options: make.Options<K, A, KE, KR, E, R>
|
options: make.Options<K, A, E, R>
|
||||||
): Effect.fn.Return<
|
): Effect.fn.Return<
|
||||||
Query<K, A, KE, KR, E, R>,
|
Query<K, A, E, R>,
|
||||||
Cause.NoSuchElementError,
|
Cause.NoSuchElementError,
|
||||||
Scope.Scope | QueryClient.QueryClient | KR | R
|
Scope.Scope | QueryClient.QueryClient | R
|
||||||
> {
|
> {
|
||||||
const client = yield* QueryClient.QueryClient
|
const client = yield* QueryClient.QueryClient
|
||||||
|
|
||||||
return new QueryImpl(
|
return new QueryImpl(
|
||||||
yield* Effect.context<Scope.Scope | QueryClient.QueryClient | KR | R>(),
|
yield* Effect.context<Scope.Scope | QueryClient.QueryClient | R>(),
|
||||||
options.key,
|
|
||||||
options.f,
|
options.f,
|
||||||
|
options.keyEquivalence ?? Equal.asEquivalence(),
|
||||||
|
|
||||||
options.staleTime ? yield* Effect.fromOption(Duration.fromInput(options.staleTime)) : client.defaultStaleTime,
|
options.staleTime ? yield* Effect.fromOption(Duration.fromInput(options.staleTime)) : client.defaultStaleTime,
|
||||||
options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus,
|
options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus,
|
||||||
@@ -319,12 +362,12 @@ export const make = Effect.fnUntraced(function* <K, A, KE = never, KR = never, E
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const service = <K, A, KE = never, KR = never, E = never, R = never>(
|
export const service = <K, A, E = never, R = never>(
|
||||||
options: make.Options<K, A, KE, KR, E, R>
|
options: make.Options<K, A, E, R>
|
||||||
): Effect.Effect<
|
): Effect.Effect<
|
||||||
Query<K, A, KE, KR, E, R>,
|
Query<K, A, E, R>,
|
||||||
Cause.NoSuchElementError,
|
Cause.NoSuchElementError,
|
||||||
Scope.Scope | QueryClient.QueryClient | KR | R
|
Scope.Scope | QueryClient.QueryClient | R
|
||||||
> => Effect.tap(
|
> => Effect.tap(
|
||||||
make(options),
|
make(options),
|
||||||
query => Effect.forkScoped(query.run),
|
query => Effect.forkScoped(query.run),
|
||||||
|
|||||||
Reference in New Issue
Block a user