@@ -3,6 +3,8 @@ import type * as React from "react"
|
|||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
|
|
||||||
|
|
||||||
|
export * from "effect/PubSub"
|
||||||
|
|
||||||
export const useFromReactiveValues = Effect.fnUntraced(function* <const A extends React.DependencyList>(
|
export const useFromReactiveValues = Effect.fnUntraced(function* <const A extends React.DependencyList>(
|
||||||
values: A
|
values: A
|
||||||
): Effect.fn.Return<PubSub.PubSub<A>, never, Scope.Scope> {
|
): Effect.fn.Return<PubSub.PubSub<A>, never, Scope.Scope> {
|
||||||
@@ -13,5 +15,3 @@ export const useFromReactiveValues = Effect.fnUntraced(function* <const A extend
|
|||||||
), values)
|
), values)
|
||||||
return pubsub
|
return pubsub
|
||||||
})
|
})
|
||||||
|
|
||||||
export * from "effect/PubSub"
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
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, Layer, 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 View from "./View.js"
|
import type * as View from "./View.js"
|
||||||
@@ -126,6 +126,8 @@ export const service = (
|
|||||||
client => Effect.forkScoped(client.run),
|
client => Effect.forkScoped(client.run),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const layer = (options?: service.Options) => Layer.effect(QueryClient, service(options))
|
||||||
|
|
||||||
|
|
||||||
export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheKey")
|
export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheKey")
|
||||||
export type QueryClientCacheKeyTypeId = typeof QueryClientCacheKeyTypeId
|
export type QueryClientCacheKeyTypeId = typeof QueryClientCacheKeyTypeId
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
import { type Context, Effect, Layer, ManagedRuntime, Predicate } from "effect"
|
import { type Context, Effect, Layer, ManagedRuntime, Predicate } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
import * as ErrorObserver from "./ErrorObserver.js"
|
|
||||||
import * as QueryClient from "./QueryClient.js"
|
|
||||||
|
|
||||||
|
|
||||||
export const ReactRuntimeTypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime")
|
export const ReactRuntimeTypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime")
|
||||||
@@ -16,18 +14,9 @@ export interface ReactRuntime<R, ER> {
|
|||||||
readonly context: React.Context<Context.Context<R>>
|
readonly context: React.Context<Context.Context<R>>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReactRuntimeProto = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const)
|
const ReactRuntimePrototype = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const)
|
||||||
|
|
||||||
export const preludeLayer: Layer.Layer<
|
|
||||||
| Component.ScopeMap
|
|
||||||
| ErrorObserver.ErrorObserver
|
|
||||||
| QueryClient.QueryClient
|
|
||||||
> = Layer.mergeAll(
|
|
||||||
Component.ScopeMap.layer,
|
|
||||||
ErrorObserver.layer,
|
|
||||||
QueryClient.QueryClient.Default,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
export const preludeLayer: Layer.Layer<Component.ScopeMap> = Layer.mergeAll(Component.ScopeMap.layer)
|
||||||
|
|
||||||
export const isReactRuntime = (u: unknown): u is ReactRuntime<unknown, unknown> => Predicate.hasProperty(u, ReactRuntimeTypeId)
|
export const isReactRuntime = (u: unknown): u is ReactRuntime<unknown, unknown> => Predicate.hasProperty(u, ReactRuntimeTypeId)
|
||||||
|
|
||||||
@@ -43,7 +32,7 @@ export const make = <R, ER>(
|
|||||||
// biome-ignore lint/style/noNonNullAssertion: context initialization
|
// biome-ignore lint/style/noNonNullAssertion: context initialization
|
||||||
context: React.createContext<Context.Context<R>>(null!),
|
context: React.createContext<Context.Context<R>>(null!),
|
||||||
}),
|
}),
|
||||||
ReactRuntimeProto,
|
ReactRuntimePrototype,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Pipeable, Predicate, pipe, type Scope, SubscriptionRef } from "effect"
|
|
||||||
import { Lens } from "effect-lens"
|
|
||||||
import type { View } from "effect-lens/View"
|
|
||||||
|
|
||||||
|
|
||||||
export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result")
|
|
||||||
export type ResultTypeId = typeof ResultTypeId
|
|
||||||
|
|
||||||
export type Result<A, E = never, P = never> = (
|
|
||||||
| Initial
|
|
||||||
| Running<P>
|
|
||||||
| Final<A, E, P>
|
|
||||||
)
|
|
||||||
|
|
||||||
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
|
|
||||||
export type Final<A, E = never, P = never> = (Success<A> | Failure<E>) & ({} | Flags<P>)
|
|
||||||
export type Flags<P = never> = WillFetch | WillRefresh | Refreshing<P>
|
|
||||||
|
|
||||||
export declare namespace Result {
|
|
||||||
export type Success<R extends Result<any, any, any>> = [R] extends [Result<infer A, infer _E, infer _P>] ? A : never
|
|
||||||
export type Failure<R extends Result<any, any, any>> = [R] extends [Result<infer _A, infer E, infer _P>] ? E : never
|
|
||||||
export type Progress<R extends Result<any, any, any>> = [R] extends [Result<infer _A, infer _E, infer P>] ? P : never
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare namespace Flags {
|
|
||||||
export type Keys = keyof WillFetch & WillRefresh & Refreshing<any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Initial extends ResultPrototype {
|
|
||||||
readonly _tag: "Initial"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Running<P = never> extends ResultPrototype {
|
|
||||||
readonly _tag: "Running"
|
|
||||||
readonly progress: P
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Success<A> extends ResultPrototype {
|
|
||||||
readonly _tag: "Success"
|
|
||||||
readonly value: A
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Failure<E = never> extends ResultPrototype {
|
|
||||||
readonly _tag: "Failure"
|
|
||||||
readonly cause: Cause.Cause<E>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WillFetch {
|
|
||||||
readonly _flag: "WillFetch"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WillRefresh {
|
|
||||||
readonly _flag: "WillRefresh"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Refreshing<P = never> {
|
|
||||||
readonly _flag: "Refreshing"
|
|
||||||
readonly progress: P
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface ResultPrototype extends Pipeable.Pipeable, Equal.Equal {
|
|
||||||
readonly [ResultTypeId]: ResultTypeId
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ResultPrototype: ResultPrototype = Object.freeze({
|
|
||||||
...Pipeable.Prototype,
|
|
||||||
[ResultTypeId]: ResultTypeId,
|
|
||||||
|
|
||||||
[Equal.symbol](this: Result<any, any, any>, that: Result<any, any, any>): boolean {
|
|
||||||
if (this._tag !== that._tag || (this as Flags)._flag !== (that as Flags)._flag)
|
|
||||||
return false
|
|
||||||
if (hasRefreshingFlag(this) && !Equal.equals(this.progress, (that as Refreshing<any>).progress))
|
|
||||||
return false
|
|
||||||
return Match.value(this).pipe(
|
|
||||||
Match.tag("Initial", () => true),
|
|
||||||
Match.tag("Running", self => Equal.equals(self.progress, (that as Running<any>).progress)),
|
|
||||||
Match.tag("Success", self => Equal.equals(self.value, (that as Success<any>).value)),
|
|
||||||
Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure<any>).cause)),
|
|
||||||
Match.exhaustive,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
[Hash.symbol](this: Result<any, any, any>): number {
|
|
||||||
return pipe(Hash.string(this._tag),
|
|
||||||
tagHash => Match.value(this).pipe(
|
|
||||||
Match.tag("Initial", () => tagHash),
|
|
||||||
Match.tag("Running", self => Hash.combine(Hash.hash(self.progress))(tagHash)),
|
|
||||||
Match.tag("Success", self => Hash.combine(Hash.hash(self.value))(tagHash)),
|
|
||||||
Match.tag("Failure", self => Hash.combine(Hash.hash(self.cause))(tagHash)),
|
|
||||||
Match.exhaustive,
|
|
||||||
),
|
|
||||||
Hash.combine(Hash.hash((this as Flags)._flag)),
|
|
||||||
hash => hasRefreshingFlag(this)
|
|
||||||
? Hash.combine(Hash.hash(this.progress))(hash)
|
|
||||||
: hash,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
} as const)
|
|
||||||
|
|
||||||
|
|
||||||
export const isResult = (u: unknown): u is Result<unknown, unknown, unknown> => Predicate.hasProperty(u, ResultTypeId)
|
|
||||||
export const isFinal = (u: unknown): u is Final<unknown, unknown, unknown> => isResult(u) && (isSuccess(u) || isFailure(u))
|
|
||||||
export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial"
|
|
||||||
export const isRunning = (u: unknown): u is Running<unknown> => isResult(u) && u._tag === "Running"
|
|
||||||
export const isSuccess = (u: unknown): u is Success<unknown> => isResult(u) && u._tag === "Success"
|
|
||||||
export const isFailure = (u: unknown): u is Failure<unknown> => isResult(u) && u._tag === "Failure"
|
|
||||||
export const hasFlag = (u: unknown): u is Flags => isResult(u) && Predicate.hasProperty(u, "_flag")
|
|
||||||
export const hasWillFetchFlag = (u: unknown): u is WillFetch => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillFetch"
|
|
||||||
export const hasWillRefreshFlag = (u: unknown): u is WillRefresh => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillRefresh"
|
|
||||||
export const hasRefreshingFlag = (u: unknown): u is Refreshing<unknown> => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "Refreshing"
|
|
||||||
|
|
||||||
export const initial: {
|
|
||||||
(): Initial
|
|
||||||
<A, E = never, P = never>(): Result<A, E, P>
|
|
||||||
} = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype)
|
|
||||||
export const running = <P = never>(progress?: P): Running<P> => Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype)
|
|
||||||
export const succeed = <A>(value: A): Success<A> => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype)
|
|
||||||
export const fail = <E>(cause: Cause.Cause<E> ): Failure<E> => Object.setPrototypeOf({ _tag: "Failure", cause }, ResultPrototype)
|
|
||||||
|
|
||||||
export const willFetch = <R extends Final<any, any, any>>(
|
|
||||||
result: R
|
|
||||||
): Omit<R, keyof Flags.Keys> & WillFetch => Object.setPrototypeOf(
|
|
||||||
Object.assign({}, result, { _flag: "WillFetch" }),
|
|
||||||
Object.getPrototypeOf(result),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const willRefresh = <R extends Final<any, any, any>>(
|
|
||||||
result: R
|
|
||||||
): Omit<R, keyof Flags.Keys> & WillRefresh => Object.setPrototypeOf(
|
|
||||||
Object.assign({}, result, { _flag: "WillRefresh" }),
|
|
||||||
Object.getPrototypeOf(result),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const refreshing = <R extends Final<any, any, any>, P = never>(
|
|
||||||
result: R,
|
|
||||||
progress?: P,
|
|
||||||
): Omit<R, keyof Flags.Keys> & Refreshing<P> => Object.setPrototypeOf(
|
|
||||||
Object.assign({}, result, { _flag: "Refreshing", progress }),
|
|
||||||
Object.getPrototypeOf(result),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const fromExit: {
|
|
||||||
<A, E>(exit: Exit.Success<A, E>): Success<A>
|
|
||||||
<A, E>(exit: Exit.Failure<A, E>): Failure<E>
|
|
||||||
<A, E>(exit: Exit.Exit<A, E>): Success<A> | Failure<E>
|
|
||||||
} = exit => (exit._tag === "Success" ? succeed(exit.value) : fail(exit.cause)) as any
|
|
||||||
|
|
||||||
export const toExit: {
|
|
||||||
<A>(self: Success<A>): Exit.Success<A, never>
|
|
||||||
<E>(self: Failure<E>): Exit.Failure<never, E>
|
|
||||||
<A, E, P>(self: Final<A, E, P>): Exit.Exit<A, E>
|
|
||||||
<A, E, P>(self: Result<A, E, P>): Exit.Exit<A, E | Cause.NoSuchElementError>
|
|
||||||
} = <A, E, P>(self: Result<A, E, P>): any => {
|
|
||||||
switch (self._tag) {
|
|
||||||
case "Success":
|
|
||||||
return Exit.succeed(self.value)
|
|
||||||
case "Failure":
|
|
||||||
return Exit.failCause(self.cause)
|
|
||||||
default:
|
|
||||||
return Exit.fail(new Cause.NoSuchElementError())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface Progress<P = never> {
|
|
||||||
readonly progress: Lens.Lens<P, PreviousResultNotRunningNorRefreshing, never, never, never>
|
|
||||||
}
|
|
||||||
export const Progress = <P = never>() => Context.Service<Progress<P>>("@effect-fc/Result/Progress")
|
|
||||||
|
|
||||||
export class PreviousResultNotRunningNorRefreshing extends Data.TaggedError("@effect-fc/Result/PreviousResultNotRunningNorRefreshing")<{
|
|
||||||
readonly previous: Result<unknown, unknown, unknown>
|
|
||||||
}> {}
|
|
||||||
|
|
||||||
export const makeProgressLayer = <A, E, P = never>(
|
|
||||||
state: Lens.Lens<Result<A, E, P>, never, never, never, never>
|
|
||||||
): Layer.Layer<Progress<P> | Progress<never>, never, never> => Layer.succeed(
|
|
||||||
Progress<P>() as Context.Service<Progress<P> | Progress<never>, Progress<P> | Progress<never>>,
|
|
||||||
{
|
|
||||||
progress: state.pipe(
|
|
||||||
Lens.mapEffect(
|
|
||||||
a => (isRunning(a) || hasRefreshingFlag(a))
|
|
||||||
? Effect.succeed(a)
|
|
||||||
: Effect.fail(new PreviousResultNotRunningNorRefreshing({ previous: a })),
|
|
||||||
(_, b) => Effect.succeed(b),
|
|
||||||
),
|
|
||||||
Lens.map(
|
|
||||||
a => a.progress,
|
|
||||||
(a, b) => isRunning(a)
|
|
||||||
? running(b)
|
|
||||||
: refreshing(a, b) as Final<A, E, P> & Refreshing<P>,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
export namespace unsafeForkEffect {
|
|
||||||
export type OutputContext<R, P> = Exclude<R, Progress<P> | Progress<never>>
|
|
||||||
|
|
||||||
export interface Options<A, E, P> {
|
|
||||||
readonly initial?: Initial | Final<A, E, P>
|
|
||||||
readonly initialProgress?: P
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const unsafeForkEffect = Effect.fnUntraced(function* <A, E, R, P = never>(
|
|
||||||
effect: Effect.Effect<A, E, R>,
|
|
||||||
options?: unsafeForkEffect.Options<NoInfer<A>, NoInfer<E>, P>,
|
|
||||||
): Effect.fn.Return<
|
|
||||||
readonly [result: View<Result<A, E, P>, never, never>, fiber: Fiber.Fiber<A, E>],
|
|
||||||
never,
|
|
||||||
Scope.Scope | unsafeForkEffect.OutputContext<R, P>
|
|
||||||
> {
|
|
||||||
const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make<Result<A, E, P>>(
|
|
||||||
options?.initial ?? initial<A, E, P>(),
|
|
||||||
))
|
|
||||||
|
|
||||||
const fiber = yield* Effect.gen(function*() {
|
|
||||||
yield* Lens.set(
|
|
||||||
state,
|
|
||||||
(isFinal(options?.initial) && hasWillRefreshFlag(options?.initial))
|
|
||||||
? refreshing(options.initial, options?.initialProgress) as Result<A, E, P>
|
|
||||||
: running(options?.initialProgress),
|
|
||||||
)
|
|
||||||
return yield* Effect.onExit(effect, exit => Lens.set(state, fromExit(exit)))
|
|
||||||
}).pipe(
|
|
||||||
Effect.forkScoped,
|
|
||||||
Effect.provide(makeProgressLayer(state)),
|
|
||||||
)
|
|
||||||
|
|
||||||
return [state, fiber] as const
|
|
||||||
})
|
|
||||||
|
|
||||||
export namespace forkEffect {
|
|
||||||
export type InputContext<R, P> = R extends Progress<infer X> ? [X] extends [P] ? R : never : R
|
|
||||||
export type OutputContext<R, P> = unsafeForkEffect.OutputContext<R, P>
|
|
||||||
export interface Options<A, E, P> extends unsafeForkEffect.Options<A, E, P> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const forkEffect: {
|
|
||||||
<A, E, R, P = never>(
|
|
||||||
effect: Effect.Effect<A, E, forkEffect.InputContext<R, NoInfer<P>>>,
|
|
||||||
options?: forkEffect.Options<NoInfer<A>, NoInfer<E>, P>,
|
|
||||||
): Effect.Effect<
|
|
||||||
readonly [result: View<Result<A, E, P>, never, never>, fiber: Fiber.Fiber<A, E>],
|
|
||||||
never,
|
|
||||||
Scope.Scope | forkEffect.OutputContext<R, P>
|
|
||||||
>
|
|
||||||
} = unsafeForkEffect
|
|
||||||
@@ -3,6 +3,8 @@ import * as React from "react"
|
|||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
|
|
||||||
|
|
||||||
|
export * from "effect/Stream"
|
||||||
|
|
||||||
export const use: {
|
export const use: {
|
||||||
<A, E, R>(
|
<A, E, R>(
|
||||||
stream: Stream.Stream<A, E, R>
|
stream: Stream.Stream<A, E, R>
|
||||||
@@ -29,5 +31,3 @@ export const use: {
|
|||||||
|
|
||||||
return reactStateValue as Option.Some<A>
|
return reactStateValue as Option.Some<A>
|
||||||
})
|
})
|
||||||
|
|
||||||
export * from "effect/Stream"
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export * as PubSub from "./PubSub.js"
|
|||||||
export * as Query from "./Query.js"
|
export * as Query from "./Query.js"
|
||||||
export * as QueryClient from "./QueryClient.js"
|
export * as QueryClient from "./QueryClient.js"
|
||||||
export * as ReactRuntime from "./ReactRuntime.js"
|
export * as ReactRuntime from "./ReactRuntime.js"
|
||||||
export * as Result from "./Result.js"
|
|
||||||
export * as SetStateAction from "./SetStateAction.js"
|
export * as SetStateAction from "./SetStateAction.js"
|
||||||
export * as Stream from "./Stream.js"
|
export * as Stream from "./Stream.js"
|
||||||
export * as View from "./View.js"
|
export * as View from "./View.js"
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export type ExcludeKeys<T, K extends PropertyKey> = K extends keyof T ? (
|
|
||||||
{ [P in K]?: never } & Omit<T, K>
|
|
||||||
) : T
|
|
||||||
Reference in New Issue
Block a user