Compare commits

..

1 Commits

Author SHA1 Message Date
f23e54b96c Update dependency @effect/language-service to ^0.53.0
All checks were successful
Lint / lint (push) Successful in 43s
Test build / test-build (pull_request) Successful in 17s
2025-11-01 12:01:17 +00:00
11 changed files with 31 additions and 73 deletions

View File

@@ -5,7 +5,7 @@
"name": "@effect-fc/monorepo", "name": "@effect-fc/monorepo",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.2.5", "@biomejs/biome": "^2.2.5",
"@effect/language-service": "^0.54.0", "@effect/language-service": "^0.53.0",
"@types/bun": "^1.2.23", "@types/bun": "^1.2.23",
"npm-check-updates": "^19.0.0", "npm-check-updates": "^19.0.0",
"npm-sort": "^0.0.4", "npm-sort": "^0.0.4",
@@ -130,7 +130,7 @@
"@effect-fc/example": ["@effect-fc/example@workspace:packages/example"], "@effect-fc/example": ["@effect-fc/example@workspace:packages/example"],
"@effect/language-service": ["@effect/language-service@0.54.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-s9z8sbmZH1dIQzL+9imlBTt4XEbo7o+D8lAAxyp6kcjnvwYUYoebdT3KhBYpvqzIBGgiJlCMcXNzhXUTrz25VA=="], "@effect/language-service": ["@effect/language-service@0.53.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-EZQMDtDqwOrgB2z7ncOLgOJYK4E0nv6XYeEOqEH7s8W/bxT/SPaaAd+thTcMvGyiz6MqRMaUMQMUyLa4H5Jetg=="],
"@effect/platform": ["@effect/platform@0.92.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.18.1" } }, "sha512-XXWCBVwyhaKZISN7aM1fv/3fWDGyxr84ObywnUrL8aHvJLoIeskWFAP/fqw3c5MFCrJ3ZV97RWLbv6JiBQugdg=="], "@effect/platform": ["@effect/platform@0.92.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.18.1" } }, "sha512-XXWCBVwyhaKZISN7aM1fv/3fWDGyxr84ObywnUrL8aHvJLoIeskWFAP/fqw3c5MFCrJ3ZV97RWLbv6JiBQugdg=="],

View File

@@ -16,7 +16,7 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.2.5", "@biomejs/biome": "^2.2.5",
"@effect/language-service": "^0.54.0", "@effect/language-service": "^0.53.0",
"@types/bun": "^1.2.23", "@types/bun": "^1.2.23",
"npm-check-updates": "^19.0.0", "npm-check-updates": "^19.0.0",
"npm-sort": "^0.0.4", "npm-sort": "^0.0.4",

View File

@@ -490,7 +490,7 @@ export const useOnMount: {
}) })
export namespace useOnChange { export namespace useOnChange {
export interface Options extends useScope.Options {} export type Options = useScope.Options
} }
export const useOnChange: { export const useOnChange: {
@@ -578,22 +578,6 @@ export const useReactLayoutEffect: {
React.useLayoutEffect(() => runReactEffect(runtime, f, options), deps) React.useLayoutEffect(() => runReactEffect(runtime, f, options), deps)
}) })
export const useRunSync: {
<R = never>(): Effect.Effect<<A, E = never>(effect: Effect.Effect<A, E, R>) => A, never, Scope.Scope | R>
} = Effect.fnUntraced(function* <Args extends unknown[], A, E, R>(
f: (...args: Args) => Effect.Effect<A, E, R>,
deps: React.DependencyList,
) {
// biome-ignore lint/style/noNonNullAssertion: context initialization
const runtimeRef = React.useRef<Runtime.Runtime<R>>(null!)
runtimeRef.current = yield* Effect.runtime<R>()
Runtime.runSync()
// biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList
return React.useCallback((...args: Args) => Runtime.runSync(runtimeRef.current)(f(...args)), deps)
})
export const useCallbackSync: { export const useCallbackSync: {
<Args extends unknown[], A, E, R>( <Args extends unknown[], A, E, R>(
f: (...args: Args) => Effect.Effect<A, E, R>, f: (...args: Args) => Effect.Effect<A, E, R>,
@@ -629,13 +613,13 @@ export const useCallbackPromise: {
}) })
export namespace useContext { export namespace useContext {
export interface Options extends useOnChange.Options {} export type Options = useOnChange.Options
} }
export const useContext = <ROut, E, RIn>( export const useContext = <ROut, E, RIn>(
layer: Layer.Layer<ROut, E, RIn>, layer: Layer.Layer<ROut, E, RIn>,
options?: useContext.Options, options?: useContext.Options,
): Effect.Effect<Context.Context<ROut>, E, Scope.Scope | RIn> => useOnChange(() => Effect.context<RIn>().pipe( ): Effect.Effect<Context.Context<ROut>, E, RIn> => useOnChange(() => Effect.context<RIn>().pipe(
Effect.map(context => ManagedRuntime.make(Layer.provide(layer, Layer.succeedContext(context)))), Effect.map(context => ManagedRuntime.make(Layer.provide(layer, Layer.succeedContext(context)))),
Effect.tap(runtime => Effect.addFinalizer(() => runtime.disposeEffect)), Effect.tap(runtime => Effect.addFinalizer(() => runtime.disposeEffect)),
Effect.andThen(runtime => runtime.runtimeEffect), Effect.andThen(runtime => runtime.runtimeEffect),

View File

@@ -162,19 +162,6 @@ export const submit = <A, I, R, SA, SE, SR>(
Result.initial() as Result.Result<SA, SE>, Result.initial() as Result.Result<SA, SE>,
(_, result) => Effect.as(Ref.set(self.submitResultRef, result), result), (_, result) => Effect.as(Ref.set(self.submitResultRef, result), result),
), ),
Effect.tap(result => Result.isFailure(result)
? Option.match(
Chunk.findFirst(
Cause.failures(result.cause as Cause.Cause<ParseResult.ParseError>),
e => e._tag === "ParseError",
),
{
onSome: e => Ref.set(self.errorRef, Option.some(e)),
onNone: () => Effect.void,
},
)
: Effect.void
),
), ),
self.canSubmitSubscribable.get, self.canSubmitSubscribable.get,

View File

@@ -239,10 +239,7 @@ export const forkEffectDequeue = <A, E, R, P = never>(
Effect.tap(([ref, queue]) => Effect.forkScoped(State<A, E, P>().pipe( Effect.tap(([ref, queue]) => Effect.forkScoped(State<A, E, P>().pipe(
Effect.andThen(state => state.set(running(options?.initialProgress)).pipe( Effect.andThen(state => state.set(running(options?.initialProgress)).pipe(
Effect.andThen(effect), Effect.andThen(effect),
Effect.onExit(exit => Effect.andThen( Effect.onExit(exit => Effect.andThen(state.set(fromExit(exit)), Queue.shutdown(queue))),
state.set(fromExit(exit)),
Effect.forkScoped(Queue.shutdown(queue)),
)),
)), )),
Effect.provide(Layer.empty.pipe( Effect.provide(Layer.empty.pipe(
Layer.provideMerge(makeProgressLayer<A, E, P>()), Layer.provideMerge(makeProgressLayer<A, E, P>()),

View File

@@ -1,4 +1,4 @@
import { Effect, Equivalence, type Scope, Stream, Subscribable } from "effect" import { Effect, Equivalence, pipe, type Scope, Stream, Subscribable } from "effect"
import * as React from "react" import * as React from "react"
import * as Component from "./Component.js" import * as Component from "./Component.js"
@@ -16,40 +16,30 @@ export const zipLatestAll = <const T extends readonly Subscribable.Subscribable<
changes: Stream.zipLatestAll(...elements.map(v => v.changes)), changes: Stream.zipLatestAll(...elements.map(v => v.changes)),
}) as any }) as any
export namespace useSubscribables {
export type Success<T extends readonly Subscribable.Subscribable<any, any, any>[]> = [T[number]] extends [never]
? never
: { [K in keyof T]: T[K] extends Subscribable.Subscribable<infer A, infer _E, infer _R> ? A : never }
export interface Options<A> {
readonly equivalence?: Equivalence.Equivalence<A>
}
}
export const useSubscribables: { export const useSubscribables: {
<const T extends readonly Subscribable.Subscribable<any, any, any>[]>( <const T extends readonly Subscribable.Subscribable<any, any, any>[]>(
elements: T, ...elements: T
options?: useSubscribables.Options<useSubscribables.Success<NoInfer<T>>>,
): Effect.Effect< ): Effect.Effect<
useSubscribables.Success<T>, [T[number]] extends [never]
? never
: { [K in keyof T]: T[K] extends Subscribable.Subscribable<infer A, infer _E, infer _R> ? A : never },
[T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer E, infer _R> ? E : never, [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer E, infer _R> ? E : never,
Scope.Scope | ([T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer _E, infer R> ? R : never) ([T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer _E, infer R> ? R : never) | Scope.Scope
> >
} = Effect.fnUntraced(function* <const T extends readonly Subscribable.Subscribable<any, any, any>[]>( } = Effect.fnUntraced(function* <const T extends readonly Subscribable.Subscribable<any, any, any>[]>(
elements: T, ...elements: T
options?: useSubscribables.Options<useSubscribables.Success<NoInfer<T>>>,
) { ) {
const [reactStateValue, setReactStateValue] = React.useState( const [reactStateValue, setReactStateValue] = React.useState(
yield* Component.useOnMount(() => Effect.all(elements.map(v => v.get))) yield* Component.useOnMount(() => Effect.all(elements.map(v => v.get)))
) )
yield* Component.useReactEffect(() => Stream.zipLatestAll(...elements.map(ref => ref.changes)).pipe( yield* Component.useReactEffect(() => Effect.forkScoped(pipe(
Stream.changesWith((options?.equivalence as Equivalence.Equivalence<any[]> | undefined) ?? Equivalence.array(Equivalence.strict())), elements.map(ref => Stream.changesWith(ref.changes, Equivalence.strict())),
streams => Stream.zipLatestAll(...streams),
Stream.runForEach(v => Stream.runForEach(v =>
Effect.sync(() => setReactStateValue(v)) Effect.sync(() => setReactStateValue(v))
), ),
Effect.forkScoped, )), elements)
), elements)
return reactStateValue as any return reactStateValue as any
}) })

View File

@@ -28,11 +28,11 @@ export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInp
// biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported // biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported
: { optional: false, ...yield* Form.useInput(props.field, props) } : { optional: false, ...yield* Form.useInput(props.field, props) }
const [issues, isValidating, isSubmitting] = yield* Subscribable.useSubscribables([ const [issues, isValidating, isSubmitting] = yield* Subscribable.useSubscribables(
props.field.issuesSubscribable, props.field.issuesSubscribable,
props.field.isValidatingSubscribable, props.field.isValidatingSubscribable,
props.field.isSubmittingSubscribable, props.field.isSubmittingSubscribable,
]) )
return ( return (
<Flex direction="column" gap="1"> <Flex direction="column" gap="1">

View File

@@ -39,10 +39,10 @@ class RegisterForm extends Effect.Service<RegisterForm>()("RegisterForm", {
), ),
initialEncodedValue: { email: "", password: "", birth: Option.none() }, initialEncodedValue: { email: "", password: "", birth: Option.none() },
onSubmit: Effect.fnUntraced(function*(v) { onSubmit: v => Effect.sleep("500 millis").pipe(
yield* Effect.sleep("500 millis") Effect.andThen(Console.log(v)),
return v Effect.andThen(Effect.sync(() => alert("Done!"))),
}), ),
debounce: "500 millis", debounce: "500 millis",
}) })
}) {} }) {}
@@ -50,10 +50,10 @@ class RegisterForm extends Effect.Service<RegisterForm>()("RegisterForm", {
class RegisterFormView extends Component.makeUntraced("RegisterFormView")(function*() { class RegisterFormView extends Component.makeUntraced("RegisterFormView")(function*() {
const form = yield* RegisterForm const form = yield* RegisterForm
const submit = yield* Form.useSubmit(form) const submit = yield* Form.useSubmit(form)
const [canSubmit, submitResult] = yield* Subscribable.useSubscribables([ const [canSubmit, submitResult] = yield* Subscribable.useSubscribables(
form.canSubmitSubscribable, form.canSubmitSubscribable,
form.submitResultRef, form.submitResultRef,
]) )
const TextFieldFormInputFC = yield* TextFieldFormInput const TextFieldFormInputFC = yield* TextFieldFormInput
@@ -93,7 +93,7 @@ class RegisterFormView extends Component.makeUntraced("RegisterFormView")(functi
Match.tag("Initial", () => <></>), Match.tag("Initial", () => <></>),
Match.tag("Running", () => <Text>Submitting...</Text>), Match.tag("Running", () => <Text>Submitting...</Text>),
Match.tag("Success", () => <Text>Submitted successfully!</Text>), Match.tag("Success", () => <Text>Submitted successfully!</Text>),
Match.tag("Failure", e => <Text>Error: {e.cause.toString()}</Text>), Match.tag("Failure", v => <Text>Error: {v.cause.toString()}</Text>),
Match.exhaustive, Match.exhaustive,
)} )}
</Container> </Container>

View File

@@ -21,7 +21,7 @@ const ResultView = Component.makeUntraced("Result")(function*() {
Effect.tap(Effect.sleep("250 millis")), Effect.tap(Effect.sleep("250 millis")),
Result.forkEffect, Result.forkEffect,
)) ))
const [result] = yield* Subscribable.useSubscribables([resultSubscribable]) const [result] = yield* Subscribable.useSubscribables(resultSubscribable)
return ( return (
<Container> <Container>

View File

@@ -78,11 +78,11 @@ export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoPr
] as const ] as const
}), [props._tag, props._tag === "edit" ? props.id : undefined]) }), [props._tag, props._tag === "edit" ? props.id : undefined])
const [index, size, canSubmit] = yield* Subscribable.useSubscribables([ const [index, size, canSubmit] = yield* Subscribable.useSubscribables(
indexRef, indexRef,
state.sizeSubscribable, state.sizeSubscribable,
form.canSubmitSubscribable, form.canSubmitSubscribable,
]) )
const submit = yield* Form.useSubmit(form) const submit = yield* Form.useSubmit(form)
const TextFieldFormInputFC = yield* TextFieldFormInput const TextFieldFormInputFC = yield* TextFieldFormInput

View File

@@ -7,7 +7,7 @@ import { TodosState } from "./TodosState.service"
export class Todos extends Component.makeUntraced("Todos")(function*() { export class Todos extends Component.makeUntraced("Todos")(function*() {
const state = yield* TodosState const state = yield* TodosState
const [todos] = yield* Subscribable.useSubscribables([state.ref]) const [todos] = yield* Subscribable.useSubscribables(state.ref)
yield* Component.useOnMount(() => Effect.andThen( yield* Component.useOnMount(() => Effect.andThen(
Console.log("Todos mounted"), Console.log("Todos mounted"),