0.3.0 (#50)
Publish / publish (push) Successful in 26s
Lint / lint (push) Successful in 13s

Co-authored-by: Julien Valverdé <julien.valverde@mailo.com>
Reviewed-on: #50
This commit was merged in pull request #50.
This commit is contained in:
2026-06-04 17:06:51 +02:00
parent e5d0808b02
commit 721ab7d736
10 changed files with 282 additions and 231 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "effect-fc",
"description": "Write React function components with Effect",
"version": "0.2.6",
"version": "0.3.0",
"type": "module",
"files": [
"./README.md",
@@ -46,6 +46,6 @@
"react": "^19.2.0"
},
"dependencies": {
"effect-lens": "^0.1.5"
"effect-lens": "^0.2.0"
}
}
+8 -7
View File
@@ -37,8 +37,8 @@ export type AsyncProps = Omit<React.SuspenseProps, "children">
export const AsyncPrototype: AsyncPrototype = Object.freeze({
[AsyncTypeId]: AsyncTypeId,
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R>(
this: Component.Component<P, A, E, R> & Async,
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Component.Signature>(
this: Component.Component<P, A, E, R, F> & Async,
runtimeRef: React.RefObject<Runtime.Runtime<Exclude<R, Scope.Scope>>>,
) {
const Inner = (props: { readonly promise: Promise<React.ReactNode> }) => React.use(props.promise)
@@ -106,7 +106,7 @@ export const isAsync = (u: unknown): u is Async => Predicate.hasProperty(u, Asyn
* )
* ```
*/
export const async = <T extends Component.Component<any, any, any, any>>(
export const async = <T extends Component.Component.Any>(
self: T & (
"promise" extends keyof Component.Component.Props<T>
? "The 'promise' prop name is restricted for Async components. Please rename the 'promise' prop to something else."
@@ -118,7 +118,8 @@ export const async = <T extends Component.Component<any, any, any, any>>(
Component.Component.Props<T> & AsyncProps,
Component.Component.Success<T>,
Component.Component.Error<T>,
Component.Component.Context<T>
Component.Component.Context<T>,
Component.Component.DefaultSignature<Component.Component.Props<T> & AsyncProps, Component.Component.Success<T>>
>
& Async
) => Object.setPrototypeOf(
@@ -154,14 +155,14 @@ export const async = <T extends Component.Component<any, any, any, any>>(
* ```
*/
export const withOptions: {
<T extends Component.Component<any, any, any, any> & Async>(
<T extends Component.Component.Any & Async>(
options: Partial<AsyncOptions>
): (self: T) => T
<T extends Component.Component<any, any, any, any> & Async>(
<T extends Component.Component.Any & Async>(
self: T,
options: Partial<AsyncOptions>,
): T
} = Function.dual(2, <T extends Component.Component<any, any, any, any> & Async>(
} = Function.dual(2, <T extends Component.Component.Any & Async>(
self: T,
options: Partial<AsyncOptions>,
): T => Object.setPrototypeOf(
+86 -73
View File
@@ -10,49 +10,52 @@ export type ComponentTypeId = typeof ComponentTypeId
/**
* Represents an Effect-based React Component that integrates the Effect system with React.
*/
export interface Component<P extends {}, A extends React.ReactNode, E, R>
extends ComponentPrototype<P, A, R>, ComponentOptions {
export interface Component<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>
extends ComponentPrototype<R, F>, ComponentOptions {
new(_: never): Record<string, never>
readonly [ComponentTypeId]: ComponentTypeId
readonly "~Props": P
readonly "~Success": A
readonly "~Error": E
readonly "~Context": R
readonly "~Function": F
readonly body: (props: P) => Effect.Effect<A, E, R>
}
export declare namespace Component {
export type Props<T extends Component<any, any, any, any>> = [T] extends [Component<infer P, infer _A, infer _E, infer _R>] ? P : never
export type Success<T extends Component<any, any, any, any>> = [T] extends [Component<infer _P, infer A, infer _E, infer _R>] ? A : never
export type Error<T extends Component<any, any, any, any>> = [T] extends [Component<infer _P, infer _A, infer E, infer _R>] ? E : never
export type Context<T extends Component<any, any, any, any>> = [T] extends [Component<infer _P, infer _A, infer _E, infer R>] ? R : never
export type Default<P extends {}, A extends React.ReactNode, E, R> = Component<P, A, E, R, DefaultSignature<P, A>>
export type Any = Component<any, any, any, any, any>
export type AsComponent<T extends Component<any, any, any, any>> = Component<Props<T>, Success<T>, Error<T>, Context<T>>
export type Signature = (props: any) => React.ReactNode
export type DefaultSignature<P extends {}, A extends React.ReactNode> = (props: P) => A
export type Props<T extends Any> = [T] extends [Component<infer P, infer _A, infer _E, infer _R, infer _F>] ? P : never
export type Success<T extends Any> = [T] extends [Component<infer _P, infer A, infer _E, infer _R, infer _F>] ? A : never
export type Error<T extends Any> = [T] extends [Component<infer _P, infer _A, infer E, infer _R, infer _F>] ? E : never
export type Context<T extends Any> = [T] extends [Component<infer _P, infer _A, infer _E, infer R, infer _F>] ? R : never
export type Function<T extends Any> = [T] extends [Component<infer _P, infer _A, infer _E, infer _R, infer F>] ? F : never
export type AsComponent<T extends Any> = Component<Props<T>, Success<T>, Error<T>, Context<T>, Function<T>>
}
export interface ComponentPrototype<P extends {}, A extends React.ReactNode, R>
extends Pipeable.Pipeable {
readonly [ComponentTypeId]: ComponentTypeId
readonly use: Effect.Effect<(props: P) => A, never, Exclude<R, Scope.Scope>>
export interface ComponentImpl<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>
extends Component<P, A, E, R, F>, ComponentImplPrototype<R, F> {}
asFunctionComponent(
runtimeRef: React.Ref<Runtime.Runtime<Exclude<R, Scope.Scope>>>
): (props: P) => A
export interface ComponentImplPrototype<R, F extends Component.Signature> {
readonly use: Effect.Effect<F, never, Exclude<R, Scope.Scope>>
setFunctionComponentName(f: React.FC<P>): void
transformFunctionComponent(f: React.FC<P>): React.FC<P>
asFunctionComponent(runtimeRef: React.Ref<Runtime.Runtime<Exclude<R, Scope.Scope>>>): F
setFunctionComponentName(f: F): void
transformFunctionComponent(f: F): F
}
export const ComponentPrototype: ComponentPrototype<any, any, any> = Object.freeze({
[ComponentTypeId]: ComponentTypeId,
...Pipeable.Prototype,
export const ComponentImplPrototype: ComponentImplPrototype<any, any> = Object.freeze({
get use() { return use(this) },
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R>(
this: Component<P, A, E, R>,
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
this: ComponentImpl<P, A, E, R, F>,
runtimeRef: React.RefObject<Runtime.Runtime<Exclude<R, Scope.Scope>>>,
) {
return (props: P) => Runtime.runSync(runtimeRef.current)(
@@ -63,8 +66,8 @@ export const ComponentPrototype: ComponentPrototype<any, any, any> = Object.free
)
},
setFunctionComponentName<P extends {}, A extends React.ReactNode, E, R>(
this: Component<P, A, E, R>,
setFunctionComponentName<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
this: ComponentImpl<P, A, E, R, F>,
f: React.FC<P>,
) {
f.displayName = this.displayName ?? "Anonymous"
@@ -73,8 +76,8 @@ export const ComponentPrototype: ComponentPrototype<any, any, any> = Object.free
transformFunctionComponent: identity,
} as const)
const use = Effect.fnUntraced(function* <P extends {}, A extends React.ReactNode, E, R>(
self: Component<P, A, E, R>
const use = Effect.fnUntraced(function* <P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
self: ComponentImpl<P, A, E, R, F>
) {
// biome-ignore lint/style/noNonNullAssertion: React ref initialization
const runtimeRef = React.useRef<Runtime.Runtime<Exclude<R, Scope.Scope>>>(null!)
@@ -82,7 +85,7 @@ const use = Effect.fnUntraced(function* <P extends {}, A extends React.ReactNode
return yield* React.useState(() => Runtime.runSync(runtimeRef.current)(Effect.cachedFunction(
(_services: readonly any[]) => Effect.sync(() => {
const f: React.FC<P> = self.asFunctionComponent(runtimeRef)
const f = self.asFunctionComponent(runtimeRef)
self.setFunctionComponentName(f)
return self.transformFunctionComponent(f)
}),
@@ -93,6 +96,23 @@ const use = Effect.fnUntraced(function* <P extends {}, A extends React.ReactNode
})
export interface ComponentPrototype<R, F extends Component.Signature>
extends Pipeable.Pipeable {
readonly [ComponentTypeId]: ComponentTypeId
readonly use: Effect.Effect<F, never, Exclude<R, Scope.Scope>>
}
export const ComponentPrototype: ComponentPrototype<any, any> = Object.freeze(
Object.defineProperties(
{
[ComponentTypeId]: ComponentTypeId,
...Pipeable.Prototype,
},
Object.getOwnPropertyDescriptors(ComponentImplPrototype),
) as ComponentPrototype<any, any>
)
export interface ComponentOptions {
/**
* Custom display name for the component in React DevTools and debugging utilities.
@@ -131,13 +151,13 @@ export const defaultOptions: ComponentOptions = {
}
export const isComponent = (u: unknown): u is Component<{}, React.ReactNode, unknown, unknown> => Predicate.hasProperty(u, ComponentTypeId)
export const isComponent = (u: unknown): u is Component.Default<{}, React.ReactNode, unknown, unknown> => Predicate.hasProperty(u, ComponentTypeId)
export declare namespace make {
export type Gen = {
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A extends React.ReactNode, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>
): Component<
): Component.Default<
P, A,
[Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E : never,
[Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R : never
@@ -152,7 +172,7 @@ export declare namespace make {
>,
props: NoInfer<P>,
) => B,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<B>>, Effect.Effect.Error<B>, Effect.Effect.Context<B>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<B>>, Effect.Effect.Error<B>, Effect.Effect.Context<B>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -164,7 +184,7 @@ export declare namespace make {
props: NoInfer<P>,
) => B,
b: (_: B, props: NoInfer<P>) => C,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<C>>, Effect.Effect.Error<C>, Effect.Effect.Context<C>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<C>>, Effect.Effect.Error<C>, Effect.Effect.Context<C>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C, D extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -177,7 +197,7 @@ export declare namespace make {
) => B,
b: (_: B, props: NoInfer<P>) => C,
c: (_: C, props: NoInfer<P>) => D,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<D>>, Effect.Effect.Error<D>, Effect.Effect.Context<D>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<D>>, Effect.Effect.Error<D>, Effect.Effect.Context<D>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C, D, E extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -191,7 +211,7 @@ export declare namespace make {
b: (_: B, props: NoInfer<P>) => C,
c: (_: C, props: NoInfer<P>) => D,
d: (_: D, props: NoInfer<P>) => E,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<E>>, Effect.Effect.Error<E>, Effect.Effect.Context<E>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<E>>, Effect.Effect.Error<E>, Effect.Effect.Context<E>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C, D, E, F extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -206,7 +226,7 @@ export declare namespace make {
c: (_: C, props: NoInfer<P>) => D,
d: (_: D, props: NoInfer<P>) => E,
e: (_: E, props: NoInfer<P>) => F,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<F>>, Effect.Effect.Error<F>, Effect.Effect.Context<F>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<F>>, Effect.Effect.Error<F>, Effect.Effect.Context<F>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C, D, E, F, G extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -222,7 +242,7 @@ export declare namespace make {
d: (_: D, props: NoInfer<P>) => E,
e: (_: E, props: NoInfer<P>) => F,
f: (_: F, props: NoInfer<P>) => G,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<G>>, Effect.Effect.Error<G>, Effect.Effect.Context<G>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<G>>, Effect.Effect.Error<G>, Effect.Effect.Context<G>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C, D, E, F, G, H extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -239,7 +259,7 @@ export declare namespace make {
e: (_: E, props: NoInfer<P>) => F,
f: (_: F, props: NoInfer<P>) => G,
g: (_: G, props: NoInfer<P>) => H,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<H>>, Effect.Effect.Error<H>, Effect.Effect.Context<H>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<H>>, Effect.Effect.Error<H>, Effect.Effect.Context<H>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C, D, E, F, G, H, I extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -257,7 +277,7 @@ export declare namespace make {
f: (_: F, props: NoInfer<P>) => G,
g: (_: G, props: NoInfer<P>) => H,
h: (_: H, props: NoInfer<P>) => I,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<I>>, Effect.Effect.Error<I>, Effect.Effect.Context<I>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<I>>, Effect.Effect.Error<I>, Effect.Effect.Context<I>>
<Eff extends Utils.YieldWrap<Effect.Effect<any, any, any>>, A, B, C, D, E, F, G, H, I, J extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Generator<Eff, A, never>,
a: (
@@ -276,35 +296,35 @@ export declare namespace make {
g: (_: G, props: NoInfer<P>) => H,
h: (_: H, props: NoInfer<P>) => I,
i: (_: I, props: NoInfer<P>) => J,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<J>>, Effect.Effect.Error<J>, Effect.Effect.Context<J>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<J>>, Effect.Effect.Error<J>, Effect.Effect.Context<J>>
}
export type NonGen = {
<Eff extends Effect.Effect<React.ReactNode, any, any>, P extends {} = {}>(
body: (props: P) => Eff
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
b: (_: B, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, C, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
b: (_: B, props: NoInfer<P>) => C,
c: (_: C, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, C, D, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
b: (_: B, props: NoInfer<P>) => C,
c: (_: C, props: NoInfer<P>) => D,
d: (_: D, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, C, D, E, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
@@ -312,7 +332,7 @@ export declare namespace make {
c: (_: C, props: NoInfer<P>) => D,
d: (_: D, props: NoInfer<P>) => E,
e: (_: E, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, C, D, E, F, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
@@ -321,7 +341,7 @@ export declare namespace make {
d: (_: D, props: NoInfer<P>) => E,
e: (_: E, props: NoInfer<P>) => F,
f: (_: F, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, C, D, E, F, G, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
@@ -331,7 +351,7 @@ export declare namespace make {
e: (_: E, props: NoInfer<P>) => F,
f: (_: F, props: NoInfer<P>) => G,
g: (_: G, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, C, D, E, F, G, H, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
@@ -342,7 +362,7 @@ export declare namespace make {
f: (_: F, props: NoInfer<P>) => G,
g: (_: G, props: NoInfer<P>) => H,
h: (_: H, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
<Eff extends Effect.Effect<React.ReactNode, any, any>, A, B, C, D, E, F, G, H, I, P extends {} = {}>(
body: (props: P) => A,
a: (_: A, props: NoInfer<P>) => B,
@@ -354,7 +374,7 @@ export declare namespace make {
g: (_: G, props: NoInfer<P>) => H,
h: (_: H, props: NoInfer<P>) => I,
i: (_: I, props: NoInfer<P>) => Eff,
): Component<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
): Component.Default<P, Effect.Effect.Success<Effect.Effect.AsEffect<Eff>>, Effect.Effect.Error<Eff>, Effect.Effect.Context<Eff>>
}
}
@@ -501,27 +521,20 @@ export const makeUntraced: (
)
export declare namespace withSignature {
export type Signature = (props: any) => React.ReactNode
export type Result<
T extends Component<any, any, any, any>,
F extends Signature,
> = Omit<T, "use" | "asFunctionComponent"> & {
readonly use: Effect.Effect<F, never, Exclude<Component.Context<T>, Scope.Scope>>
asFunctionComponent(
runtimeRef: React.Ref<Runtime.Runtime<Exclude<Component.Context<T>, Scope.Scope>>>
): F
}
export type Result<T extends Component.Any, F extends Component.Signature> = (
& Omit<T, keyof Component.AsComponent<T>>
& Component<Component.Props<T>, Component.Success<T>, Component.Error<T>, Component.Context<T>, F>
)
}
export const withSignature: {
<F extends withSignature.Signature>(): <T extends Component<any, any, any, any>>(
<F extends Component.Signature>(): <T extends Component.Any>(
self: T
) => withSignature.Result<T, F>
<F extends withSignature.Signature, T extends Component<any, any, any, any>>(
<F extends Component.Signature, T extends Component.Any>(
self: T
): withSignature.Result<T, F>
} = (self?: Component<any, any, any, any>): any => self === undefined
} = (self?: Component.Any): any => self === undefined
? identity
: self
@@ -542,14 +555,14 @@ export const withSignature: {
* ```
*/
export const withOptions: {
<T extends Component<any, any, any, any>>(
<T extends Component.Any>(
options: Partial<ComponentOptions>
): (self: T) => T
<T extends Component<any, any, any, any>>(
<T extends Component.Any>(
self: T,
options: Partial<ComponentOptions>,
): T
} = Function.dual(2, <T extends Component<any, any, any, any>>(
} = Function.dual(2, <T extends Component.Any>(
self: T,
options: Partial<ComponentOptions>,
): T => Object.setPrototypeOf(
@@ -595,19 +608,19 @@ export const withOptions: {
*
*/
export const withRuntime: {
<P extends {}, A extends React.ReactNode, E, R>(
<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
context: React.Context<Runtime.Runtime<R>>,
): (self: Component<P, A, E, Scope.Scope | NoInfer<R>>) => (props: P) => A
<P extends {}, A extends React.ReactNode, E, R>(
self: Component<P, A, E, Scope.Scope | NoInfer<R>>,
): (self: Component<P, A, E, Scope.Scope | NoInfer<R>, F>) => F
<P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
self: Component<P, A, E, Scope.Scope | NoInfer<R>, F>,
context: React.Context<Runtime.Runtime<R>>,
): (props: P) => A
} = Function.dual(2, <P extends {}, A extends React.ReactNode, E, R>(
self: Component<P, A, E, R>,
): F
} = Function.dual(2, <P extends {}, A extends React.ReactNode, E, R, F extends Component.Signature>(
self: Component<P, A, E, R, F>,
context: React.Context<Runtime.Runtime<R>>,
) => function WithRuntime(props: P) {
return React.createElement(
Runtime.runSync(React.useContext(context))(self.use),
Runtime.runSync(React.useContext(context))(self.use) as React.FC<P>,
props,
)
})
+4 -4
View File
@@ -61,7 +61,7 @@ export const isMemoized = (u: unknown): u is Memoized<unknown> => Predicate.hasP
* )
* ```
*/
export const memoized = <T extends Component.Component<any, any, any, any>>(
export const memoized = <T extends Component.Component.Any>(
self: T
): T & Memoized<Component.Component.Props<T>> => Object.setPrototypeOf(
Object.assign(function() {}, self),
@@ -96,14 +96,14 @@ export const memoized = <T extends Component.Component<any, any, any, any>>(
* ```
*/
export const withOptions: {
<T extends Component.Component<any, any, any, any> & Memoized<any>>(
<T extends Component.Component.Any & Memoized<any>>(
options: Partial<MemoizedOptions<Component.Component.Props<T>>>
): (self: T) => T
<T extends Component.Component<any, any, any, any> & Memoized<any>>(
<T extends Component.Component.Any & Memoized<any>>(
self: T,
options: Partial<MemoizedOptions<Component.Component.Props<T>>>,
): T
} = Function.dual(2, <T extends Component.Component<any, any, any, any> & Memoized<any>>(
} = Function.dual(2, <T extends Component.Component.Any & Memoized<any>>(
self: T,
options: Partial<MemoizedOptions<Component.Component.Props<T>>>,
): T => Object.setPrototypeOf(
+10 -10
View File
@@ -212,24 +212,24 @@ export const unsafeForkEffect = Effect.fnUntraced(function* <A, E, R, P = never>
never,
Scope.Scope | unsafeForkEffect.OutputContext<R, P>
> {
const ref = yield* SynchronizedRef.make<Result<A, E, P>>(options?.initial ?? initial<A, E, P>())
const ref = (yield* SynchronizedRef.make(
options?.initial ?? initial<A, E, P>()
)) as Lens.SynchronizedRefLensImpl.SynchronizedRefWithInternals<Result<A, E, P>>
const pubsub = yield* PubSub.unbounded<Result<A, E, P>>()
const state = Lens.make<Result<A, E, P>, never, never, never, never>({
get get() { return Ref.get(ref) },
const state = Lens.make({
get: Ref.get(ref.ref),
get changes() {
return Stream.unwrapScoped(Effect.map(
Effect.all([Ref.get(ref), Stream.fromPubSub(pubsub, { scoped: true })]),
Effect.all([Ref.get(ref.ref), Stream.fromPubSub(pubsub, { scoped: true })]),
([latest, stream]) => Stream.concat(Stream.make(latest), stream),
))
},
modify: f => Ref.get(ref).pipe(
Effect.flatMap(f),
Effect.flatMap(([b, a]) => Ref.set(ref, a).pipe(
Effect.as(b),
Effect.zipLeft(PubSub.publish(pubsub, a))
)),
commit: value => Effect.zipLeft(
Ref.set(ref.ref, value),
PubSub.publish(pubsub, value),
),
lock: Effect.succeed(ref.withLock),
})
const fiber = yield* Effect.gen(function*() {
+13 -19
View File
@@ -53,27 +53,21 @@ extends Pipeable.Class() implements SubmittableForm<A, I, R, MA, ME, MR, MP> {
) {
super()
this.encodedValue = Effect.succeed(this).pipe(
Effect.map(self => Lens.make<I, never, never, never, never>({
get get() { return self.internalEncodedValue.get },
get changes() { return self.internalEncodedValue.changes },
modify: f => self.internalEncodedValue.modify(
encodedValue => Effect.map(
f(encodedValue),
([b, nextEncodedValue]) => [
[b, nextEncodedValue] as const,
nextEncodedValue,
] as const,
)
).pipe(
Effect.tap(([, nextEncodedValue]) =>
self.synchronizeEncodedValue(nextEncodedValue).pipe(
Effect.forkScoped,
Effect.provide(self.context),
)
this.encodedValue = Effect.all([
Effect.succeed(this),
Effect.succeed(Lens.asLensImpl(this.internalEncodedValue)),
]).pipe(
Effect.map(([self, parent]) => Lens.make({
get: parent.get,
get changes() { return parent.changes },
commit: a => Effect.andThen(
Effect.flatMap(
parent.resolve,
resolved => resolved.commit(Effect.succeed(a)),
),
Effect.map(([b]) => b),
self.synchronizeEncodedValue(a),
),
lock: parent.lock,
})),
Lens.unwrap,
)
+17 -18
View File
@@ -15,7 +15,7 @@ export interface SynchronizedForm<
in out TEW = never,
in out TRR = never,
in out TRW = never,
> extends Form.Form<readonly [], A, I, TER, TEW> {
> extends Form.Form<readonly [], A, I, TER, TER | TEW> {
readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId
readonly schema: Schema.Schema<A, I, R>
@@ -41,7 +41,7 @@ export class SynchronizedFormImpl<
readonly path = [] as const
readonly value: Subscribable.Subscribable<Option.Option<A>, never, never>
readonly encodedValue: Lens.Lens<I, TER, TEW, never, never>
readonly encodedValue: Lens.Lens<I, TER, TER | TEW, never, never>
readonly isValidating: Subscribable.Subscribable<boolean, never, never>
readonly canCommit: Subscribable.Subscribable<boolean, never, never>
@@ -61,7 +61,7 @@ export class SynchronizedFormImpl<
this.value = Effect.succeed(this).pipe(
Effect.map(self => Subscribable.make({
get get() { return Effect.provide(Effect.option(self.target.get), self.context) },
get: Effect.provide(Effect.option(self.target.get), self.context),
get changes() {
return Stream.provideContext(
self.target.changes.pipe(
@@ -74,22 +74,21 @@ export class SynchronizedFormImpl<
})),
Subscribable.unwrap,
)
this.encodedValue = Effect.succeed(this).pipe(
Effect.map(self => Lens.make<I, TER, TEW, never, never>({
get get() { return self.internalEncodedValue.get },
get changes() { return self.internalEncodedValue.changes },
modify: f => self.internalEncodedValue.modify(
encodedValue => Effect.map(
f(encodedValue),
([b, nextEncodedValue]) => [
[b, nextEncodedValue] as const,
nextEncodedValue,
] as const,
)
).pipe(
Effect.tap(([, nextEncodedValue]) => self.synchronizeEncodedValue(nextEncodedValue)),
Effect.map(([b]) => b),
this.encodedValue = Effect.all([
Effect.succeed(this),
Effect.succeed(Lens.asLensImpl(this.internalEncodedValue)),
]).pipe(
Effect.map(([self, parent]) => Lens.make<I, TER, TER | TEW, never, never>({
get: parent.get,
get changes() { return parent.changes },
commit: a => Effect.andThen(
Effect.flatMap(
parent.resolve,
resolved => resolved.commit(Effect.succeed(a)),
),
self.synchronizeEncodedValue(a),
),
lock: parent.lock,
})),
Lens.unwrap,
)
+11 -11
View File
@@ -13,17 +13,17 @@
"clean:modules": "rm -rf node_modules"
},
"devDependencies": {
"@tanstack/react-router": "^1.169.1",
"@tanstack/react-router-devtools": "^1.166.13",
"@tanstack/router-plugin": "^1.167.32",
"@types/react": "^19.2.14",
"@tanstack/react-router": "^1.170.10",
"@tanstack/react-router-devtools": "^1.167.0",
"@tanstack/router-plugin": "^1.168.13",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"@vitejs/plugin-react": "^6.0.2",
"globals": "^17.6.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"type-fest": "^5.6.0",
"vite": "^8.0.10"
"react": "^19.2.6",
"react-dom": "^19.2.6",
"type-fest": "^5.7.0",
"vite": "^8.0.16"
},
"dependencies": {
"@effect/platform": "^0.96.1",
@@ -35,8 +35,8 @@
"react-icons": "^5.6.0"
},
"overrides": {
"@types/react": "^19.2.14",
"@types/react": "^19.2.15",
"effect": "^3.21.2",
"react": "^19.2.5"
"react": "^19.2.6"
}
}