0.2.2 #31

Merged
Thilawyn merged 184 commits from next into master 2026-01-16 17:05:31 +01:00
Showing only changes of commit 0f79f12632 - Show all commits

View File

@@ -11,13 +11,11 @@ export type Result<A, E = never, P = never> = (
| Final<A, E, P>
)
export type Final<A, E = never, P = never> = (
& (Success<A> | Failure<A, E>)
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
& ({} | WillFetch | WillRefresh | Refreshing<P>)
)
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
export type Final<A, E = never, P = never> = (Success<A> | Failure<A, E>) & ({} | Flags<P>)
export type Flags<P = never> = WillFetch | WillRefresh | Refreshing<P>
export namespace Result {
export declare namespace Result {
export interface Prototype extends Pipeable.Pipeable, Equal.Equal {
readonly [ResultTypeId]: ResultTypeId
}
@@ -27,6 +25,10 @@ export namespace Result {
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 Result.Prototype {
readonly _tag: "Initial"
}
@@ -47,16 +49,16 @@ export interface Failure<A, E = never> extends Result.Prototype {
readonly previousSuccess: Option.Option<Success<A>>
}
export interface WillFetch extends Result.Prototype {
readonly willFetch: true
export interface WillFetch {
readonly _flag: "WillFetch"
}
export interface WillRefresh extends Result.Prototype {
readonly willRefresh: true
export interface WillRefresh {
readonly _flag: "WillRefresh"
}
export interface Refreshing<P = never> extends Result.Prototype {
readonly refreshing: true
export interface Refreshing<P = never> {
readonly _flag: "Refreshing"
readonly progress: P
}
@@ -113,9 +115,9 @@ export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag ===
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, unknown> => isResult(u) && u._tag === "Failure"
export const isWillFetch = (u: unknown): u is WillFetch => isResult(u) && Predicate.hasProperty(u, "willFetch")
export const isWillRefresh = (u: unknown): u is WillRefresh => isResult(u) && Predicate.hasProperty(u, "willRefresh")
export const isRefreshing = (u: unknown): u is Refreshing<unknown> => isResult(u) && Predicate.hasProperty(u, "refreshing")
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
@@ -135,23 +137,23 @@ export const fail = <E, A = never>(
export const willFetch = <R extends Initial | Final<any, any, any>>(
result: R
): Omit<R, keyof WillFetch> & WillFetch => Object.setPrototypeOf(
Object.assign({}, result, { willFetch: true }),
): 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 WillRefresh> & WillRefresh => Object.setPrototypeOf(
Object.assign({}, result, { willRefresh: true }),
): 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 Refreshing<Result.Progress<R>>> & Refreshing<P> => Object.setPrototypeOf(
Object.assign({}, result, { refreshing: true, progress }),
): Omit<R, keyof Flags.Keys> & Refreshing<P> => Object.setPrototypeOf(
Object.assign({}, result, { _flag: "Refreshing", progress }),
Object.getPrototypeOf(result),
)
@@ -203,17 +205,17 @@ export const makeProgressLayer = <A, E, P = never>(): Layer.Layer<
const state = yield* State<A, E, P>()
return {
update: <E, R>(f: (previous: P) => Effect.Effect<P, E, R>) => Effect.Do.pipe(
update: <FE, FR>(f: (previous: P) => Effect.Effect<P, FE, FR>) => Effect.Do.pipe(
Effect.bind("previous", () => Effect.andThen(state.get, previous =>
isRunning(previous) || isRefreshing(previous)
(isRunning(previous) || hasRefreshingFlag(previous))
? Effect.succeed(previous)
: Effect.fail(new PreviousResultNotRunningNorRefreshing({ previous })),
)),
Effect.bind("progress", ({ previous }) => f(previous.progress)),
Effect.let("next", ({ previous, progress }) => Object.setPrototypeOf(
Object.assign({}, previous, { progress }),
Object.getPrototypeOf(previous),
)),
Effect.let("next", ({ previous, progress }) => isRunning(previous)
? running(progress)
: refreshing(previous, progress) as Final<A, E, P> & Refreshing<P>
),
Effect.andThen(({ next }) => state.set(next)),
),
}
@@ -223,19 +225,11 @@ export const makeProgressLayer = <A, E, P = never>(): Layer.Layer<
export namespace unsafeForkEffect {
export type OutputContext<A, E, R, P> = Exclude<R, State<A, E, P> | Progress<P> | Progress<never>>
export type Options<A, E, P> = {
readonly initial?: Result<A, E, P>
export interface Options<A, E, P> {
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
readonly initial?: (Initial | Success<A> | Failure<A, E>) & ({} | WillFetch | WillRefresh)
readonly initialProgress?: P
readonly previous?: Final<A, E, P>
} & (
| {
readonly refresh: true
readonly previous: Final<A, E, P>
}
| {
readonly refresh?: false
}
)
}
}
export const unsafeForkEffect = <A, E, R, P = never>(
@@ -249,13 +243,17 @@ export const unsafeForkEffect = <A, E, R, P = never>(
Effect.bind("ref", () => Ref.make(options?.initial ?? initial<A, E, P>())),
Effect.bind("pubsub", () => PubSub.unbounded<Result<A, E, P>>()),
Effect.bind("fiber", ({ ref, pubsub }) => Effect.forkScoped(State<A, E, P>().pipe(
Effect.andThen(state => state.set(options?.refresh
? refreshing(options.previous, options?.initialProgress) as Result<A, E, P>
: running(options?.initialProgress)
Effect.andThen(state => state.set(
(isFinal(options?.initial) && hasWillRefreshFlag(options?.initial))
? refreshing(options.initial, options?.initialProgress) as Result<A, E, P>
: running(options?.initialProgress)
).pipe(
Effect.andThen(effect),
Effect.onExit(exit => Effect.andThen(
state.set(fromExit(exit, (options?.previous && isSuccess(options.previous)) ? options.previous : undefined)),
state.set(fromExit(
exit,
isSuccess(options?.initial) ? options.initial : undefined),
),
Effect.forkScoped(PubSub.shutdown(pubsub)),
)),
)),
@@ -286,7 +284,7 @@ export const unsafeForkEffect = <A, E, R, P = never>(
export namespace forkEffect {
export type InputContext<R, P> = R extends Progress<infer X> ? [X] extends [P] ? R : never : R
export type OutputContext<A, E, R, P> = unsafeForkEffect.OutputContext<A, E, R, P>
export type Options<A, E, P> = unsafeForkEffect.Options<A, E, P>
export interface Options<A, E, P> extends unsafeForkEffect.Options<A, E, P> {}
}
export const forkEffect: {