/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { type Context, Effect, Layer, ManagedRuntime, Predicate } from "effect" import * as React from "react" import * as Component from "./Component.js" import * as ScopeRegistry from "./ScopeRegistry.js" export const ReactRuntimeTypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime") export type ReactRuntimeTypeId = typeof ReactRuntimeTypeId export interface ReactRuntime { new(_: never): Record readonly [ReactRuntimeTypeId]: ReactRuntimeTypeId readonly runtime: ManagedRuntime.ManagedRuntime readonly context: React.Context> } const ReactRuntimePrototype = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) export const preludeLayer: Layer.Layer = ScopeRegistry.ScopeRegistry.layer export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, ReactRuntimeTypeId) export const make = ( layer: Layer.Layer, memoMap?: Layer.MemoMap, ): ReactRuntime | R, ER> => Object.setPrototypeOf( Object.assign(function() {}, { runtime: ManagedRuntime.make( Layer.merge(preludeLayer, layer), { memoMap }, ), // biome-ignore lint/style/noNonNullAssertion: context initialization context: React.createContext | R>>(null!), }), ReactRuntimePrototype, ) export namespace Provider { export interface Props extends React.SuspenseProps { readonly runtime: ReactRuntime readonly children?: React.ReactNode } } export const Provider = ( { runtime, children, ...suspenseProps }: Provider.Props ): React.ReactNode => { const promise = React.useMemo(() => runtime.runtime.context(), [runtime]) return React.createElement( React.Suspense, suspenseProps, React.createElement(ProviderInner, { runtime, promise, children }), ) } const ProviderInner = ( { runtime, promise, children }: { readonly runtime: ReactRuntime readonly promise: Promise> readonly children?: React.ReactNode } ): React.ReactNode => { const context = React.use(promise) Effect.runSyncWith(context)(Component.useOnChange( () => Effect.addFinalizer(() => runtime.runtime.disposeEffect), [runtime], )) return React.createElement(runtime.context, { value: context }, children) }