From 636beedd13e9e9f0704c6167999c0ed21b6ef663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 18 Jan 2026 14:31:38 +0100 Subject: [PATCH 01/12] Query work --- packages/effect-fc/src/Query.ts | 16 ++++++++++++---- packages/effect-fc/src/QueryClient.ts | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/effect-fc/src/Query.ts b/packages/effect-fc/src/Query.ts index 9aea2d7..70b2765 100644 --- a/packages/effect-fc/src/Query.ts +++ b/packages/effect-fc/src/Query.ts @@ -225,9 +225,17 @@ extends Pipeable.Class() implements Query { getCacheEntry( key: K ): Effect.Effect, never, QueryClient.QueryClient> { - return QueryClient.QueryClient.pipe( - Effect.andThen(client => client.cache), - Effect.map(HashMap.get(this.makeCacheKey(key))), + return Effect.all([ + Effect.succeed(this.makeCacheKey(key)), + Effect.map(QueryClient.QueryClient, client => client.cache), + DateTime.now, + ]).pipe( + Effect.andThen(([key, ref, now]) => ref.pipe( + Effect.andThen(HashMap.get(key)), + Effect.map(entry => new QueryClient.QueryClientCacheEntry(entry.result, entry.createdAt, now)), + Effect.tap(entry => SubscriptionRef.update(ref, HashMap.set(key, entry))), + )), + Effect.option, ) } @@ -238,7 +246,7 @@ extends Pipeable.Class() implements Query { return Effect.Do.pipe( Effect.bind("client", () => QueryClient.QueryClient), Effect.bind("now", () => DateTime.now), - Effect.let("entry", ({ now }) => new QueryClient.QueryClientCacheEntry(result, now)), + Effect.let("entry", ({ now }) => new QueryClient.QueryClientCacheEntry(result, now, now)), Effect.tap(({ client, entry }) => SubscriptionRef.update( client.cache, HashMap.set(this.makeCacheKey(key), entry), diff --git a/packages/effect-fc/src/QueryClient.ts b/packages/effect-fc/src/QueryClient.ts index 19517b7..4dcfb0d 100644 --- a/packages/effect-fc/src/QueryClient.ts +++ b/packages/effect-fc/src/QueryClient.ts @@ -103,6 +103,7 @@ implements Pipeable.Pipeable { constructor( readonly result: Result.Success, readonly createdAt: DateTime.DateTime, + readonly lastAccessedAt: DateTime.DateTime, ) { super() } -- 2.49.1 From bbacee7ad41bb44ee196d51953a02573870695b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 18 Jan 2026 16:08:39 +0100 Subject: [PATCH 02/12] Refactor Query --- packages/effect-fc/src/Query.ts | 57 ++++++++------------ packages/effect-fc/src/QueryClient.ts | 77 ++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 46 deletions(-) diff --git a/packages/effect-fc/src/Query.ts b/packages/effect-fc/src/Query.ts index 70b2765..9c306b7 100644 --- a/packages/effect-fc/src/Query.ts +++ b/packages/effect-fc/src/Query.ts @@ -1,4 +1,4 @@ -import { type Cause, type Context, DateTime, type Duration, Effect, Equal, Equivalence, Fiber, HashMap, identity, Option, Pipeable, Predicate, type Scope, Stream, Subscribable, SubscriptionRef } from "effect" +import { type Cause, type Context, type Duration, Effect, Equal, Fiber, identity, Option, Pipeable, Predicate, type Scope, Stream, Subscribable, SubscriptionRef } from "effect" import * as QueryClient from "./QueryClient.js" import * as Result from "./Result.js" @@ -80,7 +80,7 @@ extends Pipeable.Class() implements Query { ) } - get interrupt(): Effect.Effect { + get interrupt(): Effect.Effect { return Effect.andThen(this.fiber, Option.match({ onSome: Fiber.interrupt, onNone: () => Effect.void, @@ -159,7 +159,7 @@ extends Pipeable.Class() implements Query { > { return Effect.andThen(this.getCacheEntry(key), Option.match({ onSome: entry => Effect.andThen( - QueryClient.isQueryClientCacheEntryStale(entry, this.staleTime), + QueryClient.isQueryClientCacheEntryStale(entry), isStale => isStale ? this.start(key, Result.willRefresh(entry.result) as Result.Final) : Effect.succeed(Subscribable.make({ @@ -212,7 +212,7 @@ extends Pipeable.Class() implements Query { ) as Effect.Effect>), Effect.tap(result => SubscriptionRef.set(this.latestFinalResult, Option.some(result))), Effect.tap(result => Result.isSuccess(result) - ? this.updateCacheEntry(key, result) + ? this.setCacheEntry(key, result) : Effect.void ), ) @@ -225,52 +225,41 @@ extends Pipeable.Class() implements Query { getCacheEntry( key: K ): Effect.Effect, never, QueryClient.QueryClient> { - return Effect.all([ - Effect.succeed(this.makeCacheKey(key)), - Effect.map(QueryClient.QueryClient, client => client.cache), - DateTime.now, - ]).pipe( - Effect.andThen(([key, ref, now]) => ref.pipe( - Effect.andThen(HashMap.get(key)), - Effect.map(entry => new QueryClient.QueryClientCacheEntry(entry.result, entry.createdAt, now)), - Effect.tap(entry => SubscriptionRef.update(ref, HashMap.set(key, entry))), - )), - Effect.option, + return Effect.andThen( + Effect.all([ + Effect.succeed(this.makeCacheKey(key)), + QueryClient.QueryClient, + ]), + ([key, client]) => client.getCacheEntry(key), ) } - updateCacheEntry( + setCacheEntry( key: K, result: Result.Success, ): Effect.Effect { - return Effect.Do.pipe( - Effect.bind("client", () => QueryClient.QueryClient), - Effect.bind("now", () => DateTime.now), - Effect.let("entry", ({ now }) => new QueryClient.QueryClientCacheEntry(result, now, now)), - Effect.tap(({ client, entry }) => SubscriptionRef.update( - client.cache, - HashMap.set(this.makeCacheKey(key), entry), - )), - Effect.map(({ entry }) => entry), + return Effect.andThen( + Effect.all([ + Effect.succeed(this.makeCacheKey(key)), + QueryClient.QueryClient, + ]), + ([key, client]) => client.setCacheEntry(key, result, this.staleTime), ) } get invalidateCache(): Effect.Effect { return QueryClient.QueryClient.pipe( - Effect.andThen(client => SubscriptionRef.update( - client.cache, - HashMap.filter((_, key) => !Equivalence.strict()(key.f, this.f)), - )), + Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect)), Effect.provide(this.context), ) } invalidateCacheEntry(key: K): Effect.Effect { - return QueryClient.QueryClient.pipe( - Effect.andThen(client => SubscriptionRef.update( - client.cache, - HashMap.remove(this.makeCacheKey(key)), - )), + return Effect.all([ + Effect.succeed(this.makeCacheKey(key)), + QueryClient.QueryClient, + ]).pipe( + Effect.andThen(([key, client]) => client.invalidateCacheEntry(key)), Effect.provide(this.context), ) } diff --git a/packages/effect-fc/src/QueryClient.ts b/packages/effect-fc/src/QueryClient.ts index 4dcfb0d..f501316 100644 --- a/packages/effect-fc/src/QueryClient.ts +++ b/packages/effect-fc/src/QueryClient.ts @@ -1,4 +1,4 @@ -import { DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, Pipeable, Predicate, type Scope, SubscriptionRef } from "effect" +import { DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, type Option, Pipeable, Predicate, Schedule, type Scope, type Subscribable, SubscriptionRef } from "effect" import type * as Query from "./Query.js" import type * as Result from "./Result.js" @@ -8,10 +8,21 @@ export type QueryClientServiceTypeId = typeof QueryClientServiceTypeId export interface QueryClientService extends Pipeable.Pipeable { readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId - readonly cache: SubscriptionRef.SubscriptionRef> - readonly gcTime: Duration.DurationInput + + readonly cache: Subscribable.Subscribable> + readonly cacheGcTime: Duration.DurationInput readonly defaultStaleTime: Duration.DurationInput readonly defaultRefreshOnWindowFocus: boolean + + readonly run: Effect.Effect + getCacheEntry(key: QueryClientCacheKey): Effect.Effect> + setCacheEntry( + key: QueryClientCacheKey, + result: Result.Success, + staleTime: Duration.DurationInput, + ): Effect.Effect + invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect + invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect } export class QueryClient extends Effect.Service()("@effect-fc/QueryClient/QueryClient", { @@ -25,20 +36,64 @@ implements QueryClientService { constructor( readonly cache: SubscriptionRef.SubscriptionRef>, - readonly gcTime: Duration.DurationInput, + readonly cacheGcTime: Duration.DurationInput, readonly defaultStaleTime: Duration.DurationInput, readonly defaultRefreshOnWindowFocus: boolean, readonly runSemaphore: Effect.Semaphore, ) { super() } + + get run(): Effect.Effect { + return Effect.repeat( + Effect.andThen( + DateTime.now, + now => SubscriptionRef.update(this.cache, HashMap.filter(entry => + Duration.greaterThanOrEqualTo( + DateTime.distanceDuration(entry.lastAccessedAt, now), + Duration.sum(entry.staleTime, this.cacheGcTime), + ) + )), + ), + Schedule.spaced("1 second"), + ) + } + + getCacheEntry(key: QueryClientCacheKey): Effect.Effect> { + return Effect.all([ + Effect.andThen(this.cache, HashMap.get(key)), + DateTime.now, + ]).pipe( + Effect.map(([entry, now]) => new QueryClientCacheEntry(entry.result, entry.staleTime, entry.createdAt, now)), + Effect.tap(entry => SubscriptionRef.update(this.cache, HashMap.set(key, entry))), + Effect.option, + ) + } + + setCacheEntry( + key: QueryClientCacheKey, + result: Result.Success, + staleTime: Duration.DurationInput, + ): Effect.Effect { + return DateTime.now.pipe( + Effect.map(now => new QueryClientCacheEntry(result, staleTime, now, now)), + Effect.tap(entry => SubscriptionRef.update(HashMap.set(key, entry))), + ) + } + + invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect { + return SubscriptionRef.update(this.cache, HashMap.filter((_, key) => !Equivalence.strict()(key.f, f))) + } + invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect { + return SubscriptionRef.update(this.cache, HashMap.remove(key)) + } } export const isQueryClientService = (u: unknown): u is QueryClientService => Predicate.hasProperty(u, QueryClientServiceTypeId) export declare namespace make { export interface Options { - readonly gcTime?: Duration.DurationInput + readonly cacheGcTime?: Duration.DurationInput readonly defaultStaleTime?: Duration.DurationInput readonly defaultRefreshOnWindowFocus?: boolean } @@ -47,22 +102,20 @@ export declare namespace make { export const make = Effect.fnUntraced(function* (options: make.Options = {}): Effect.fn.Return { return new QueryClientServiceImpl( yield* SubscriptionRef.make(HashMap.empty()), - options.gcTime ?? "5 minutes", + options.cacheGcTime ?? "5 minutes", options.defaultStaleTime ?? "0 minutes", options.defaultRefreshOnWindowFocus ?? true, yield* Effect.makeSemaphore(1), ) }) -export const run = (_self: QueryClientService): Effect.Effect => Effect.void - export declare namespace service { export interface Options extends make.Options {} } export const service = (options?: service.Options): Effect.Effect => Effect.tap( make(options), - client => Effect.forkScoped(run(client)), + client => Effect.forkScoped(client.run), ) @@ -102,6 +155,7 @@ implements Pipeable.Pipeable { constructor( readonly result: Result.Success, + readonly staleTime: Duration.DurationInput, readonly createdAt: DateTime.DateTime, readonly lastAccessedAt: DateTime.DateTime, ) { @@ -112,9 +166,8 @@ implements Pipeable.Pipeable { export const isQueryClientCacheEntry = (u: unknown): u is QueryClientCacheEntry => Predicate.hasProperty(u, QueryClientCacheEntryTypeId) export const isQueryClientCacheEntryStale = ( - self: QueryClientCacheEntry, - staleTime: Duration.DurationInput, + self: QueryClientCacheEntry ): Effect.Effect => Effect.andThen( DateTime.now, - now => Duration.greaterThanOrEqualTo(DateTime.distanceDuration(self.createdAt, now), staleTime), + now => Duration.greaterThanOrEqualTo(DateTime.distanceDuration(self.createdAt, now), self.staleTime), ) -- 2.49.1 From f88daeefd42e69360240ebb4ef615754a9883a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 18 Jan 2026 16:10:53 +0100 Subject: [PATCH 03/12] Fix --- packages/effect-fc/src/QueryClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/effect-fc/src/QueryClient.ts b/packages/effect-fc/src/QueryClient.ts index f501316..c48856c 100644 --- a/packages/effect-fc/src/QueryClient.ts +++ b/packages/effect-fc/src/QueryClient.ts @@ -49,13 +49,13 @@ implements QueryClientService { Effect.andThen( DateTime.now, now => SubscriptionRef.update(this.cache, HashMap.filter(entry => - Duration.greaterThanOrEqualTo( + Duration.lessThan( DateTime.distanceDuration(entry.lastAccessedAt, now), Duration.sum(entry.staleTime, this.cacheGcTime), ) )), ), - Schedule.spaced("1 second"), + Schedule.spaced("30 second"), ) } -- 2.49.1 From 1779eebe3bc6ee4d8672e08d73b1879c5b40d788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 18 Jan 2026 17:07:06 +0100 Subject: [PATCH 04/12] Fix --- packages/effect-fc/src/QueryClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/effect-fc/src/QueryClient.ts b/packages/effect-fc/src/QueryClient.ts index c48856c..6f010f2 100644 --- a/packages/effect-fc/src/QueryClient.ts +++ b/packages/effect-fc/src/QueryClient.ts @@ -45,7 +45,7 @@ implements QueryClientService { } get run(): Effect.Effect { - return Effect.repeat( + return this.runSemaphore.withPermits(1)(Effect.repeat( Effect.andThen( DateTime.now, now => SubscriptionRef.update(this.cache, HashMap.filter(entry => @@ -56,7 +56,7 @@ implements QueryClientService { )), ), Schedule.spaced("30 second"), - ) + )) } getCacheEntry(key: QueryClientCacheKey): Effect.Effect> { -- 2.49.1 From c9dd4e6aa93fb8aa716696bce642e8817e3c1cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 18 Jan 2026 17:10:56 +0100 Subject: [PATCH 05/12] Fix --- packages/effect-fc/src/QueryClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/effect-fc/src/QueryClient.ts b/packages/effect-fc/src/QueryClient.ts index 6f010f2..deb8d1d 100644 --- a/packages/effect-fc/src/QueryClient.ts +++ b/packages/effect-fc/src/QueryClient.ts @@ -3,7 +3,7 @@ import type * as Query from "./Query.js" import type * as Result from "./Result.js" -export const QueryClientServiceTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientServiceTypeId") +export const QueryClientServiceTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientService") export type QueryClientServiceTypeId = typeof QueryClientServiceTypeId export interface QueryClientService extends Pipeable.Pipeable { -- 2.49.1 From 929e062d0c2310e67cf37ef981050cba436c8a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 18 Jan 2026 17:26:07 +0100 Subject: [PATCH 06/12] Fix --- packages/effect-fc/src/QueryClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/effect-fc/src/QueryClient.ts b/packages/effect-fc/src/QueryClient.ts index deb8d1d..fde7005 100644 --- a/packages/effect-fc/src/QueryClient.ts +++ b/packages/effect-fc/src/QueryClient.ts @@ -77,7 +77,7 @@ implements QueryClientService { ): Effect.Effect { return DateTime.now.pipe( Effect.map(now => new QueryClientCacheEntry(result, staleTime, now, now)), - Effect.tap(entry => SubscriptionRef.update(HashMap.set(key, entry))), + Effect.tap(entry => SubscriptionRef.update(this.cache, HashMap.set(key, entry))), ) } -- 2.49.1 From 49c79295d6cc1db3851b6a34956a0206fc0d7794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 18 Jan 2026 19:01:00 +0100 Subject: [PATCH 07/12] Add doc comments --- packages/effect-fc/src/Component.ts | 68 ++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index d773fc3..ee0d0a5 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -8,6 +8,13 @@ import { Memoized } from "./index.js" export const TypeId: unique symbol = Symbol.for("@effect-fc/Component/Component") export type TypeId = typeof TypeId +/** + * Interface representing an Effect-based React Component. + * + * This is both: + * - an Effect that produces a React function component + * - a constructor-like object with component metadata and options + */ export interface Component

extends Effect.Effect<(props: P) => A, never, Exclude>, @@ -20,7 +27,6 @@ extends readonly "~Error": E readonly "~Context": R - /** @internal */ readonly body: (props: P) => Effect.Effect /** @internal */ @@ -37,9 +43,22 @@ export declare namespace Component { export type AsComponent> = Component, Success, Error, Context> + /** + * Options that can be set on the component + */ export interface Options { + /** Custom displayName for React DevTools and debugging */ readonly displayName?: string + /** + * Strategy used when executing finalizers on unmount/scope close + * @default ExecutionStrategy.sequential + */ readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy + /** + * Debounce time before executing finalizers after component unmount + * Helps avoid unnecessary work during fast remount/remount cycles + * @default "100 millis" + */ readonly finalizerExecutionDebounce: Duration.DurationInput } } @@ -318,6 +337,11 @@ export declare namespace make { } } +/** + * Creates an Effect-based React component. + * + * Follows the `Effect.fn` API. Supports both generator syntax (recommended) and direct Effect composition. + */ export const make: ( & make.Gen & make.NonGen @@ -346,6 +370,12 @@ export const make: ( } } +/** + * Same as `make` but creates an untraced version (no automatic span created). + * Useful for very low-level utilities or when you want full control over tracing. + * + * Follows the `Effect.fnUntraced` API. + */ export const makeUntraced: ( & make.Gen & make.NonGen @@ -367,6 +397,9 @@ export const makeUntraced: ( ) ) +/** + * Creates a new component with modified options while preserving original behavior. + */ export const withOptions: { >( options: Partial @@ -383,6 +416,39 @@ export const withOptions: { Object.getPrototypeOf(self), )) +/** + * Wraps an Effect-FC `Component` and turns it into a regular React function component + * that serves as an **entrypoint** into an Effect-FC component hierarchy. + * + * This is the recommended way to connect Effect-FC components to the rest of your React app, + * especially when using routers (TanStack Router, React Router, etc.), lazy-loaded routes, + * or any place where a standard React component is expected. + * + * The runtime is obtained from the provided React Context, allowing you to: + * - Provide dependencies once at a high level + * - Use the same runtime across an entire route tree or feature + * + * @example Using TanStack Router + * ```tsx + * // Main + * export const runtime = ReactRuntime.make(Layer.empty) + * function App() { + * return ( + * + * + * + * ) + * } + * + * // Route + * export const Route = createFileRoute("/")({ + * component: withRuntime(HomePage, runtime.context), + * }) + * ``` + * + * @param self - The Effect-FC Component you want to render as a regular React component. + * @param context - React Context that holds the Runtime to use for this component tree. See the `ReactRuntime` module to create one. + */ export const withRuntime: {

( context: React.Context>, -- 2.49.1 From 3be9d94aa8e251ba103c35f04da0afa56cb4770b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 22 Jan 2026 02:28:18 +0100 Subject: [PATCH 08/12] Add Component doc --- packages/effect-fc/src/Component.ts | 86 +++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index ee0d0a5..284f09d 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -47,16 +47,18 @@ export declare namespace Component { * Options that can be set on the component */ export interface Options { - /** Custom displayName for React DevTools and debugging */ + /** Custom displayName for React DevTools and debugging. */ readonly displayName?: string + /** - * Strategy used when executing finalizers on unmount/scope close + * Strategy used when executing finalizers on unmount/scope close. * @default ExecutionStrategy.sequential */ readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy + /** - * Debounce time before executing finalizers after component unmount - * Helps avoid unnecessary work during fast remount/remount cycles + * Debounce time before executing finalizers after component unmount. + * Helps avoid unnecessary work during fast remount/remount cycles. * @default "100 millis" */ readonly finalizerExecutionDebounce: Duration.DurationInput @@ -338,9 +340,17 @@ export declare namespace make { } /** - * Creates an Effect-based React component. + * Creates an Effect-FC Component following the same overloads and pipeline style as `Effect.fn`. * - * Follows the `Effect.fn` API. Supports both generator syntax (recommended) and direct Effect composition. + * This is the **recommended** way to define components. It supports: + * - Generator syntax (yield* style) — most ergonomic and readable + * - Direct Effect return (non-generator) + * - Chained transformation functions (like Effect.fn pipelines) + * - Optional tracing span with automatic `displayName` + * + * When you provide a `spanName` as the first argument, two things happen automatically: + * 1. A tracing span is created with that name (unless using `makeUntraced`) + * 2. The resulting React component gets `displayName = spanName` */ export const make: ( & make.Gen @@ -371,10 +381,15 @@ export const make: ( } /** - * Same as `make` but creates an untraced version (no automatic span created). - * Useful for very low-level utilities or when you want full control over tracing. + * Same as `make`, but creates an **untraced** version — no automatic tracing span is created. * - * Follows the `Effect.fnUntraced` API. + * Follows the exact same API shape as `Effect.fnUntraced`. + * Useful for: + * - Components where you want full manual control over tracing + * - Avoiding span noise in deeply nested UI + * + * When a string is provided as first argument, it is **only** used as the React component's `displayName` + * (no tracing span is created). */ export const makeUntraced: ( & make.Gen @@ -442,7 +457,7 @@ export const withOptions: { * * // Route * export const Route = createFileRoute("/")({ - * component: withRuntime(HomePage, runtime.context), + * component: Component.withRuntime(HomePage, runtime.context) * }) * ``` * @@ -468,6 +483,10 @@ export const withRuntime: { }) +/** + * Service that keeps track of scopes associated with React components + * (used internally by the `useScope` hook). + */ export class ScopeMap extends Effect.Service()("@effect-fc/Component/ScopeMap", { effect: Effect.bind(Effect.Do, "ref", () => Ref.make(HashMap.empty())) }) {} @@ -487,6 +506,14 @@ export declare namespace useScope { } } +/** + * Hook that creates and manages a `Scope` for the current component instance. + * + * Automatically closes the scope whenever `deps` changes or the component unmounts. + * + * @param deps - dependency array like in `React.useEffect` + * @param options - finalizer execution control + */ export const useScope = Effect.fnUntraced(function*( deps: React.DependencyList, options?: useScope.Options, @@ -542,6 +569,9 @@ export const useScope = Effect.fnUntraced(function*( return scope }) +/** + * Runs an effect and returns its result only once on component mount. + */ export const useOnMount = Effect.fnUntraced(function* ( f: () => Effect.Effect ): Effect.fn.Return { @@ -553,6 +583,11 @@ export declare namespace useOnChange { export interface Options extends useScope.Options {} } +/** + * Runs an effect and returns its result whenever dependencies change. + * + * Provides its own `Scope` which closes whenever `deps` changes or the component unmounts. + */ export const useOnChange = Effect.fnUntraced(function* ( f: () => Effect.Effect, deps: React.DependencyList, @@ -574,6 +609,11 @@ export declare namespace useReactEffect { } } +/** + * Like `React.useEffect` but accepts an effect. + * + * Cleanup logic is handled through the `Scope` API rather than using imperative cleanup. + */ export const useReactEffect = Effect.fnUntraced(function* ( f: () => Effect.Effect, deps?: React.DependencyList, @@ -610,6 +650,11 @@ export declare namespace useReactLayoutEffect { export interface Options extends useReactEffect.Options {} } +/** + * Like `React.useReactLayoutEffect` but accepts an effect. + * + * Cleanup logic is handled through the `Scope` API rather than using imperative cleanup. + */ export const useReactLayoutEffect = Effect.fnUntraced(function* ( f: () => Effect.Effect, deps?: React.DependencyList, @@ -620,18 +665,27 @@ export const useReactLayoutEffect = Effect.fnUntraced(function* ( React.useLayoutEffect(() => runReactEffect(runtime, f, options), deps) }) +/** + * Get a synchronous run function for the current runtime context. + */ export const useRunSync = (): Effect.Effect< (effect: Effect.Effect) => A, never, Scope.Scope | R > => Effect.andThen(Effect.runtime(), Runtime.runSync) +/** + * Get a Promise-based run function for the current runtime context. + */ export const useRunPromise = (): Effect.Effect< (effect: Effect.Effect) => Promise, never, Scope.Scope | R > => Effect.andThen(Effect.runtime(), context => Runtime.runPromise(context)) +/** + * Turns a function returning an effect into a memoized synchronous function. + */ export const useCallbackSync = Effect.fnUntraced(function* ( f: (...args: Args) => Effect.Effect, deps: React.DependencyList, @@ -644,6 +698,9 @@ export const useCallbackSync = Effect.fnUntraced(function* Runtime.runSync(runtimeRef.current)(f(...args)), deps) }) +/** + * Turns a function returning an effect into a memoized Promise-based asynchronous function. + */ export const useCallbackPromise = Effect.fnUntraced(function* ( f: (...args: Args) => Effect.Effect, deps: React.DependencyList, @@ -660,10 +717,17 @@ export declare namespace useContext { export interface Options extends useOnChange.Options {} } +/** + * Hook that constructs a layer and returns the created context. + * + * The layer gets reconstructed everytime `layer` changes, so make sure its value is stable. + * + * Building a layer containing asynchronous effects require the component calling this hook to be made async using `Async.async`. + */ export const useContext = ( layer: Layer.Layer, options?: useContext.Options, -): Effect.Effect, E, Scope.Scope | RIn> => useOnChange(() => Effect.context().pipe( +): Effect.Effect, E, Exclude> => useOnChange(() => Effect.context().pipe( Effect.map(context => ManagedRuntime.make(Layer.provide(layer, Layer.succeedContext(context)))), Effect.tap(runtime => Effect.addFinalizer(() => runtime.disposeEffect)), Effect.andThen(runtime => runtime.runtimeEffect), -- 2.49.1 From 581bab027cae8003c5d2998c2a25ecaed03dea2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 23 Jan 2026 01:20:11 +0100 Subject: [PATCH 09/12] Fix useScope --- packages/effect-fc/src/Component.ts | 57 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index 284f09d..3cd4b2d 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -522,43 +522,40 @@ export const useScope = Effect.fnUntraced(function*( const runtimeRef = React.useRef>(null!) runtimeRef.current = yield* Effect.runtime() - const scopeMap = yield* ScopeMap as unknown as Effect.Effect - - const [key, scope] = React.useMemo(() => Runtime.runSync(runtimeRef.current)(Effect.andThen( - Effect.all([Effect.succeed({}), scopeMap.ref]), - ([key, map]) => Effect.andThen( - Option.match(HashMap.get(map, key), { - onSome: entry => Effect.succeed(entry.scope), - onNone: () => Effect.tap( - Scope.make(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy), - scope => Ref.update(scopeMap.ref, HashMap.set(key, { - scope, - closeFiber: Option.none(), - })), - ), - }), - scope => [key, scope] as const, + const { key, scope } = React.useMemo(() => Runtime.runSync(runtimeRef.current)(Effect.Do.pipe( + Effect.bind("scopeMapRef", () => Effect.map( + ScopeMap as unknown as Effect.Effect, + scopeMap => scopeMap.ref, + )), + Effect.let("key", () => ({})), + Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy)), + Effect.tap(({ scopeMapRef, key, scope }) => + Ref.update(scopeMapRef, HashMap.set(key, { + scope, + closeFiber: Option.none(), + })) ), // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList )), deps) // biome-ignore lint/correctness/useExhaustiveDependencies: only reactive on "key" - React.useEffect(() => Runtime.runSync(runtimeRef.current)(scopeMap.ref.pipe( - Effect.andThen(HashMap.get(key)), - Effect.tap(entry => Option.match(entry.closeFiber, { - onSome: fiber => Effect.andThen( - Ref.update(scopeMap.ref, HashMap.set(key, { ...entry, closeFiber: Option.none() })), - Fiber.interruptFork(fiber), - ), - onNone: () => Effect.void, - })), - Effect.map(({ scope }) => + React.useEffect(() => Runtime.runSync(runtimeRef.current)((ScopeMap as unknown as Effect.Effect).pipe( + Effect.map(scopeMap => scopeMap.ref), + Effect.tap(ref => ref.pipe( + Effect.andThen(HashMap.get(key)), + Effect.andThen(entry => Option.match(entry.closeFiber, { + onSome: Fiber.interruptFork, + onNone: () => Effect.void, + })), + )), + Effect.map(ref => () => Runtime.runSync(runtimeRef.current)(Effect.andThen( - Effect.forkDaemon(Effect.sleep(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce).pipe( + Effect.sleep(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce).pipe( Effect.andThen(Scope.close(scope, Exit.void)), - Effect.andThen(Ref.update(scopeMap.ref, HashMap.remove(key))), - )), - fiber => Ref.update(scopeMap.ref, HashMap.set(key, { + Effect.onExit(() => Ref.update(ref, HashMap.remove(key))), + Effect.forkDaemon, + ), + fiber => Ref.update(ref, HashMap.set(key, { scope, closeFiber: Option.some(fiber), })), -- 2.49.1 From 2f60ed340615ad078eae256e06a799da1a8ab5f9 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 23 Jan 2026 01:24:03 +0100 Subject: [PATCH 10/12] Update dependency @effect/language-service to ^0.72.0 (#32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@effect/language-service](https://github.com/Effect-TS/language-service) | [`^0.65.0` → `^0.72.0`](https://renovatebot.com/diffs/npm/@effect%2flanguage-service/0.65.0/0.72.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@effect%2flanguage-service/0.72.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@effect%2flanguage-service/0.65.0/0.72.0?slim=true) | --- ### Release Notes

Effect-TS/language-service (@​effect/language-service) ### [`v0.72.0`](https://github.com/Effect-TS/language-service/releases/tag/%40effect/language-service%400.72.0) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.71.2...@effect/language-service@0.72.0) ##### Minor Changes - [#​627](https://github.com/Effect-TS/language-service/pull/627) [`a34f997`](https://github.com/Effect-TS/language-service/commit/a34f997af3b97f0d97ac755ee044f9653724b7d2) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Refactor internal structure and harness ### [`v0.71.2`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0712) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.71.1...v0.71.2) ##### Patch Changes - [#​625](https://github.com/Effect-TS/language-service/pull/625) [`422087d`](https://github.com/Effect-TS/language-service/commit/422087d01211da6e917da0a5f67cf1d8b08924e5) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Fix CLI patching to target `emitFilesAndReportErrors` function instead of `emitFilesAndReportErrorsAndGetExitStatus`, updating the injection approach to replace the diagnostics property in the return statement's object literal. ### [`v0.71.1`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0711) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.71.0...v0.71.1) ##### Patch Changes - [#​624](https://github.com/Effect-TS/language-service/pull/624) [`d279457`](https://github.com/Effect-TS/language-service/commit/d279457ed54c389e6725b4ed8a19edf53f4e7094) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Add `ignoreEffectSuggestionsInTscExitCode` option (default: `true`) to control whether Effect-related suggestions affect the TSC exit code. When enabled, suggestions won't cause `tsc` to return a non-zero exit code. - [#​622](https://github.com/Effect-TS/language-service/pull/622) [`5eab20a`](https://github.com/Effect-TS/language-service/commit/5eab20a1a8fd86f19360a573f074f1dec0dcf308) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Add `ignoreEffectWarningsInTscExitCode` option to allow Effect-related warnings to not affect the TSC exit code. When enabled, `tsc` will compile successfully even if Effect warnings are emitted. This is useful for CI/CD pipelines where Effect diagnostics should be informational rather than blocking. ### [`v0.71.0`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0710) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.70.0...v0.71.0) ##### Minor Changes - [#​619](https://github.com/Effect-TS/language-service/pull/619) [`f171350`](https://github.com/Effect-TS/language-service/commit/f171350ffc34d9dfe0989027ec3d39eed42eaabe) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Add `effectSucceedWithVoid` diagnostic to suggest using `Effect.void` instead of `Effect.succeed(undefined)` or `Effect.succeed(void 0)`. The diagnostic detects calls to `Effect.succeed` where the argument is exactly `undefined` or `void 0` (including parenthesized variants) and suggests replacing them with the more idiomatic `Effect.void`. A quick fix is provided to automatically apply the replacement. Before: ```typescript Effect.succeed(undefined); Effect.succeed(void 0); ``` After: ```typescript Effect.void; ``` ##### Patch Changes - [#​621](https://github.com/Effect-TS/language-service/pull/621) [`74ef937`](https://github.com/Effect-TS/language-service/commit/74ef937ae072e051b113d75429b48338fcc57bc4) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improve diagnostic messages for `globalErrorInEffectFailure` and `globalErrorInEffectCatch` to be more concise and actionable. Before: ``` The global Error type is used in an Effect failure channel. It's not recommended to use the global Error type in Effect failures as they can get merged together. Instead, use tagged errors or custom errors with a discriminator property to get properly type-checked errors. ``` After: ``` Global 'Error' loses type safety as untagged errors merge together in the Effect failure channel. Consider using a tagged error and optionally wrapping the original in a 'cause' property. ``` ### [`v0.70.0`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0700) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.69.2...v0.70.0) ##### Minor Changes - [#​618](https://github.com/Effect-TS/language-service/pull/618) [`ed689f8`](https://github.com/Effect-TS/language-service/commit/ed689f8e557481eddee3ed4bdd56d8e1320f164d) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improve `globalErrorInEffectFailure` diagnostic to detect global Error type in any Effect failure channel. The diagnostic now works by finding `new Error()` expressions and checking if they end up in an Effect's failure channel, rather than only checking `Effect.fail` calls. This means it will now detect global Error usage in: - `Effect.fail(new Error(...))` - `Effect.gen` functions that fail with global Error - `Effect.mapError` converting to global Error - `Effect.flatMap` chains that include global Error The diagnostic now reports at the `new Error()` location for better precision. ##### Patch Changes - [#​616](https://github.com/Effect-TS/language-service/pull/616) [`b32da44`](https://github.com/Effect-TS/language-service/commit/b32da446de65bcc1dad802a9b6c82c43ae4dd6fd) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improve `missedPipeableOpportunity` diagnostic message to show the suggested subject for `.pipe(...)`. Before: ``` Nested function calls can be converted to pipeable style for better readability. ``` After: ``` Nested function calls can be converted to pipeable style for better readability; consider using addOne(5).pipe(...) instead. ``` ### [`v0.69.2`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0692) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.69.1...v0.69.2) ##### Patch Changes - [#​612](https://github.com/Effect-TS/language-service/pull/612) [`2b49181`](https://github.com/Effect-TS/language-service/commit/2b49181323b5d969897749412af896a0a7d2325f) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improve effectFnIife diagnostic message to suggest Effect.withSpan with the trace name when available When `Effect.fn("traceName")` is used as an IIFE, the diagnostic now suggests using `Effect.gen` with `Effect.withSpan("traceName")` piped at the end to maintain tracing spans. For `Effect.fnUntraced`, it simply suggests using `Effect.gen` without the span suggestion. - [#​615](https://github.com/Effect-TS/language-service/pull/615) [`ae4f054`](https://github.com/Effect-TS/language-service/commit/ae4f054cdd6d1c6c2c5f906d7c6dd25ab2b35526) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improve effectFnOpportunity diagnostic with more specific messages and configurable fixes - Add new `effectFn` configuration option to control which code fix variants are offered: `"untraced"`, `"span"`, `"inferred-span"`, `"no-span"` (defaults to `["span"]`) - Diagnostic message now shows the exact expected signature for the rewrite - Distinguish between explicit trace from `Effect.withSpan` vs inferred trace from function name - Skip functions with return type annotations to avoid issues with recursive functions **Before:** ``` This function could benefit from Effect.fn's automatic tracing... ``` **After:** ``` Can be rewritten as a reusable function: Effect.fn("myFunction")(function*() { ... }) ``` ### [`v0.69.1`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0691) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.69.0...v0.69.1) ##### Patch Changes - [#​610](https://github.com/Effect-TS/language-service/pull/610) [`990ccbc`](https://github.com/Effect-TS/language-service/commit/990ccbc98f784fe2aea8f12be4fa8c138de2feca) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improve effectFnOpportunity diagnostic message to mention that quickfixes are available in the editor or via the CLI quickfixes command. ### [`v0.69.0`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0690) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.68.0...v0.69.0) ##### Minor Changes - [#​608](https://github.com/Effect-TS/language-service/pull/608) [`bc7da1e`](https://github.com/Effect-TS/language-service/commit/bc7da1ef6f0f3d4aa0e88ef28de49e6845c764df) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Add `effectFnIife` diagnostic to warn when `Effect.fn` or `Effect.fnUntraced` is used as an IIFE (Immediately Invoked Function Expression). `Effect.fn` is designed to create reusable functions that can take arguments and provide tracing. When used as an IIFE, `Effect.gen` is more appropriate. **Example:** ```ts // Before (triggers warning) const result = Effect.fn("test")(function* () { yield* Effect.succeed(1); })(); // After (using Effect.gen) const result = Effect.gen(function* () { yield* Effect.succeed(1); }); ``` A quick fix is provided to automatically convert `Effect.fn` IIFEs to `Effect.gen`. ### [`v0.68.0`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0680) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.67.0...v0.68.0) ##### Minor Changes - [#​603](https://github.com/Effect-TS/language-service/pull/603) [`d747210`](https://github.com/Effect-TS/language-service/commit/d747210f173d87e068ad2370f6b7667be7cde07d) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Added `instanceOfSchema` diagnostic that suggests using `Schema.is` instead of `instanceof` for Effect Schema types. Example: ```typescript import { Schema } from "effect" const MySchema = Schema.Struct({ name: Schema.String }) // Before - triggers diagnostic if (value instanceof MySchema) { ... } // After - using Schema.is if (Schema.is(MySchema)(value)) { ... } ``` The diagnostic is disabled by default and can be enabled with `instanceOfSchema:suggestion` or `instanceOfSchema:warning`. ##### Patch Changes - [#​605](https://github.com/Effect-TS/language-service/pull/605) [`d63d5df`](https://github.com/Effect-TS/language-service/commit/d63d5df97858c8fd5a5af325141b08414f3d6eca) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improve `leakingRequirements` diagnostic message for clarity ### [`v0.67.0`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0670) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.66.1...v0.67.0) ##### Minor Changes - [#​599](https://github.com/Effect-TS/language-service/pull/599) [`4c9f5c7`](https://github.com/Effect-TS/language-service/commit/4c9f5c7c27e551e23c12ba31e07a955c5e15f5c9) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Add `quickfixes` CLI command that shows diagnostics with available quick fixes and their proposed code changes. Example usage: ```bash # Check a specific file effect-language-service quickfixes --file ./src/index.ts # Check an entire project effect-language-service quickfixes --project ./tsconfig.json ``` The command displays each diagnostic along with the available code fixes and a diff preview of the proposed changes, making it easy to see what automatic fixes are available before applying them. ##### Patch Changes - [#​601](https://github.com/Effect-TS/language-service/pull/601) [`c0a6da3`](https://github.com/Effect-TS/language-service/commit/c0a6da3811915b53e04cc1a237c4fa93d6fc91b0) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Reduce over-suggestion of effectFnOpportunity diagnostic for regular functions. The diagnostic now only suggests `Effect.fn` for regular functions (not using `Effect.gen`) when: - The function has a block body (not a concise arrow expression) - The function body has more than 5 statements Functions using `Effect.gen` are still always suggested regardless of body size. ### [`v0.66.1`](https://github.com/Effect-TS/language-service/blob/HEAD/CHANGELOG.md#0661) [Compare Source](https://github.com/Effect-TS/language-service/compare/v0.65.0...v0.66.1) ##### Patch Changes - [#​597](https://github.com/Effect-TS/language-service/pull/597) [`3833a10`](https://github.com/Effect-TS/language-service/commit/3833a10e3188c4ebf113625c00f60e17b8bf6b80) Thanks [@​mattiamanzati](https://github.com/mattiamanzati)! - Improved `effectFnOpportunity` diagnostic message to mention that Effect.fn accepts piped transformations as additional arguments when pipe transformations are detected. When a function has `.pipe()` calls that would be absorbed by Effect.fn, the message now includes: "Effect.fn also accepts the piped transformations as additional arguments."
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://git.valverde.cloud/Thilawyn/effect-fc/pulls/32 Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- bun.lock | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index fedfde5..de45f65 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "@effect-fc/monorepo", "devDependencies": { "@biomejs/biome": "^2.3.8", - "@effect/language-service": "^0.65.0", + "@effect/language-service": "^0.72.0", "@types/bun": "^1.3.3", "npm-check-updates": "^19.1.2", "npm-sort": "^0.0.4", @@ -116,7 +116,7 @@ "@effect-fc/example": ["@effect-fc/example@workspace:packages/example"], - "@effect/language-service": ["@effect/language-service@0.65.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-eHcpLNCZa1XEDRrXLZqTdky6jAQojL6zQEW53Ba6vJL35j77tJTnV9BFkk34G3rxKoplNo39U0Mum3RfuH9rsg=="], + "@effect/language-service": ["@effect/language-service@0.72.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-MWkyTPCXSs5Q3OIBWR3q24SA+ipkdWW7EBJBt6EPUzlzZxjJLXtLBhXpMoCFheSEM0FTWOHT4BRLh5lufsmjVw=="], "@effect/platform": ["@effect/platform@0.94.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.14" } }, "sha512-SlL8OMTogHmMNnFLnPAHHo3ua1yrB1LNQOVQMiZsqYu9g3216xjr0gn5WoDgCxUyOdZcseegMjWJ7dhm/2vnfg=="], diff --git a/package.json b/package.json index 3799a38..8355c94 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@biomejs/biome": "^2.3.8", - "@effect/language-service": "^0.65.0", + "@effect/language-service": "^0.72.0", "@types/bun": "^1.3.3", "npm-check-updates": "^19.1.2", "npm-sort": "^0.0.4", -- 2.49.1 From 8aeea1d1bc97f27ac7555daf431d0d4a76a72135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 23 Jan 2026 01:42:40 +0100 Subject: [PATCH 11/12] Version bump --- bun.lock | 68 ++++++++++++++++----------------- package.json | 10 ++--- packages/effect-fc/package.json | 2 +- packages/example/package.json | 28 +++++++------- 4 files changed, 54 insertions(+), 54 deletions(-) diff --git a/bun.lock b/bun.lock index de45f65..d48c501 100644 --- a/bun.lock +++ b/bun.lock @@ -5,12 +5,12 @@ "": { "name": "@effect-fc/monorepo", "devDependencies": { - "@biomejs/biome": "^2.3.8", + "@biomejs/biome": "^2.3.11", "@effect/language-service": "^0.72.0", - "@types/bun": "^1.3.3", - "npm-check-updates": "^19.1.2", + "@types/bun": "^1.3.6", + "npm-check-updates": "^19.3.1", "npm-sort": "^0.0.4", - "turbo": "^2.6.1", + "turbo": "^2.7.5", "typescript": "^5.9.3", }, }, @@ -30,26 +30,26 @@ "name": "@effect-fc/example", "version": "0.0.0", "dependencies": { - "@effect/platform": "^0.94.0", + "@effect/platform": "^0.94.2", "@effect/platform-browser": "^0.74.0", "@radix-ui/themes": "^3.2.1", "@typed/id": "^0.17.2", - "effect": "^3.19.8", + "effect": "^3.19.15", "effect-fc": "workspace:*", "react-icons": "^5.5.0", }, "devDependencies": { - "@tanstack/react-router": "^1.139.12", - "@tanstack/react-router-devtools": "^1.139.12", - "@tanstack/router-plugin": "^1.139.12", - "@types/react": "^19.2.7", + "@tanstack/react-router": "^1.154.12", + "@tanstack/react-router-devtools": "^1.154.12", + "@tanstack/router-plugin": "^1.154.12", + "@types/react": "^19.2.9", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.1", + "@vitejs/plugin-react": "^5.1.2", "globals": "^17.0.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "type-fest": "^5.2.0", - "vite": "^7.2.6", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "type-fest": "^5.4.1", + "vite": "^7.3.1", }, }, }, @@ -118,7 +118,7 @@ "@effect/language-service": ["@effect/language-service@0.72.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-MWkyTPCXSs5Q3OIBWR3q24SA+ipkdWW7EBJBt6EPUzlzZxjJLXtLBhXpMoCFheSEM0FTWOHT4BRLh5lufsmjVw=="], - "@effect/platform": ["@effect/platform@0.94.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.14" } }, "sha512-SlL8OMTogHmMNnFLnPAHHo3ua1yrB1LNQOVQMiZsqYu9g3216xjr0gn5WoDgCxUyOdZcseegMjWJ7dhm/2vnfg=="], + "@effect/platform": ["@effect/platform@0.94.2", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.15" } }, "sha512-85vdwpnK4oH/rJ3EuX/Gi2Hkt+K4HvXWr9bxCuqvty9hxyEcRxkJcqTesYrcVoQB6aULb1Za2B0MKoTbvffB3Q=="], "@effect/platform-browser": ["@effect/platform-browser@0.74.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.94.0", "effect": "^3.19.13" } }, "sha512-PAgkg5L5cASQpScA0SZTSy543MVA4A9kmpVCjo2fCINLRpTeuCFAOQHgPmw8dKHnYS0yGs2TYn7AlrhhqQ5o3g=="], @@ -382,27 +382,27 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@tanstack/history": ["@tanstack/history@1.145.7", "", {}, "sha512-gMo/ReTUp0a3IOcZoI3hH6PLDC2R/5ELQ7P2yu9F6aEkA0wSQh+Q4qzMrtcKvF2ut0oE+16xWCGDo/TdYd6cEQ=="], + "@tanstack/history": ["@tanstack/history@1.154.7", "", {}, "sha512-YBgwS9qG4rs1ZY/ZrhQtjOH8BG9Qa2wf2AsxT/SnZ4HZJ1DcCEqkoiHH0yH6CYvdDit31X5HokOqQrRSsZEwGA=="], - "@tanstack/react-router": ["@tanstack/react-router@1.150.0", "", { "dependencies": { "@tanstack/history": "1.145.7", "@tanstack/react-store": "^0.8.0", "@tanstack/router-core": "1.150.0", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-k/oycTCpBT2XoEk9dNd/nNYhF0X9fLSB10lT40+NVX1TjOtBq5whksk8MT6oRnSoQ8KWeb7La3G9kFaAeSULkA=="], + "@tanstack/react-router": ["@tanstack/react-router@1.154.12", "", { "dependencies": { "@tanstack/history": "1.154.7", "@tanstack/react-store": "^0.8.0", "@tanstack/router-core": "1.154.12", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-WiYfC6IYC2HwjkATouJCQlAM5RJ8MViefslfUcZpsbCb+WGQpdpvUY7GPJLEeessSpqgiC2EabRYC2kYVNyMPg=="], - "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.150.0", "", { "dependencies": { "@tanstack/router-devtools-core": "1.150.0" }, "peerDependencies": { "@tanstack/react-router": "^1.150.0", "@tanstack/router-core": "^1.150.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-TlvTE+XK5XVCfYjazoMWkjyyPKe4kMw2nCA7EuWoYUJKOqRW5oKvBY7auViGWxp51FKDEjV3bbok3wPKBYwZww=="], + "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.154.12", "", { "dependencies": { "@tanstack/router-devtools-core": "1.154.12" }, "peerDependencies": { "@tanstack/react-router": "^1.154.12", "@tanstack/router-core": "^1.154.12", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-TcGe7pmeVjk1zD58eMR87GG9OXMx6LDGz5QopmJS4LafvK2hvuaht+eKBnZlCvKLPlXu5juwHT4u+2bYdn6sqQ=="], "@tanstack/react-store": ["@tanstack/react-store@0.8.0", "", { "dependencies": { "@tanstack/store": "0.8.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow=="], - "@tanstack/router-core": ["@tanstack/router-core@1.150.0", "", { "dependencies": { "@tanstack/history": "1.145.7", "@tanstack/store": "^0.8.0", "cookie-es": "^2.0.0", "seroval": "^1.4.1", "seroval-plugins": "^1.4.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-cAm44t/tUbfyzaDH+rE/WO4u3AgaZdpJp00xjQ4gNkC2O95ntVHq5fx+4fhtrkKpgdXoKldgk8OK66djiWpuGQ=="], + "@tanstack/router-core": ["@tanstack/router-core@1.154.12", "", { "dependencies": { "@tanstack/history": "1.154.7", "@tanstack/store": "^0.8.0", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-p+TKxkXcLGtCwwW237D8pV4f6ea2K1pzc/e65ljugoTawsA/YR2/gmTSBDTUsSYy6Tmu4mMJmZ0Q4zNkcfCS3g=="], - "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.150.0", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.150.0", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-61V+4fq2fOPru/48cuojKvWhQx2h/nuj4nVHwzu9E7O8h391h4Hks6axxRbY98/rIz96mn5TCoc0aYuoga53bg=="], + "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.154.12", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.154.12", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-lvnP9cqknvSSkUjqQRVn61TcBhq72hCFFOzMwdFdFPTO8nMEXvYE6ZZJiXtivwcvsKmO6XVFLMXuJr/928gNkw=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.150.0", "", { "dependencies": { "@tanstack/router-core": "1.150.0", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-WsA1bN5/I+cxE6V1DkU5ABIPBQxZLlxszElYgnIhs884tzukv76rYMFOy6Xqd51YIFdYtjDrxZbp4/vfkrVCug=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.154.12", "", { "dependencies": { "@tanstack/router-core": "1.154.12", "@tanstack/router-utils": "1.154.7", "@tanstack/virtual-file-routes": "1.154.7", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-cjr3KS3Esnyh05CWl78KgK2Z9kTjeFasZXcSUrh//TzzU72eXQ+dzKppD3kMsjuyRfUxAfdufsR9GDNMMuLk9w=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.150.0", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.150.0", "@tanstack/router-generator": "1.150.0", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "babel-dead-code-elimination": "^1.0.11", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.150.0", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-k2NLysBXO4Wpt4Oo0xeBhNtFsMwHOU8ud48/cWNWbV89QAjlk0XU5CGNj2JEaFMT0zlF3H/aM5/h0+vYnDjFFA=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.154.12", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.154.12", "@tanstack/router-generator": "1.154.12", "@tanstack/router-utils": "1.154.7", "@tanstack/virtual-file-routes": "1.154.7", "babel-dead-code-elimination": "^1.0.11", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.154.12", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-YlFjrL5j7RbYT/B3RZZedbXOHXfqRV7b/qIGyojBaHsrIgKFGo4AHg/FyS50HJaHGQ27vvgWNSy/4Orrozbm0Q=="], - "@tanstack/router-utils": ["@tanstack/router-utils@1.143.11", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-N24G4LpfyK8dOlnP8BvNdkuxg1xQljkyl6PcrdiPSA301pOjatRT1y8wuCCJZKVVD8gkd0MpCZ0VEjRMGILOtA=="], + "@tanstack/router-utils": ["@tanstack/router-utils@1.154.7", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-61bGx32tMKuEpVRseu2sh1KQe8CfB7793Mch/kyQt0EP3tD7X0sXmimCl3truRiDGUtI0CaSoQV1NPjAII1RBA=="], "@tanstack/store": ["@tanstack/store@0.8.0", "", {}, "sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ=="], - "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.145.4", "", {}, "sha512-CI75JrfqSluhdGwLssgVeQBaCphgfkMQpi8MCY3UJX1hoGzXa8kHYJcUuIFMOLs1q7zqHy++EVVtMK03osR5wQ=="], + "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.154.7", "", {}, "sha512-cHHDnewHozgjpI+MIVp9tcib6lYEQK5MyUr0ChHpHFGBl8Xei55rohFK0I0ve/GKoHeioaK42Smd8OixPp6CTg=="], "@typed/id": ["@typed/id@0.17.2", "", { "peerDependencies": { "effect": "^3.14.7" } }, "sha512-z/Z14/moeu9x45IpkGaRwuvb+CQ3s3UCc/agcpZibTz1yPb3RgSDXx4rOHIuyb6hG6oNzqe9yY4GbbMq3Hb5Ug=="], @@ -420,7 +420,7 @@ "@types/node": ["@types/node@25.0.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw=="], - "@types/react": ["@types/react@19.2.8", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg=="], + "@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -470,7 +470,7 @@ "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], - "effect": ["effect@3.19.14", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA=="], + "effect": ["effect@3.19.15", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-vzMmgfZKLcojmUjBdlQx+uaKryO7yULlRxjpDnHdnvcp1NPHxJyoM6IOXBLlzz2I/uPtZpGKavt5hBv7IvGZkA=="], "effect-fc": ["effect-fc@workspace:packages/effect-fc"], @@ -604,19 +604,19 @@ "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], - "turbo": ["turbo@2.7.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.4", "turbo-darwin-arm64": "2.7.4", "turbo-linux-64": "2.7.4", "turbo-linux-arm64": "2.7.4", "turbo-windows-64": "2.7.4", "turbo-windows-arm64": "2.7.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-bkO4AddmDishzJB2ze7aYYPaejMoJVfS0XnaR6RCdXFOY8JGJfQE+l9fKiV7uDPa5Ut44gmOWJL3894CIMeH9g=="], + "turbo": ["turbo@2.7.5", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.5", "turbo-darwin-arm64": "2.7.5", "turbo-linux-64": "2.7.5", "turbo-linux-arm64": "2.7.5", "turbo-windows-64": "2.7.5", "turbo-windows-arm64": "2.7.5" }, "bin": { "turbo": "bin/turbo" } }, "sha512-7Imdmg37joOloTnj+DPrab9hIaQcDdJ5RwSzcauo/wMOSAgO+A/I/8b3hsGGs6PWQz70m/jkPgdqWsfNKtwwDQ=="], - "turbo-darwin-64": ["turbo-darwin-64@2.7.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xDR30ltfkSsRfGzABBckvl1nz1cZ3ssTujvdj+TPwOweeDRvZ0e06t5DS0rmRBvyKpgGs42K/EK6Mn2qLlFY9A=="], + "turbo-darwin-64": ["turbo-darwin-64@2.7.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-nN3wfLLj4OES/7awYyyM7fkU8U8sAFxsXau2bYJwAWi6T09jd87DgHD8N31zXaJ7LcpyppHWPRI2Ov9MuZEwnQ=="], - "turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P7sjqXtOL/+nYWPvcDGWhi8wf8M8mZHHB8XEzw2VX7VJrS8IGHyJHGD1AYfDvhAEcr7pnk3gGifz3/xyhI655w=="], + "turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wCoDHMiTf3FgLAbZHDDx/unNNonSGhsF5AbbYODbxnpYyoKDpEYacUEPjZD895vDhNvYCH0Nnk24YsP4n/cD6g=="], - "turbo-linux-64": ["turbo-linux-64@2.7.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GofFOxRO/IhG8BcPyMSSB3Y2+oKQotsaYbHxL9yD6JPb20/o35eo+zUSyazOtilAwDHnak5dorAJFoFU8MIg2A=="], + "turbo-linux-64": ["turbo-linux-64@2.7.5", "", { "os": "linux", "cpu": "x64" }, "sha512-KKPvhOmJMmzWj/yjeO4LywkQ85vOJyhru7AZk/+c4B6OUh/odQ++SiIJBSbTG2lm1CuV5gV5vXZnf/2AMlu3Zg=="], - "turbo-linux-arm64": ["turbo-linux-arm64@2.7.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+RQKgNjksVPxYAyAgmDV7w/1qj++qca+nSNTAOKGOfJiDtSvRKoci89oftJ6anGs00uamLKVEQ712TI/tfNAIw=="], + "turbo-linux-arm64": ["turbo-linux-arm64@2.7.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-8PIva4L6BQhiPikUTds9lSFSHXVDAsEvV6QUlgwPsXrtXVQMVi6Sv9p+IxtlWQFvGkdYJUgX9GnK2rC030Xcmw=="], - "turbo-windows-64": ["turbo-windows-64@2.7.4", "", { "os": "win32", "cpu": "x64" }, "sha512-rfak1+g+ON3czs1mDYsCS4X74ZmK6gOgRQTXjDICtzvR4o61paqtgAYtNPofcVsMWeF4wvCajSeoAkkeAnQ1kg=="], + "turbo-windows-64": ["turbo-windows-64@2.7.5", "", { "os": "win32", "cpu": "x64" }, "sha512-rupskv/mkIUgQXzX/wUiK00mKMorQcK8yzhGFha/D5lm05FEnLx8dsip6rWzMcVpvh+4GUMA56PgtnOgpel2AA=="], - "turbo-windows-arm64": ["turbo-windows-arm64@2.7.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-1ZgBNjNRbDu/fPeqXuX9i26x3CJ/Y1gcwUpQ+Vp7kN9Un6RZ9kzs164f/knrjcu5E+szCRexVjRSJay1k5jApA=="], + "turbo-windows-arm64": ["turbo-windows-arm64@2.7.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-G377Gxn6P42RnCzfMyDvsqQV7j69kVHKlhz9J4RhtJOB5+DyY4yYh/w0oTIxZQ4JRMmhjwLu3w9zncMoQ6nNDw=="], "type-fest": ["type-fest@5.4.1", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-xygQcmneDyzsEuKZrFbRMne5HDqMs++aFzefrJTgEIKjQ3rekM+RPfFCVq2Gp1VIDqddoYeppCj4Pcb+RZW0GQ=="], diff --git a/package.json b/package.json index 8355c94..5b31e58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@effect-fc/monorepo", - "packageManager": "bun@1.3.3", + "packageManager": "bun@1.3.6", "private": true, "workspaces": [ "./packages/*" @@ -15,12 +15,12 @@ "clean:modules": "turbo clean:modules && rm -rf node_modules" }, "devDependencies": { - "@biomejs/biome": "^2.3.8", + "@biomejs/biome": "^2.3.11", "@effect/language-service": "^0.72.0", - "@types/bun": "^1.3.3", - "npm-check-updates": "^19.1.2", + "@types/bun": "^1.3.6", + "npm-check-updates": "^19.3.1", "npm-sort": "^0.0.4", - "turbo": "^2.6.1", + "turbo": "^2.7.5", "typescript": "^5.9.3" } } diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index 2b307f9..c8429df 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -1,7 +1,7 @@ { "name": "effect-fc", "description": "Write React function components with Effect", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "files": [ "./README.md", diff --git a/packages/example/package.json b/packages/example/package.json index ef8fa8d..b38319c 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -13,30 +13,30 @@ "clean:modules": "rm -rf node_modules" }, "devDependencies": { - "@tanstack/react-router": "^1.139.12", - "@tanstack/react-router-devtools": "^1.139.12", - "@tanstack/router-plugin": "^1.139.12", - "@types/react": "^19.2.7", + "@tanstack/react-router": "^1.154.12", + "@tanstack/react-router-devtools": "^1.154.12", + "@tanstack/router-plugin": "^1.154.12", + "@types/react": "^19.2.9", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.1", + "@vitejs/plugin-react": "^5.1.2", "globals": "^17.0.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "type-fest": "^5.2.0", - "vite": "^7.2.6" + "react": "^19.2.3", + "react-dom": "^19.2.3", + "type-fest": "^5.4.1", + "vite": "^7.3.1" }, "dependencies": { - "@effect/platform": "^0.94.0", + "@effect/platform": "^0.94.2", "@effect/platform-browser": "^0.74.0", "@radix-ui/themes": "^3.2.1", "@typed/id": "^0.17.2", - "effect": "^3.19.8", + "effect": "^3.19.15", "effect-fc": "workspace:*", "react-icons": "^5.5.0" }, "overrides": { - "@types/react": "^19.2.7", - "effect": "^3.19.8", - "react": "^19.2.0" + "@types/react": "^19.2.9", + "effect": "^3.19.15", + "react": "^19.2.3" } } -- 2.49.1 From 9965648f330b0ededa852accd48c744205c1b297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 23 Jan 2026 01:47:39 +0100 Subject: [PATCH 12/12] Update README --- packages/effect-fc/README.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/effect-fc/README.md b/packages/effect-fc/README.md index c9c65c2..b3c7ecd 100644 --- a/packages/effect-fc/README.md +++ b/packages/effect-fc/README.md @@ -1,31 +1,25 @@ # Effect FC -[Effect-TS](https://effect.website/) integration for React 19+ that allows you to write function components using Effect generators. +[Effect-TS](https://effect.website/) integration for React 19.2+ that allows you to write function components using Effect generators. This library is in early development. While it is (almost) feature complete and mostly usable, expect bugs and quirks. Things are still being ironed out, so ideas and criticisms are more than welcome. Documentation is currently being written. In the meantime, you can take a look at the `packages/example` directory. ## Peer dependencies -- `effect` 3.15+ -- `react` & `@types/react` 19+ +- `effect` 3.19+ +- `react` & `@types/react` 19.2+ ## Known issues - React Refresh doesn't work for Effect FC's yet. Page reload is required to view changes. Regular React components are unaffected. ## What writing components looks like ```typescript -import { Component } from "effect-fc" -import { useOnce, useSubscribables } from "effect-fc/Hooks" -import { Todo } from "./Todo" -import { TodosState } from "./TodosState.service" - - -export class Todos extends Component.makeUntraced("Todos")(function*() { +export class Todos extends Component.make("Todos")(function*() { const state = yield* TodosState const [todos] = yield* useSubscribables(state.ref) - yield* useOnce(() => Effect.andThen( + yield* useOnMount(() => Effect.andThen( Console.log("Todos mounted"), Effect.addFinalizer(() => Console.log("Todos unmounted")), )) @@ -49,8 +43,8 @@ export class Todos extends Component.makeUntraced("Todos")(function*() { const TodosStateLive = TodosState.Default("todos") -const Index = Component.makeUntraced("Index")(function*() { - const context = yield* useContext(TodosStateLive, { finalizerExecutionMode: "fork" }) +const Index = Component.make("Index")(function*() { + const context = yield* useContext(TodosStateLive) const TodosFC = yield* Effect.provide(Todos, context) return -- 2.49.1