import { Context, Effect, ExecutionStrategy, Function, pipe, Pipeable, Predicate, Runtime, Scope, String, Tracer, type Utils } from "effect" import * as React from "react" import * as Hook from "./Hook.js" import * as Memoized from "./Memoized.js" import * as Suspense from "./Suspense.js" export const TypeId: unique symbol = Symbol.for("effect-fc/Component") export type TypeId = typeof TypeId export interface Component extends Pipeable.Pipeable, Component.Options { new(_: never): {} readonly [TypeId]: TypeId readonly body: (props: P) => Effect.Effect } export namespace Component { export type Error = T extends Component ? E : never export type Context = T extends Component ? R : never export type Props = T extends Component ? P : never export interface Options { readonly displayName?: string readonly finalizerExecutionMode: "sync" | "fork" readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy } } const ComponentProto = Object.seal({ [TypeId]: TypeId, pipe() { return Pipeable.pipeArguments(this, arguments) } } as const) const makeWithDefaults = (): Component => Object.assign( Object.setPrototypeOf(function() {}, ComponentProto), { finalizerExecutionMode: "sync", finalizerExecutionStrategy: ExecutionStrategy.sequential, } ) const nonReactiveTags = [Tracer.ParentSpan] as const export const isComponent = (u: unknown): u is Component => Predicate.hasProperty(u, TypeId) export namespace make { export type Gen = { >, P extends {} = {}>( body: (props: P) => Generator, ): Component< [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never, P > >, A, B 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 ): Component, Effect.Effect.Context, P> >, 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, P> >, 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, P> >, 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, P> >, 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, P> >, 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, P> >, 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, P> >, 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, P> >, 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, P> } export type NonGen = { , P extends {} = {}>( body: (props: P) => Eff ): Component, Effect.Effect.Context, P> , A, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

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

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

) => Eff, ): Component, Effect.Effect.Context, P> , 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, P> , 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, P> , 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, P> , 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, P> , 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, P> , 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, P> , 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, P> } } 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.assign(makeWithDefaults(), { body: displayName ? Effect.fn(displayName)(spanNameOrBody as any, ...pipeables as []) : Effect.fn(spanNameOrBody as any, ...pipeables), displayName, }) } else { const spanOptions = pipeables[0] return (body: any, ...pipeables: any[]) => Object.assign(makeWithDefaults(), { body: Effect.fn(spanNameOrBody, spanOptions)(body, ...pipeables as []), displayName: displayNameFromBody(body) ?? spanNameOrBody, }) } } export const makeUntraced: make.Gen & make.NonGen = ( body: Function, ...pipeables: any[] ) => Object.assign(makeWithDefaults(), { 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( { ...self, ...options }, Object.getPrototypeOf(self), )) export const useFC: { ( self: Component ): Effect.Effect, never, Exclude> } = Effect.fn("useFC")(function* ( self: Component ) { const runtimeRef = React.useRef>>(null!) runtimeRef.current = yield* Effect.runtime>() return React.useCallback(function ScopeProvider(props: P) { const scope = Runtime.runSync(runtimeRef.current)(Hook.useScope( Array.from( Context.omit(...nonReactiveTags)(runtimeRef.current.context).unsafeMap.values() ), self, )) const FC = React.useMemo(() => { const f: React.FC

= Suspense.isSuspense(self) ? pipe( function SuspenseInner(props: { readonly promise: Promise }) { return React.use(props.promise) }, SuspenseInner => ({ fallback, name, ...props }: P & Suspense.Suspense.Props) => { const promise = Runtime.runPromise(runtimeRef.current)( Effect.provideService(self.body(props as P), Scope.Scope, scope) ) return React.createElement( React.Suspense, { fallback: fallback ?? self.defaultFallback, name }, React.createElement(SuspenseInner, { promise }), ) }, ) : (props: P) => Runtime.runSync(runtimeRef.current)( Effect.provideService(self.body(props), Scope.Scope, scope) ) f.displayName = self.displayName ?? "Anonymous" return Memoized.isMemoized(self) ? React.memo(f, self.propsAreEqual) : f }, [scope]) return React.createElement(FC, props) }, []) }) export const use: { ( self: Component, fn: (Component: React.FC

) => React.ReactNode, ): Effect.Effect> } = Effect.fn("use")(function*(self, fn) { return fn(yield* useFC(self)) }) export const withRuntime: { ( context: React.Context>, ): (self: Component) => React.FC

( self: Component, context: React.Context>, ): React.FC

} = Function.dual(2, ( self: Component, context: React.Context>, ): React.FC

=> function WithRuntime(props) { const runtime = React.useContext(context) return React.createElement(Runtime.runSync(runtime)(useFC(self)), props) })