QueryClient work
All checks were successful
Lint / lint (push) Successful in 13s

This commit is contained in:
Julien Valverdé
2025-12-11 06:14:24 +01:00
parent 94ba0b0c5e
commit cb5e3a84ae
2 changed files with 71 additions and 17 deletions

View File

@@ -121,7 +121,7 @@ extends Pipeable.Class() implements Form<A, I, R, MA, ME, MR, MP> {
export const isForm = (u: unknown): u is Form<unknown, unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, FormTypeId) export const isForm = (u: unknown): u is Form<unknown, unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, FormTypeId)
export namespace make { export declare namespace make {
export interface Options<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never> export interface Options<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never>
extends Mutation.make.Options< extends Mutation.make.Options<
readonly [value: NoInfer<A>, form: Form<NoInfer<A>, NoInfer<I>, NoInfer<R>, unknown, unknown, unknown>], readonly [value: NoInfer<A>, form: Form<NoInfer<A>, NoInfer<I>, NoInfer<R>, unknown, unknown, unknown>],
@@ -203,7 +203,7 @@ export const run = <A, I, R, MA, ME, MR, MP>(
)) ))
} }
export namespace service { export declare namespace service {
export interface Options<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never> export interface Options<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never>
extends make.Options<A, I, R, MA, ME, MR, MP> {} extends make.Options<A, I, R, MA, ME, MR, MP> {}
} }

View File

@@ -1,43 +1,97 @@
import { Equal, Equivalence, Hash, type HashMap, Pipeable, Predicate, type Ref } from "effect" import { type DateTime, type Duration, Effect, Equal, Equivalence, Hash, HashMap, Pipeable, Predicate, Ref, type Scope } from "effect"
import type * as Query from "./Query.js" import type * as Query from "./Query.js"
import type * as Result from "./Result.js" import type * as Result from "./Result.js"
export const QueryClientTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClient") export const QueryClientServiceTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientServiceTypeId")
export type QueryClientTypeId = typeof QueryClientTypeId export type QueryClientServiceTypeId = typeof QueryClientServiceTypeId
export interface QueryClient extends Pipeable.Pipeable {
readonly [QueryClientTypeId]: QueryClientTypeId
export interface QueryClientService extends Pipeable.Pipeable {
readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId
readonly cache: Ref.Ref<HashMap.HashMap<Query.Query.AnyKey, Result.Final<unknown, unknown, unknown>>> readonly cache: Ref.Ref<HashMap.HashMap<Query.Query.AnyKey, Result.Final<unknown, unknown, unknown>>>
readonly defaultTtl: Duration.DurationInput
} }
export class QueryClientImpl extends Pipeable.Class() implements QueryClient { export class QueryClient extends Effect.Service<QueryClient>()("@effect-fc/QueryClient/QueryClient", {
readonly [QueryClientTypeId]: QueryClientTypeId = QueryClientTypeId scoped: Effect.suspend(() => service())
}) {}
export class QueryClientServiceImpl extends Pipeable.Class() implements QueryClientService {
readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId = QueryClientServiceTypeId
constructor( constructor(
readonly cache: Ref.Ref<HashMap.HashMap<Query.Query.AnyKey, Result.Final<unknown, unknown, unknown>>> readonly cache: Ref.Ref<HashMap.HashMap<Query.Query.AnyKey, Result.Final<unknown, unknown, unknown>>>,
readonly defaultTtl: Duration.DurationInput,
) { ) {
super() super()
} }
} }
export const isQueryClient = (u: unknown): u is QueryClient => Predicate.hasProperty(u, QueryClientTypeId) export const isQueryClientService = (u: unknown): u is QueryClientService => Predicate.hasProperty(u, QueryClientServiceTypeId)
export declare namespace make {
export interface Options {
readonly defaultTtl?: Duration.DurationInput
}
}
export const make = Effect.fnUntraced(function* (options: make.Options = {}): Effect.fn.Return<QueryClientService> {
return new QueryClientServiceImpl(
yield* Ref.make(HashMap.empty<Query.Query.AnyKey, Result.Final<unknown, unknown, unknown>>()),
options.defaultTtl ?? "5 minutes",
)
})
export const run = (_self: QueryClientService): Effect.Effect<void> => Effect.void
export declare namespace service {
export interface Options extends make.Options {}
}
export const service = (options?: service.Options): Effect.Effect<QueryClientService, never, Scope.Scope> => Effect.tap(
make(options),
client => Effect.forkScoped(run(client)),
)
const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheKey") export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheKey")
type QueryClientCacheKeyTypeId = typeof QueryClientCacheKeyTypeId export type QueryClientCacheKeyTypeId = typeof QueryClientCacheKeyTypeId
class QueryClientCacheKey implements Equal.Equal { export class QueryClientCacheKey implements Equal.Equal {
readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId
constructor(readonly key: Query.Query.AnyKey) {} constructor(readonly key: Query.Query.AnyKey) {}
[Equal.symbol](that: Equal.Equal) { [Equal.symbol](that: Equal.Equal) {
return isQueryClientKey(that) && Equivalence.array(Equal.equivalence())(this.key, that.key) return isQueryClientCacheKey(that) && Equivalence.array(Equal.equivalence())(this.key, that.key)
} }
[Hash.symbol]() { [Hash.symbol]() {
return Hash.array(this.key) return Hash.array(this.key)
} }
} }
const isQueryClientKey = (u: unknown): u is QueryClientCacheKey => Predicate.hasProperty(u, QueryClientCacheKeyTypeId) export const isQueryClientCacheKey = (u: unknown): u is QueryClientCacheKey => Predicate.hasProperty(u, QueryClientCacheKeyTypeId)
export const QueryClientCacheEntryTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheEntry")
export type QueryClientCacheEntryTypeId = typeof QueryClientCacheEntryTypeId
export interface QueryClientCacheEntry extends Pipeable.Pipeable {
readonly [QueryClientCacheEntryTypeId]: QueryClientCacheEntryTypeId
readonly at: DateTime.DateTime
readonly ttl: Duration.DurationInput
readonly result: Result.Final<unknown, unknown, unknown>
}
export class QueryClientCacheEntryImpl extends Pipeable.Class() implements QueryClientCacheEntry {
readonly [QueryClientCacheEntryTypeId]: QueryClientCacheEntryTypeId = QueryClientCacheEntryTypeId
constructor(
readonly at: DateTime.DateTime,
readonly ttl: Duration.DurationInput,
readonly result: Result.Final<unknown, unknown, unknown>,
) {
super()
}
}
export const isQueryClientCacheEntry = (u: unknown): u is QueryClientCacheEntry => Predicate.hasProperty(u, QueryClientCacheEntryTypeId)