import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, String, Tracer, type Types, type Utils } from "effect" import * as React from "react" import { Hooks } from "./hooks/index.js" import * as Memoized from "./Memoized.js" export const TypeId: unique symbol = Symbol.for("effect-fc/Component") export type TypeId = typeof TypeId export interface Component extends Effect.Effect<(props: P) => A, never, Exclude>, Component.Options { new(_: never): {} readonly [TypeId]: TypeId readonly ["~Props"]: P readonly ["~Success"]: A readonly ["~Error"]: E readonly ["~Context"]: R /** @internal */ readonly body: (props: P) => Effect.Effect /** @internal */ makeFunctionComponent( runtimeRef: React.Ref>>, scope: Scope.Scope, ): (props: P) => A } export namespace Component { export type Props> = T extends Component ? P : never export type Success> = T extends Component ? A : never export type Error> = T extends Component ? E : never export type Context> = T extends Component ? R : never export type AsComponent> = Component, Success, Error, Context> export interface Options { readonly displayName?: string readonly finalizerExecutionMode: "sync" | "fork" readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy } } const ComponentProto = Object.freeze({ ...Effectable.CommitPrototype, [TypeId]: TypeId, commit: Effect.fnUntraced(function* ( this: Component ) { const self = this const runtimeRef = React.useRef>>(null!) runtimeRef.current = yield* Effect.runtime>() return React.useCallback(function ScopeProvider(props: P) { const scope = Runtime.runSync(runtimeRef.current)(Hooks.useScope( Array.from( Context.omit(...nonReactiveTags)(runtimeRef.current.context).unsafeMap.values() ), self, )) const FC = React.useMemo(() => { const f: React.FC

= self.makeFunctionComponent(runtimeRef, scope) f.displayName = self.displayName ?? "Anonymous" return Memoized.isMemoized(self) ? React.memo(f, self.propsAreEqual) : f }, [scope]) return React.createElement(FC as React.FC<{}>, props as {}) }, []) }), makeFunctionComponent( this: Component, runtimeRef: React.RefObject>>, scope: Scope.Scope, ) { return (props: P) => Runtime.runSync(runtimeRef.current)( Effect.provideService(this.body(props), Scope.Scope, scope) ) }, } as const) const defaultOptions = { finalizerExecutionMode: "sync", finalizerExecutionStrategy: ExecutionStrategy.sequential, } as const const nonReactiveTags = [Tracer.ParentSpan] as const export const isComponent = (u: unknown): u is Component => Predicate.hasProperty(u, TypeId) export namespace make { export type Gen = { >, A extends React.ReactNode, P = never>( body: (props: P) => Generator ): Component< P, A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never > >, A, B extends Effect.Effect, P = never>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B ): Component, Effect.Effect.Error, Effect.Effect.Context> >, A, B, C extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, ): Component, Effect.Effect.Context> >, A, B, C, D extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, ): Component, Effect.Effect.Context> >, A, B, C, D, E extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, ): Component, Effect.Effect.Context> >, A, B, C, D, E, F extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, ): Component, Effect.Effect.Context> >, A, B, C, D, E, F, G extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, ): Component, Effect.Effect.Context> >, A, B, C, D, E, F, G, H extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, ): Component, Effect.Effect.Context> >, A, B, C, D, E, F, G, H, I extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, ): Component, Effect.Effect.Context> >, A, B, C, D, E, F, G, H, I, J extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, i: (_: I, props: NoInfer

) => J, ): Component, Effect.Effect.Context> } export type NonGen = { , P extends {} = {}>( body: (props: P) => Eff ): Component, Effect.Effect.Context> , A, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, C, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, C, D, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, C, D, E, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, C, D, E, F, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, C, D, E, F, G, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, C, D, E, F, G, H, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> , A, B, C, D, E, F, G, H, I, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, i: (_: I, props: NoInfer

) => Eff, ): Component, Effect.Effect.Context> } } export const make: ( & make.Gen & make.NonGen & (( spanName: string, spanOptions?: Tracer.SpanOptions, ) => make.Gen & make.NonGen) ) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => { if (typeof spanNameOrBody !== "string") { const displayName = displayNameFromBody(spanNameOrBody) return Object.setPrototypeOf( Object.assign(function() {}, defaultOptions, { body: displayName ? Effect.fn(displayName)(spanNameOrBody as any, ...pipeables as []) : Effect.fn(spanNameOrBody as any, ...pipeables), displayName, }), ComponentProto, ) } else { const spanOptions = pipeables[0] return (body: any, ...pipeables: any[]) => Object.setPrototypeOf( Object.assign(function() {}, defaultOptions, { body: Effect.fn(spanNameOrBody, spanOptions)(body, ...pipeables as []), displayName: displayNameFromBody(body) ?? spanNameOrBody, }), ComponentProto, ) } } export const makeUntraced: make.Gen & make.NonGen = ( body: Function, ...pipeables: any[] ) => Object.setPrototypeOf( Object.assign(function() {}, defaultOptions, { body: Effect.fnUntraced(body as any, ...pipeables as []), displayName: displayNameFromBody(body), }), ComponentProto, ) const displayNameFromBody = (body: Function) => !String.isEmpty(body.name) ? body.name : undefined export const withOptions: { >( options: Partial ): (self: T) => T >( self: T, options: Partial, ): T } = Function.dual(2, >( self: T, options: Partial, ): T => Object.setPrototypeOf( Object.assign(function() {}, self, options), Object.getPrototypeOf(self), )) export const withRuntime: { ( context: React.Context>, ): (self: Component>) => (props: P) => A ( self: Component>, context: React.Context>, ): (props: P) => A } = Function.dual(2,

( self: Component, context: React.Context>, ) => function WithRuntime(props: P) { return React.createElement( Runtime.runSync(React.useContext(context))(self), props, ) })