import { Context, Effect, type Equivalence, 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 type { ExcludeKeys } from "./utils.js" export interface Component extends Pipeable.Pipeable { readonly body: (props: P) => Effect.Effect readonly displayName?: string readonly finalizerExecutionMode: "sync" | "fork" readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy } 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 } const ComponentProto = Object.seal({ pipe() { return Pipeable.pipeArguments(this, arguments) } } as const) const nonReactiveTags = [Tracer.ParentSpan] as const 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 > >, AEff, EOut, ROut, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< AEff, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never >, props: NoInfer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

) => Effect.Effect, ): Component } } export const make: ( & make.Gen & (( spanName: string, spanOptions?: Tracer.SpanOptions, ) => make.Gen) ) = (spanNameOrBody: Function | string, ...pipeables: any[]) => { if (typeof spanNameOrBody !== "string") { const displayName = displayNameFromBody(spanNameOrBody) return Object.setPrototypeOf({ 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({ body: Effect.fn(spanNameOrBody, spanOptions)(body, ...pipeables as []), displayName: displayNameFromBody(body) ?? spanNameOrBody, }, ComponentProto) } } export const makeUntraced: make.Gen = (body: Function, ...pipeables: any[]) => Object.setPrototypeOf({ 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 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) }) export interface Memoized

{ readonly memo: true readonly propsAreEqual?: Equivalence.Equivalence

} export const memo = >( self: ExcludeKeys>> ): T & Memoized> => Object.setPrototypeOf( { ...self, memo: true }, Object.getPrototypeOf(self), ) export const memoWithEquivalence: { >( propsAreEqual: Equivalence.Equivalence> ): ( self: ExcludeKeys>> ) => T & Memoized> >( self: ExcludeKeys>>, propsAreEqual: Equivalence.Equivalence>, ): T & Memoized> } = Function.dual(2, >( self: ExcludeKeys>>, propsAreEqual: Equivalence.Equivalence>, ): T & Memoized> => Object.setPrototypeOf( { ...self, memo: true, propsAreEqual }, Object.getPrototypeOf(self), )) export interface Suspense { readonly suspense: true } export type SuspenseProps = Omit export const suspense = , P extends {}>( self: ExcludeKeys & Component> ): T & Suspense => Object.setPrototypeOf( { ...self, suspense: true }, Object.getPrototypeOf(self), ) export const useFC: { ( self: Component & Suspense ): Effect.Effect, never, Exclude> ( self: Component ): Effect.Effect, never, Exclude> } = Effect.fn("useFC")(function* ( self: Component & (Memoized

| Suspense | {}) ) { 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

= Predicate.hasProperty(self, "suspense") ? pipe( function SuspenseInner(props: { readonly promise: Promise }) { return React.use(props.promise) }, SuspenseInner => ({ fallback, name, ...props }: P & SuspenseProps) => { const promise = Runtime.runPromise(runtimeRef.current)( Effect.provideService(self.body(props as P), Scope.Scope, scope) ) return React.createElement( React.Suspense, { fallback, 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 Predicate.hasProperty(self, "memo") ? React.memo(f, self.propsAreEqual) : f }, [scope]) return React.createElement(FC, props) }, []) }) export const use: { ( self: Component & Suspense, fn: (Component: React.FC

) => React.ReactNode, ): Effect.Effect> ( self: Component, fn: (Component: React.FC

) => React.ReactNode, ): Effect.Effect> } = Effect.fn("use")(function*(self, fn) { return fn(yield* useFC(self)) })