0.1.4 #5

Merged
Thilawyn merged 67 commits from next into master 2025-10-02 18:18:23 +02:00
10 changed files with 37 additions and 33 deletions
Showing only changes of commit 8c1fed7800 - Show all commits

View File

@@ -1,4 +1,4 @@
import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, String, Tracer, type Types, type Utils } from "effect" import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, Tracer, type Types, type Utils } from "effect"
import * as React from "react" import * as React from "react"
import { Hooks } from "./hooks/index.js" import { Hooks } from "./hooks/index.js"
import * as Memo from "./Memo.js" import * as Memo from "./Memo.js"
@@ -331,13 +331,9 @@ export const make: (
) => make.Gen & make.NonGen) ) => make.Gen & make.NonGen)
) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => { ) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => {
if (typeof spanNameOrBody !== "string") { if (typeof spanNameOrBody !== "string") {
const displayName = displayNameFromBody(spanNameOrBody)
return Object.setPrototypeOf( return Object.setPrototypeOf(
Object.assign(function() {}, defaultOptions, { Object.assign(function() {}, defaultOptions, {
body: displayName body: Effect.fn(spanNameOrBody as any, ...pipeables),
? Effect.fn(displayName)(spanNameOrBody as any, ...pipeables as [])
: Effect.fn(spanNameOrBody as any, ...pipeables),
displayName,
}), }),
ComponentProto, ComponentProto,
) )
@@ -347,26 +343,34 @@ export const make: (
return (body: any, ...pipeables: any[]) => Object.setPrototypeOf( return (body: any, ...pipeables: any[]) => Object.setPrototypeOf(
Object.assign(function() {}, defaultOptions, { Object.assign(function() {}, defaultOptions, {
body: Effect.fn(spanNameOrBody, spanOptions)(body, ...pipeables as []), body: Effect.fn(spanNameOrBody, spanOptions)(body, ...pipeables as []),
displayName: displayNameFromBody(body) ?? spanNameOrBody, displayName: spanNameOrBody,
}), }),
ComponentProto, ComponentProto,
) )
} }
} }
export const makeUntraced: make.Gen & make.NonGen = ( export const makeUntraced: (
body: Function, & make.Gen
...pipeables: any[] & make.NonGen
) => Object.setPrototypeOf( & ((name: string) => make.Gen & make.NonGen)
Object.assign(function() {}, defaultOptions, { ) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => (
body: Effect.fnUntraced(body as any, ...pipeables as []), typeof spanNameOrBody !== "string"
displayName: displayNameFromBody(body), ? Object.setPrototypeOf(
}), Object.assign(function() {}, defaultOptions, {
ComponentProto, body: Effect.fnUntraced(spanNameOrBody as any, ...pipeables as []),
}),
ComponentProto,
)
: (body: any, ...pipeables: any[]) => Object.setPrototypeOf(
Object.assign(function() {}, defaultOptions, {
body: Effect.fnUntraced(body, ...pipeables as []),
displayName: spanNameOrBody,
}),
ComponentProto,
)
) )
const displayNameFromBody = (body: Function) => !String.isEmpty(body.name) ? body.name : undefined
export const withOptions: { export const withOptions: {
<T extends Component<any, any, any, any>>( <T extends Component<any, any, any, any>>(
options: Partial<Component.Options> options: Partial<Component.Options>

View File

@@ -15,7 +15,7 @@ export const TextAreaInput = <A, R>(options: {
React.JSX.Element, React.JSX.Element,
ParseResult.ParseError, ParseResult.ParseError,
R R
> => Component.makeUntraced(function* TextFieldInput(props) { > => Component.makeUntraced("TextFieldInput")(function* TextFieldInput(props) {
const input = yield* useInput({ ...options, ...props }) const input = yield* useInput({ ...options, ...props })
const issue = React.useMemo(() => input.error.pipe( const issue = React.useMemo(() => input.error.pipe(
Option.map(ParseResult.ArrayFormatter.formatErrorSync), Option.map(ParseResult.ArrayFormatter.formatErrorSync),

View File

@@ -18,7 +18,7 @@ export const TextFieldInput = <A, R, O extends boolean = false>(options: {
readonly optional?: O readonly optional?: O
readonly schema: Schema.Schema<A, string, R> readonly schema: Schema.Schema<A, string, R>
readonly equivalence?: Equivalence.Equivalence<A> readonly equivalence?: Equivalence.Equivalence<A>
}) => Component.makeUntraced(function* TextFieldInput(props: O extends true }) => Component.makeUntraced("TextFieldInput")(function* TextFieldInput(props: O extends true
? TextFieldOptionalInputProps<A, R> ? TextFieldOptionalInputProps<A, R>
: TextFieldInputProps<A, R> : TextFieldInputProps<A, R>
) { ) {

View File

@@ -51,7 +51,7 @@ const RouteComponent = Component.makeUntraced(function* AsyncRendering() {
// ) // )
class AsyncComponent extends Component.makeUntraced(function* AsyncComponent() { class AsyncComponent extends Component.makeUntraced("AsyncComponent")(function*() {
const SubComponentFC = yield* SubComponent const SubComponentFC = yield* SubComponent
yield* Effect.sleep("500 millis") // Async operation yield* Effect.sleep("500 millis") // Async operation
@@ -69,7 +69,7 @@ class AsyncComponent extends Component.makeUntraced(function* AsyncComponent() {
) {} ) {}
class MemoizedAsyncComponent extends Memo.memo(AsyncComponent) {} class MemoizedAsyncComponent extends Memo.memo(AsyncComponent) {}
class SubComponent extends Component.makeUntraced(function* SubComponent() { class SubComponent extends Component.makeUntraced("SubComponent")(function*() {
const [state] = React.useState(yield* Hooks.useOnce(() => Effect.provide(makeUuid4, GetRandomValues.CryptoRandom))) const [state] = React.useState(yield* Hooks.useOnce(() => Effect.provide(makeUuid4, GetRandomValues.CryptoRandom)))
return <Text>{state}</Text> return <Text>{state}</Text>
}) {} }) {}

View File

@@ -4,7 +4,7 @@ import { Container } from "@radix-ui/themes"
import { createFileRoute } from "@tanstack/react-router" import { createFileRoute } from "@tanstack/react-router"
import { Schema, SubscriptionRef } from "effect" import { Schema, SubscriptionRef } from "effect"
import { Component, Memo } from "effect-fc" import { Component, Memo } from "effect-fc"
import { useInput, useOnce, useRefState } from "effect-fc/hooks" import { useOnce } from "effect-fc/hooks"
const IntFromString = Schema.NumberFromString.pipe(Schema.int()) const IntFromString = Schema.NumberFromString.pipe(Schema.int())
@@ -12,18 +12,18 @@ const IntFromString = Schema.NumberFromString.pipe(Schema.int())
const IntTextFieldInput = TextFieldInput({ schema: IntFromString }) const IntTextFieldInput = TextFieldInput({ schema: IntFromString })
const StringTextFieldInput = TextFieldInput({ schema: Schema.String }) const StringTextFieldInput = TextFieldInput({ schema: Schema.String })
const Input = Component.makeUntraced(function* Input() { const Input = Component.makeUntraced("Input")(function*() {
const IntTextFieldInputFC = yield* IntTextFieldInput const IntTextFieldInputFC = yield* IntTextFieldInput
const StringTextFieldInputFC = yield* StringTextFieldInput const StringTextFieldInputFC = yield* StringTextFieldInput
const intRef1 = yield* useOnce(() => SubscriptionRef.make(0)) const intRef1 = yield* useOnce(() => SubscriptionRef.make(0))
const intRef2 = yield* useOnce(() => SubscriptionRef.make(0)) // const intRef2 = yield* useOnce(() => SubscriptionRef.make(0))
const stringRef = yield* useOnce(() => SubscriptionRef.make("")) const stringRef = yield* useOnce(() => SubscriptionRef.make(""))
// yield* useFork(() => Stream.runForEach(intRef1.changes, Console.log), [intRef1]) // yield* useFork(() => Stream.runForEach(intRef1.changes, Console.log), [intRef1])
const input2 = yield* useInput({ schema: IntFromString, ref: intRef2 }) // const input2 = yield* useInput({ schema: IntFromString, ref: intRef2 })
const [str, setStr] = yield* useRefState(stringRef) // const [str, setStr] = yield* useRefState(stringRef)
return ( return (
<Container> <Container>

View File

@@ -7,7 +7,7 @@ import { Component, Memo } from "effect-fc"
import * as React from "react" import * as React from "react"
const RouteComponent = Component.makeUntraced(function* RouteComponent() { const RouteComponent = Component.makeUntraced("RouteComponent")(function*() {
const [value, setValue] = React.useState("") const [value, setValue] = React.useState("")
return ( return (
@@ -25,7 +25,7 @@ const RouteComponent = Component.makeUntraced(function* RouteComponent() {
Component.withRuntime(runtime.context) Component.withRuntime(runtime.context)
) )
class SubComponent extends Component.makeUntraced(function* SubComponent() { class SubComponent extends Component.makeUntraced("SubComponent")(function*() {
const id = yield* makeUuid4.pipe(Effect.provide(GetRandomValues.CryptoRandom)) const id = yield* makeUuid4.pipe(Effect.provide(GetRandomValues.CryptoRandom))
return <Text>{id}</Text> return <Text>{id}</Text>
}) {} }) {}

View File

@@ -28,7 +28,7 @@ class RegisterForm extends Effect.Service<RegisterForm>()("RegisterForm", {
}) })
}) {} }) {}
class RegisterPage extends Component.makeUntraced(function* RegisterPage() { class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() {
const form = yield* RegisterForm const form = yield* RegisterForm
const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" }) const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" })
const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" }) const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" })

View File

@@ -9,7 +9,7 @@ import { useContext } from "effect-fc/hooks"
const TodosStateLive = TodosState.Default("todos") const TodosStateLive = TodosState.Default("todos")
const Index = Component.makeUntraced(function* Index() { const Index = Component.makeUntraced("Index")(function*() {
const context = yield* useContext(TodosStateLive, { finalizerExecutionMode: "fork" }) const context = yield* useContext(TodosStateLive, { finalizerExecutionMode: "fork" })
const TodosFC = yield* Effect.provide(Todos, context) const TodosFC = yield* Effect.provide(Todos, context)

View File

@@ -31,7 +31,7 @@ export type TodoProps = (
| { readonly _tag: "edit", readonly id: string } | { readonly _tag: "edit", readonly id: string }
) )
export class Todo extends Component.makeUntraced(function* Todo(props: TodoProps) { export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoProps) {
const runtime = yield* Effect.runtime() const runtime = yield* Effect.runtime()
const state = yield* TodosState const state = yield* TodosState

View File

@@ -6,7 +6,7 @@ import { Todo } from "./Todo"
import { TodosState } from "./TodosState.service" import { TodosState } from "./TodosState.service"
export class Todos extends Component.makeUntraced(function* Todos() { export class Todos extends Component.makeUntraced("Todos")(function*() {
const state = yield* TodosState const state = yield* TodosState
const [todos] = yield* useSubscribe(state.ref) const [todos] = yield* useSubscribe(state.ref)