diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts
index 36205bf..9843bb8 100644
--- a/packages/effect-fc/src/Component.ts
+++ b/packages/effect-fc/src/Component.ts
@@ -1,4 +1,4 @@
-import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, String, Tracer, type Utils } from "effect"
+import { Context, Effect, Effectable, ExecutionStrategy, Function, identity, Predicate, Runtime, Scope, String, Tracer, type Types } from "effect"
import * as React from "react"
import { Hooks } from "./hooks/index.js"
import * as Memoized from "./Memoized.js"
@@ -7,20 +7,35 @@ 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, never, Exclude>, Component.Options {
+export interface Component
+extends
+ Effect.Effect>,
+ Component.Options
+{
new(_: never): {}
readonly [TypeId]: TypeId
+ readonly ["~FunctionComponent"]: F
+ readonly ["~Props"]: FunctionComponent.Props
+ readonly ["~Success"]: FunctionComponent.Success
+ readonly ["~Error"]: E
+ readonly ["~Context"]: R
+
/** @internal */
- makeFunctionComponent(runtimeRef: React.Ref>>, scope: Scope.Scope): React.FC
+ readonly body: (props: FunctionComponent.Props) => Effect.Effect, E, R>
+
/** @internal */
- readonly body: (props: P) => Effect.Effect
+ makeFunctionComponent(
+ runtimeRef: React.Ref>>,
+ scope: Scope.Scope,
+ ): F
}
export namespace Component {
- export type Props = T extends Component ? P : never
- export type Error = T extends Component ? E : never
- export type Context = T extends Component ? R : never
+ export type FunctionComponent> = T extends Component ? F : never
+ export type Error> = T extends Component ? E : never
+ export type Context> = T extends Component ? R : never
+
+ export type AsComponent> = Component, Error, Context>
export interface Options {
readonly displayName?: string
@@ -29,17 +44,26 @@ export namespace Component {
}
}
+export type FunctionComponent = (...args: readonly any[]) => React.ReactNode
+
+export namespace FunctionComponent {
+ export type Props = T extends (...args: readonly [infer P, ...any[]]) => infer _A ? P : never
+ export type Success = T extends (...args: infer _Args) => infer A ? A : never
+}
+
const ComponentProto = Object.freeze({
...Effectable.CommitPrototype,
[TypeId]: TypeId,
- commit: Effect.fnUntraced(function* (this: Component
) {
+ 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) {
+ return React.useCallback(function ScopeProvider(props: FunctionComponent.Props) {
const scope = Runtime.runSync(runtimeRef.current)(Hooks.useScope(
Array.from(
Context.omit(...nonReactiveTags)(runtimeRef.current.context).unsafeMap.values()
@@ -48,7 +72,7 @@ const ComponentProto = Object.freeze({
))
const FC = React.useMemo(() => {
- const f = self.makeFunctionComponent(runtimeRef, scope)
+ const f: React.FC> = self.makeFunctionComponent(runtimeRef, scope)
f.displayName = self.displayName ?? "Anonymous"
return Memoized.isMemoized(self)
? React.memo(f, self.propsAreEqual)
@@ -59,12 +83,12 @@ const ComponentProto = Object.freeze({
}, [])
}),
- makeFunctionComponent (
- this: Component
,
+ makeFunctionComponent(
+ this: Component,
runtimeRef: React.RefObject>>,
scope: Scope.Scope,
- ): React.FC {
- return (props: P) => Runtime.runSync(runtimeRef.current)(
+ ) {
+ return (props: FunctionComponent.Props) => Runtime.runSync(runtimeRef.current)(
Effect.provideService(this.body(props), Scope.Scope, scope)
)
},
@@ -78,277 +102,23 @@ const defaultOptions = {
const nonReactiveTags = [Tracer.ParentSpan] as const
-export const isComponent = (u: unknown): u is Component<{}, unknown, unknown> => Predicate.hasProperty(u, TypeId)
+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<
- P,
- [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 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>
- >, 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(
+export const make = (
+ body: (...args: Args) => Effect.Effect
+): Component<(...args: Args) => A, E, R> => Object.setPrototypeOf(
Object.assign(function() {}, defaultOptions, {
- body: Effect.fnUntraced(body as any, ...pipeables as []),
- displayName: displayNameFromBody(body),
+ body,
+ displayName: !String.isEmpty(body.name) ? body.name : undefined,
}),
ComponentProto,
)
-const displayNameFromBody = (body: Function) => !String.isEmpty(body.name) ? body.name : undefined
+export const withFunctionComponentSignature: {
+ (): >(self: T) =>
+ & Omit>
+ & Component, Component.Context>
+} = () => identity
export const withOptions: {
>(
@@ -367,17 +137,17 @@ export const withOptions: {
))
export const withRuntime: {
- (
+ (
context: React.Context>,
- ): (self: Component) => React.FC
-
(
- self: Component
,
+ ): (self: Component>) => F
+ (
+ self: Component>,
context: React.Context>,
- ): React.FC
-} = Function.dual(2,
(
- self: Component
,
+ ): F
+} = Function.dual(2, (
+ self: Component,
context: React.Context>,
-): React.FC => function WithRuntime(props) {
+) => function WithRuntime(props: FunctionComponent.Props) {
return React.createElement(
Runtime.runSync(React.useContext(context))(self),
props,