@reffuse/extension-query 0.1.5 #16

Merged
Thilawyn merged 347 commits from next into master 2025-06-01 05:28:47 +02:00
2 changed files with 48 additions and 27 deletions
Showing only changes of commit dda868d444 - Show all commits

View File

@@ -1,4 +1,4 @@
import { Array, Context, Effect, ExecutionStrategy, Exit, Layer, Ref, Runtime, Scope } from "effect" import { Array, Context, Effect, ExecutionStrategy, Exit, Layer, Match, Ref, Runtime, Scope } from "effect"
import * as React from "react" import * as React from "react"
import * as ReffuseRuntime from "./ReffuseRuntime.js" import * as ReffuseRuntime from "./ReffuseRuntime.js"
@@ -25,6 +25,8 @@ export type R<T> = T extends ReffuseContext<infer R> ? R : never
export type ReactProvider<R> = React.FC<{ export type ReactProvider<R> = React.FC<{
readonly layer: Layer.Layer<R, unknown, Scope.Scope> readonly layer: Layer.Layer<R, unknown, Scope.Scope>
readonly scope?: Scope.Scope readonly scope?: Scope.Scope
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
readonly finalizerExecutionMode?: "sync" | "fork"
readonly children?: React.ReactNode readonly children?: React.ReactNode
}> }>
@@ -32,16 +34,25 @@ const makeProvider = <R>(Context: React.Context<Context.Context<R>>): ReactProvi
return function ReffuseContextReactProvider(props) { return function ReffuseContextReactProvider(props) {
const runtime = ReffuseRuntime.useRuntime() const runtime = ReffuseRuntime.useRuntime()
const runSync = React.useMemo(() => Runtime.runSync(runtime), [runtime]) const runSync = React.useMemo(() => Runtime.runSync(runtime), [runtime])
const runFork = React.useMemo(() => Runtime.runFork(runtime), [runtime])
const makeScope = React.useMemo(() => props.scope const makeScope = React.useMemo(() => props.scope
? Scope.fork(props.scope, ExecutionStrategy.sequential) ? Scope.fork(props.scope, props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
: Scope.make(), : Scope.make(props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential),
[props.scope]) [props.scope])
const makeContext = React.useCallback((scope: Scope.CloseableScope) => Effect.context<R>().pipe( const makeContext = (scope: Scope.CloseableScope) => Effect.context<R>().pipe(
Effect.provide(props.layer), Effect.provide(props.layer),
Effect.provideService(Scope.Scope, scope), Effect.provideService(Scope.Scope, scope),
), [props.layer]) )
const closeScope = (scope: Scope.CloseableScope) => Scope.close(scope, Exit.void).pipe(
effect => Match.value(props.finalizerExecutionMode ?? "sync").pipe(
Match.when("sync", () => { runSync(effect) }),
Match.when("fork", () => { runFork(effect) }),
Match.exhaustive,
)
)
const [isInitialRun, initialScope, initialValue] = React.useMemo(() => Effect.Do.pipe( const [isInitialRun, initialScope, initialValue] = React.useMemo(() => Effect.Do.pipe(
Effect.bind("isInitialRun", () => Ref.make(true)), Effect.bind("isInitialRun", () => Ref.make(true)),
@@ -57,7 +68,7 @@ const makeProvider = <R>(Context: React.Context<Context.Context<R>>): ReactProvi
Effect.if({ Effect.if({
onTrue: () => Ref.set(isInitialRun, false).pipe( onTrue: () => Ref.set(isInitialRun, false).pipe(
Effect.map(() => Effect.map(() =>
() => runSync(Scope.close(initialScope, Exit.void)) () => closeScope(initialScope)
) )
), ),
@@ -68,13 +79,13 @@ const makeProvider = <R>(Context: React.Context<Context.Context<R>>): ReactProvi
Effect.sync(() => setValue(context)) Effect.sync(() => setValue(context))
), ),
Effect.map(({ scope }) => Effect.map(({ scope }) =>
() => runSync(Scope.close(scope, Exit.void)) () => closeScope(scope)
), ),
), ),
}), }),
runSync, runSync,
), [makeScope, makeContext, runSync]) ), [makeScope, runSync, runFork])
return React.createElement(Context, { ...props, value }) return React.createElement(Context, { ...props, value })
} }
@@ -84,6 +95,7 @@ export type AsyncReactProvider<R> = React.FC<{
readonly layer: Layer.Layer<R, unknown, Scope.Scope> readonly layer: Layer.Layer<R, unknown, Scope.Scope>
readonly scope?: Scope.Scope readonly scope?: Scope.Scope
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
readonly finalizerExecutionMode?: "sync" | "fork"
readonly fallback?: React.ReactNode readonly fallback?: React.ReactNode
readonly children?: React.ReactNode readonly children?: React.ReactNode
}> }>
@@ -112,7 +124,7 @@ const makeAsyncProvider = <R>(Context: React.Context<Context.Context<R>>): Async
const scope = runSync(props.scope const scope = runSync(props.scope
? Scope.fork(props.scope, props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential) ? Scope.fork(props.scope, props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
: Scope.make(props.finalizerExecutionStrategy) : Scope.make(props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
) )
Effect.context<R>().pipe( Effect.context<R>().pipe(
@@ -126,7 +138,13 @@ const makeAsyncProvider = <R>(Context: React.Context<Context.Context<R>>): Async
effect => runFork(effect, { ...props, scope }), effect => runFork(effect, { ...props, scope }),
) )
return () => { runFork(Scope.close(scope, Exit.void)) } return () => Scope.close(scope, Exit.void).pipe(
effect => Match.value(props.finalizerExecutionMode ?? "sync").pipe(
Match.when("sync", () => { runSync(effect) }),
Match.when("fork", () => { runFork(effect) }),
Match.exhaustive,
)
)
}, [props.layer, runSync, runFork]) }, [props.layer, runSync, runFork])
return React.createElement(React.Suspense, { return React.createElement(React.Suspense, {

View File

@@ -15,6 +15,7 @@ export interface ScopeOptions {
} }
export interface UseScopeOptions extends RenderOptions, ScopeOptions { export interface UseScopeOptions extends RenderOptions, ScopeOptions {
readonly scope?: Scope.Scope
readonly finalizerExecutionMode?: "sync" | "fork" readonly finalizerExecutionMode?: "sync" | "fork"
} }
@@ -101,10 +102,23 @@ export abstract class ReffuseNamespace<R> {
const runSync = this.useRunSync() const runSync = this.useRunSync()
const runFork = this.useRunFork() const runFork = this.useRunFork()
const makeScope = React.useMemo(() => options?.scope
? Scope.fork(options.scope, options.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
: Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential),
[options?.scope])
const closeScope = (scope: Scope.CloseableScope) => Scope.close(scope, Exit.void).pipe(
effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe(
Match.when("sync", () => { runSync(effect) }),
Match.when("fork", () => { runFork(effect) }),
Match.exhaustive,
)
)
const [isInitialRun, initialScope] = React.useMemo(() => runSync(Effect.all([ const [isInitialRun, initialScope] = React.useMemo(() => runSync(Effect.all([
Ref.make(true), Ref.make(true),
Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential), makeScope,
])), []) ])), [makeScope])
const [scope, setScope] = React.useState(initialScope) const [scope, setScope] = React.useState(initialScope)
@@ -112,29 +126,18 @@ export abstract class ReffuseNamespace<R> {
Effect.if({ Effect.if({
onTrue: () => Effect.as( onTrue: () => Effect.as(
Ref.set(isInitialRun, false), Ref.set(isInitialRun, false),
() => Scope.close(initialScope, Exit.void).pipe( () => closeScope(initialScope),
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: () => makeScope.pipe(
Effect.tap(v => Effect.sync(() => setScope(v))), Effect.tap(v => Effect.sync(() => setScope(v))),
Effect.map(v => () => Scope.close(v, Exit.void).pipe( Effect.map(v => () => closeScope(v)),
effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe(
Match.when("sync", () => { runSync(effect) }),
Match.when("fork", () => { runFork(effect) }),
Match.exhaustive,
)
)),
), ),
}), }),
runSync, runSync,
), [ ), [
makeScope,
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runFork], ...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runFork],
...deps, ...deps,
]) ])