0.1.13 #18

Merged
Thilawyn merged 359 commits from next into master 2025-06-18 00:12:19 +02:00
Showing only changes of commit 87d27dd48d - Show all commits

View File

@@ -1,4 +1,4 @@
import { type Context, Effect, ExecutionStrategy, Exit, type Fiber, type Layer, Option, pipe, Pipeable, Queue, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect" import { type Context, Effect, ExecutionStrategy, Exit, type Fiber, type Layer, Match, Option, pipe, Pipeable, Queue, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
import * as React from "react" import * as React from "react"
import * as ReffuseContext from "./ReffuseContext.js" import * as ReffuseContext from "./ReffuseContext.js"
import * as ReffuseRuntime from "./ReffuseRuntime.js" import * as ReffuseRuntime from "./ReffuseRuntime.js"
@@ -14,6 +14,10 @@ export interface ScopeOptions {
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
} }
export interface UseScopeOptions extends ScopeOptions {
readonly finalizerExecutionMode?: "sync" | "fork"
}
export type RefsA<T extends readonly SubscriptionRef.SubscriptionRef<any>[]> = { export type RefsA<T extends readonly SubscriptionRef.SubscriptionRef<any>[]> = {
[K in keyof T]: Effect.Effect.Success<T[K]> [K in keyof T]: Effect.Effect.Success<T[K]>
} }
@@ -91,9 +95,10 @@ export abstract class ReffuseNamespace<R> {
useScope<R>( useScope<R>(
this: ReffuseNamespace<R>, this: ReffuseNamespace<R>,
deps: React.DependencyList = [], deps: React.DependencyList = [],
options?: ScopeOptions, options?: UseScopeOptions,
): Scope.Scope { ): Scope.Scope {
const runSync = this.useRunSync() const runSync = this.useRunSync()
const runFork = this.useRunFork()
const [isInitialRun, initialScope] = React.useMemo(() => runSync(Effect.all([ const [isInitialRun, initialScope] = React.useMemo(() => runSync(Effect.all([
Ref.make(true), Ref.make(true),
@@ -106,17 +111,29 @@ export abstract class ReffuseNamespace<R> {
Effect.if({ Effect.if({
onTrue: () => Effect.as( onTrue: () => Effect.as(
Ref.set(isInitialRun, false), Ref.set(isInitialRun, false),
() => runSync(Scope.close(initialScope, Exit.void)), () => Scope.close(initialScope, Exit.void).pipe(
effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe(
Match.when("sync", () => { runSync(effect) }),
Match.when("fork", () => { runFork(effect) }),
Match.exhaustive,
)
),
), ),
onFalse: () => Scope.make(options?.finalizerExecutionStrategy).pipe( onFalse: () => Scope.make(options?.finalizerExecutionStrategy).pipe(
Effect.tap(v => Effect.sync(() => setScope(v))), Effect.tap(v => Effect.sync(() => setScope(v))),
Effect.map(v => () => runSync(Scope.close(v, Exit.void))), Effect.map(v => () => Scope.close(v, Exit.void).pipe(
effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe(
Match.when("sync", () => { runSync(effect) }),
Match.when("fork", () => { runFork(effect) }),
Match.exhaustive,
)
)),
), ),
}), }),
runSync, runSync,
), [runSync, ...deps]) ), [runSync, runFork, ...deps])
return scope return scope
} }
@@ -144,51 +161,6 @@ export abstract class ReffuseNamespace<R> {
]) ])
} }
useMemoScoped<A, E, R>(
this: ReffuseNamespace<R>,
effect: () => Effect.Effect<A, E, R | Scope.Scope>,
deps: React.DependencyList,
options?: RenderOptions & ScopeOptions,
): A {
const runSync = this.useRunSync()
const { isInitialRun, initialScope, initialValue } = React.useMemo(() => Effect.Do.pipe(
Effect.bind("isInitialRun", () => Ref.make(true)),
Effect.bind("initialScope", () => Scope.make(options?.finalizerExecutionStrategy)),
Effect.bind("initialValue", ({ initialScope }) => Effect.provideService(effect(), Scope.Scope, initialScope)),
runSync,
), [])
const [value, setValue] = React.useState(initialValue)
React.useEffect(() => isInitialRun.pipe(
Effect.if({
onTrue: () => Effect.as(
Ref.set(isInitialRun, false),
() => runSync(Scope.close(initialScope, Exit.void)),
),
onFalse: () => Effect.Do.pipe(
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy)),
Effect.bind("value", ({ scope }) => Effect.provideService(effect(), Scope.Scope, scope)),
Effect.tap(({ value }) =>
Effect.sync(() => setValue(value))
),
Effect.map(({ scope }) =>
() => runSync(Scope.close(scope, Exit.void))
),
),
}),
runSync,
), [
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync],
...deps,
])
return value
}
/** /**
* Reffuse equivalent to `React.useEffect`. * Reffuse equivalent to `React.useEffect`.
* *