Add Effect v4 support, Fast Refresh tooling, and revamped docs #56

Merged
Thilawyn merged 133 commits from next into master 2026-07-26 02:32:59 +02:00
3 changed files with 21 additions and 31 deletions
Showing only changes of commit c59e2ee035 - Show all commits
+6 -10
View File
@@ -7,7 +7,7 @@ import type * as Subscribable from "./Subscribable.js"
export const MutationTypeId: unique symbol = Symbol.for("@effect-fc/Mutation/Mutation") export const MutationTypeId: unique symbol = Symbol.for("@effect-fc/Mutation/Mutation")
export type MutationTypeId = typeof MutationTypeId export type MutationTypeId = typeof MutationTypeId
export interface Mutation<in out K extends Mutation.AnyKey, in out A, in out E = never, in out R = never> export interface Mutation<in out K, in out A, in out E = never, in out R = never>
extends Pipeable.Pipeable { extends Pipeable.Pipeable {
readonly [MutationTypeId]: MutationTypeId readonly [MutationTypeId]: MutationTypeId
@@ -23,11 +23,10 @@ extends Pipeable.Pipeable {
mutateSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>> mutateSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>>
} }
export declare namespace Mutation { export const isMutation = (u: unknown): u is Mutation<unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, MutationTypeId)
export type AnyKey = readonly any[]
}
export class MutationImpl<in out K extends Mutation.AnyKey, in out A, in out E = never, in out R = never>
export class MutationImpl<in out K, in out A, in out E = never, in out R = never>
extends Pipeable.Class implements Mutation<K, A, E, R> { extends Pipeable.Class implements Mutation<K, A, E, R> {
readonly [MutationTypeId]: MutationTypeId = MutationTypeId readonly [MutationTypeId]: MutationTypeId = MutationTypeId
@@ -129,16 +128,13 @@ extends Pipeable.Class implements Mutation<K, A, E, R> {
} }
export const isMutation = (u: unknown): u is Mutation<readonly unknown[], unknown, unknown, unknown> => Predicate.hasProperty(u, MutationTypeId)
export declare namespace make { export declare namespace make {
export interface Options<K extends Mutation.AnyKey = never, A = void, E = never, R = never> { export interface Options<K = never, A = void, E = never, R = never> {
readonly f: (key: K) => Effect.Effect<A, E, R> readonly f: (key: K) => Effect.Effect<A, E, R>
} }
} }
export const make = Effect.fnUntraced(function* <const K extends Mutation.AnyKey = never, A = void, E = never, R = never>( export const make = Effect.fnUntraced(function* <K = never, A = void, E = never, R = never>(
options: make.Options<K, A, E, R> options: make.Options<K, A, E, R>
): Effect.fn.Return< ): Effect.fn.Return<
Mutation<K, A, E, R>, Mutation<K, A, E, R>,
+9 -14
View File
@@ -8,7 +8,7 @@ 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 extends Query.AnyKey, 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 KE = never, in out KR = never, in out E = never, in out R = never>
extends Pipeable.Pipeable { extends Pipeable.Pipeable {
readonly [QueryTypeId]: QueryTypeId readonly [QueryTypeId]: QueryTypeId
@@ -34,11 +34,10 @@ extends Pipeable.Pipeable {
invalidateCacheEntry(key: K): Effect.Effect<void> invalidateCacheEntry(key: K): Effect.Effect<void>
} }
export declare namespace Query { export const isQuery = (u: unknown): u is Query<unknown, unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, QueryTypeId)
export type AnyKey = readonly any[]
}
export class QueryImpl<in out K extends Query.AnyKey, 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 KE = never, in out KR = never, 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, KE, KR, E, R> {
readonly [QueryTypeId]: QueryTypeId = QueryTypeId readonly [QueryTypeId]: QueryTypeId = QueryTypeId
@@ -239,7 +238,7 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
} }
makeCacheKey(key: K): QueryClient.QueryClientCacheKey { makeCacheKey(key: K): QueryClient.QueryClientCacheKey {
return new QueryClient.QueryClientCacheKey(key, this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>) return new QueryClient.QueryClientCacheKey(key, this.f as (key: unknown) => Effect.Effect<unknown, unknown, unknown>)
} }
getCacheEntry( getCacheEntry(
@@ -269,7 +268,7 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
get invalidateCache(): Effect.Effect<void> { get invalidateCache(): Effect.Effect<void> {
return QueryClient.QueryClient.pipe( return QueryClient.QueryClient.pipe(
Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>)), Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: unknown) => Effect.Effect<unknown, unknown, unknown>)),
Effect.provide(this.context), Effect.provide(this.context),
) )
} }
@@ -285,12 +284,8 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
} }
} }
export const isQuery = (u: unknown): u is Query<readonly unknown[], unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, QueryTypeId)
export declare namespace make { export declare namespace make {
export interface Options<K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never> { export interface Options<K, A, KE = never, KR = never, E = never, R = never> {
readonly key: Stream.Stream<K, KE, KR> readonly key: Stream.Stream<K, KE, KR>
readonly f: (key: NoInfer<K>) => Effect.Effect<A, E, R> readonly f: (key: NoInfer<K>) => Effect.Effect<A, E, R>
readonly staleTime?: Duration.Input readonly staleTime?: Duration.Input
@@ -298,7 +293,7 @@ export declare namespace make {
} }
} }
export const make = Effect.fnUntraced(function* <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never>( export const make = Effect.fnUntraced(function* <K, A, KE = never, KR = never, E = never, R = never>(
options: make.Options<K, A, KE, KR, E, R> options: make.Options<K, A, KE, KR, E, R>
): Effect.fn.Return< ): Effect.fn.Return<
Query<K, A, KE, KR, E, R>, Query<K, A, KE, KR, E, R>,
@@ -324,7 +319,7 @@ export const make = Effect.fnUntraced(function* <K extends Query.AnyKey, A, KE =
) )
}) })
export const service = <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never>( export const service = <K, A, KE = never, KR = never, E = never, R = never>(
options: make.Options<K, A, KE, KR, E, R> options: make.Options<K, A, KE, KR, E, R>
): Effect.Effect< ): Effect.Effect<
Query<K, A, KE, KR, E, R>, Query<K, A, KE, KR, E, R>,
+6 -7
View File
@@ -1,7 +1,6 @@
import { type Cause, Context, DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, type Option, Pipeable, Predicate, Schedule, type Scope, Semaphore, SubscriptionRef } from "effect" import { type Cause, Context, DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, type Option, Pipeable, Predicate, Schedule, type Scope, Semaphore, SubscriptionRef } from "effect"
import type { AsyncResult } from "effect/unstable/reactivity" import type { AsyncResult } from "effect/unstable/reactivity"
import * as Lens from "./Lens.js" import * as Lens from "./Lens.js"
import type * as Query from "./Query.js"
import type * as Subscribable from "./Subscribable.js" import type * as Subscribable from "./Subscribable.js"
@@ -23,7 +22,7 @@ export interface QueryClientService extends Pipeable.Pipeable {
result: AsyncResult.Success<unknown, unknown>, result: AsyncResult.Success<unknown, unknown>,
staleTime: Duration.Duration, staleTime: Duration.Duration,
): Effect.Effect<QueryClientCacheEntry> ): Effect.Effect<QueryClientCacheEntry>
invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>): Effect.Effect<void> invalidateCacheEntries(f: (key: unknown) => Effect.Effect<unknown, unknown, unknown>): Effect.Effect<void>
invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect<void> invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect<void>
} }
@@ -86,7 +85,7 @@ implements QueryClientService {
) )
} }
invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>): Effect.Effect<void> { invalidateCacheEntries(f: (key: unknown) => Effect.Effect<unknown, unknown, unknown>): Effect.Effect<void> {
return Lens.update(this.cache, HashMap.filter((_, key) => !Equivalence.strictEqual()(key.f, f))) return Lens.update(this.cache, HashMap.filter((_, key) => !Equivalence.strictEqual()(key.f, f)))
} }
invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect<void> { invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect<void> {
@@ -137,17 +136,17 @@ implements Pipeable.Pipeable, Equal.Equal {
readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId
constructor( constructor(
readonly key: Query.Query.AnyKey, readonly key: unknown,
readonly f: (key: Query.Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>, readonly f: (key: unknown) => Effect.Effect<unknown, unknown, unknown>,
) { ) {
super() super()
} }
[Equal.symbol](that: Equal.Equal) { [Equal.symbol](that: Equal.Equal) {
return isQueryClientCacheKey(that) && Equivalence.Array(Equal.asEquivalence())(this.key, that.key) && Equivalence.strictEqual()(this.f, that.f) return isQueryClientCacheKey(that) && Equal.equals(this.key, that.key) && Equivalence.strictEqual()(this.f, that.f)
} }
[Hash.symbol]() { [Hash.symbol]() {
return Hash.combine(Hash.hash(this.f))(Hash.array(this.key)) return Hash.combine(Hash.hash(this.f))(Hash.hash(this.key))
} }
} }