/** 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 ErrorObserver from "./ErrorObserver.js" import * as QueryClient from "./QueryClient.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 ReactRuntimeProto = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) export const preludeLayer: Layer.Layer< | Component.ScopeMap | ErrorObserver.ErrorObserver | QueryClient.QueryClient > = Layer.mergeAll( Component.ScopeMap.layer, ErrorObserver.layer, QueryClient.QueryClient.Default, ) export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, TypeId) export const make = ( layer: Layer.Layer, memoMap?: Layer.MemoMap, ): ReactRuntime | R, ER> => Object.setPrototypeOf( Object.assign(function() {}, { runtime: ManagedRuntime.make( Layer.merge(layer, preludeLayer), memoMap, ), // biome-ignore lint/style/noNonNullAssertion: context initialization context: React.createContext>(null!), }), ReactRuntimeProto, ) 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 => { Effect.runSync(Component.useOnChange( () => Effect.addFinalizer(() => runtime.runtime.disposeEffect), [runtime], )) 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 => React.createElement(runtime.context, { value: React.use(promise) }, children)