From 6dc0a548cdb74c9169f176145dac229617322a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 1 Jun 2025 05:28:46 +0200 Subject: [PATCH] @reffuse/extension-query 0.1.5 (#16) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julien Valverdé Reviewed-on: https://gitea:3000/Thilawyn/reffuse/pulls/16 --- packages/example/src/VQueryErrorHandler.tsx | 6 +- packages/example/src/main.tsx | 6 +- packages/example/src/query.ts | 17 ----- packages/example/src/reffuse.ts | 5 +- .../example/src/services/AppQueryClient.ts | 7 ++ .../src/services/AppQueryErrorHandler.ts | 13 ++++ packages/example/src/services/index.ts | 3 +- packages/extension-query/package.json | 2 +- packages/extension-query/src/QueryClient.ts | 55 +++++++-------- .../extension-query/src/QueryErrorHandler.ts | 69 ++++++------------- 10 files changed, 78 insertions(+), 105 deletions(-) delete mode 100644 packages/example/src/query.ts create mode 100644 packages/example/src/services/AppQueryClient.ts create mode 100644 packages/example/src/services/AppQueryErrorHandler.ts diff --git a/packages/example/src/VQueryErrorHandler.tsx b/packages/example/src/VQueryErrorHandler.tsx index 1c4ca27..05b396d 100644 --- a/packages/example/src/VQueryErrorHandler.tsx +++ b/packages/example/src/VQueryErrorHandler.tsx @@ -1,16 +1,16 @@ import { AlertDialog, Button, Flex, Text } from "@radix-ui/themes" import { Cause, Console, Effect, Either, flow, Match, Option, Stream } from "effect" import { useState } from "react" -import { AppQueryClient } from "./query" import { R } from "./reffuse" +import { AppQueryErrorHandler } from "./services" export function VQueryErrorHandler() { const [open, setOpen] = useState(false) const error = R.useSubscribeStream( - R.useMemo(() => AppQueryClient.pipe( - Effect.map(client => client.errorHandler.errors.pipe( + R.useMemo(() => AppQueryErrorHandler.AppQueryErrorHandler.pipe( + Effect.map(handler => handler.errors.pipe( Stream.changes, Stream.tap(Console.error), Stream.tap(() => Effect.sync(() => setOpen(true))), diff --git a/packages/example/src/main.tsx b/packages/example/src/main.tsx index 44742b5..32a603d 100644 --- a/packages/example/src/main.tsx +++ b/packages/example/src/main.tsx @@ -5,14 +5,14 @@ import { Layer } from "effect" import { StrictMode } from "react" import { createRoot } from "react-dom/client" import { ReffuseRuntime } from "reffuse" -import { AppQueryClient, AppQueryErrorHandler } from "./query" import { RootContext } from "./reffuse" import { routeTree } from "./routeTree.gen" +import { AppQueryClient, AppQueryErrorHandler } from "./services" const layer = Layer.empty.pipe( - Layer.provideMerge(AppQueryClient.Default), - Layer.provideMerge(AppQueryErrorHandler.Default), + Layer.provideMerge(AppQueryClient.AppQueryClient.Default), + Layer.provideMerge(AppQueryErrorHandler.AppQueryErrorHandler.Default), Layer.provideMerge(Clipboard.layer), Layer.provideMerge(Geolocation.layer), Layer.provideMerge(Permissions.layer), diff --git a/packages/example/src/query.ts b/packages/example/src/query.ts deleted file mode 100644 index 853f6b0..0000000 --- a/packages/example/src/query.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { HttpClientError } from "@effect/platform" -import { QueryClient, QueryErrorHandler } from "@reffuse/extension-query" -import { Effect } from "effect" - - -export class AppQueryErrorHandler extends QueryErrorHandler.Service()( - "AppQueryErrorHandler", - - (self, failure, defect) => self.pipe( - Effect.catchTag("RequestError", "ResponseError", failure), - Effect.catchAllDefect(defect), - ), -) {} - -export class AppQueryClient extends QueryClient.Service()({ ErrorHandler: AppQueryErrorHandler }) {} diff --git a/packages/example/src/reffuse.ts b/packages/example/src/reffuse.ts index 3a08e53..00abc14 100644 --- a/packages/example/src/reffuse.ts +++ b/packages/example/src/reffuse.ts @@ -3,11 +3,12 @@ import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser" import { LazyRefExtension } from "@reffuse/extension-lazyref" import { QueryExtension } from "@reffuse/extension-query" import { Reffuse, ReffuseContext } from "reffuse" -import { AppQueryClient } from "./query" +import { AppQueryClient, AppQueryErrorHandler } from "./services" export const RootContext = ReffuseContext.make< - | AppQueryClient + | AppQueryClient.AppQueryClient + | AppQueryErrorHandler.AppQueryErrorHandler | Clipboard.Clipboard | Geolocation.Geolocation | Permissions.Permissions diff --git a/packages/example/src/services/AppQueryClient.ts b/packages/example/src/services/AppQueryClient.ts new file mode 100644 index 0000000..bee5014 --- /dev/null +++ b/packages/example/src/services/AppQueryClient.ts @@ -0,0 +1,7 @@ +import { QueryClient } from "@reffuse/extension-query" +import * as AppQueryErrorHandler from "./AppQueryErrorHandler" + + +export class AppQueryClient extends QueryClient.Service()({ + errorHandler: AppQueryErrorHandler.AppQueryErrorHandler +}) {} diff --git a/packages/example/src/services/AppQueryErrorHandler.ts b/packages/example/src/services/AppQueryErrorHandler.ts new file mode 100644 index 0000000..efff7ec --- /dev/null +++ b/packages/example/src/services/AppQueryErrorHandler.ts @@ -0,0 +1,13 @@ +import { HttpClientError } from "@effect/platform" +import { QueryErrorHandler } from "@reffuse/extension-query" +import { Effect } from "effect" + + +export class AppQueryErrorHandler extends Effect.Service()("AppQueryErrorHandler", { + effect: QueryErrorHandler.make()( + (self, failure, defect) => self.pipe( + Effect.catchTag("RequestError", "ResponseError", failure), + Effect.catchAllDefect(defect), + ) + ) +}) {} diff --git a/packages/example/src/services/index.ts b/packages/example/src/services/index.ts index 336ce12..691ab08 100644 --- a/packages/example/src/services/index.ts +++ b/packages/example/src/services/index.ts @@ -1 +1,2 @@ -export {} +export * as AppQueryClient from "./AppQueryClient" +export * as AppQueryErrorHandler from "./AppQueryErrorHandler" diff --git a/packages/extension-query/package.json b/packages/extension-query/package.json index 11f96ff..b72e34f 100644 --- a/packages/extension-query/package.json +++ b/packages/extension-query/package.json @@ -1,6 +1,6 @@ { "name": "@reffuse/extension-query", - "version": "0.1.4", + "version": "0.1.5", "type": "module", "files": [ "./README.md", diff --git a/packages/extension-query/src/QueryClient.ts b/packages/extension-query/src/QueryClient.ts index 1e638f6..c16d937 100644 --- a/packages/extension-query/src/QueryClient.ts +++ b/packages/extension-query/src/QueryClient.ts @@ -1,4 +1,4 @@ -import { Context, Effect, Layer } from "effect" +import { Context, Effect, identity, Layer } from "effect" import type { Mutable } from "effect/Types" import * as QueryErrorHandler from "./QueryErrorHandler.js" @@ -8,6 +8,17 @@ export interface QueryClient { } +export interface MakeProps { + readonly errorHandler: QueryErrorHandler.QueryErrorHandler +} + +export const make = ( + { errorHandler }: MakeProps +): Effect.Effect> => Effect.Do.pipe( + Effect.let("errorHandler", () => errorHandler) +) + + const id = "@reffuse/extension-query/QueryClient" export type TagClassShape = Context.TagClassShape> @@ -19,46 +30,28 @@ export type GenericTagClass = Context.TagClass< export const makeGenericTagClass = (): GenericTagClass => Context.Tag(id)() -export interface ServiceProps { - readonly ErrorHandler?: Context.Tag> +export interface ServiceProps { + readonly errorHandler?: Effect.Effect, E, R> } -export interface ServiceResult extends Context.TagClass< +export interface ServiceResult extends Context.TagClass< Self, typeof id, QueryClient > { - readonly Default: Layer.Layer< - Self | (EH extends QueryErrorHandler.DefaultQueryErrorHandler ? EH : never), - never, - EH extends QueryErrorHandler.DefaultQueryErrorHandler ? never : EH - > + readonly Default: Layer.Layer } export const Service = () => ( - < - EH = QueryErrorHandler.DefaultQueryErrorHandler, - FallbackA = QueryErrorHandler.Fallback>, - HandledE = QueryErrorHandler.Error>, - >( - props?: ServiceProps - ): ServiceResult => { - const TagClass = Context.Tag(id)() as ServiceResult + ( + props?: ServiceProps + ): ServiceResult => { + const TagClass = Context.Tag(id)() as ServiceResult - (TagClass as Mutable).Default = Layer.effect(TagClass, Effect.Do.pipe( - Effect.bind("errorHandler", () => - (props?.ErrorHandler ?? QueryErrorHandler.DefaultQueryErrorHandler) as Effect.Effect< - QueryErrorHandler.QueryErrorHandler, - never, - EH extends QueryErrorHandler.DefaultQueryErrorHandler ? never : EH - > - ) - )).pipe( - Layer.provideMerge((props?.ErrorHandler - ? Layer.empty - : QueryErrorHandler.DefaultQueryErrorHandler.Default - ) as Layer.Layer) - ) + (TagClass as Mutable).Default = Layer.effect(TagClass, Effect.flatMap( + props?.errorHandler ?? QueryErrorHandler.make()(identity), + errorHandler => make({ errorHandler }), + )) return TagClass } diff --git a/packages/extension-query/src/QueryErrorHandler.ts b/packages/extension-query/src/QueryErrorHandler.ts index ebfa750..7cbed7d 100644 --- a/packages/extension-query/src/QueryErrorHandler.ts +++ b/packages/extension-query/src/QueryErrorHandler.ts @@ -1,5 +1,4 @@ -import { Cause, Context, Effect, identity, Layer, PubSub, Stream } from "effect" -import type { Mutable } from "effect/Types" +import { Cause, Effect, PubSub, Stream } from "effect" export interface QueryErrorHandler { @@ -11,55 +10,31 @@ export type Fallback = T extends QueryErrorHandler ? A : never export type Error = T extends QueryErrorHandler ? E : never -export interface ServiceResult< - Self, - Id extends string, - FallbackA, - HandledE, -> extends Context.TagClass< - Self, - Id, - QueryErrorHandler -> { - readonly Default: Layer.Layer -} - -export const Service = () => ( - ( - id: Id, +export const make = () => ( + ( f: ( self: Effect.Effect, failure: (failure: HandledE) => Effect.Effect, defect: (defect: unknown) => Effect.Effect, - ) => Effect.Effect, - ): ServiceResult => { - const TagClass = Context.Tag(id)() as ServiceResult + ) => Effect.Effect + ): Effect.Effect> => Effect.gen(function*() { + const pubsub = yield* PubSub.unbounded>() + const errors = Stream.fromPubSub(pubsub) - (TagClass as Mutable).Default = Layer.effect(TagClass, Effect.gen(function*() { - const pubsub = yield* PubSub.unbounded>() - const errors = Stream.fromPubSub(pubsub) + const handle = ( + self: Effect.Effect + ): Effect.Effect, R> => f( + self as unknown as Effect.Effect, + (failure: HandledE) => Effect.andThen( + PubSub.publish(pubsub, Cause.fail(failure)), + Effect.failCause(Cause.empty), + ), + (defect: unknown) => Effect.andThen( + PubSub.publish(pubsub, Cause.die(defect)), + Effect.failCause(Cause.empty), + ), + ) - const handle = ( - self: Effect.Effect - ): Effect.Effect, R> => f( - self as unknown as Effect.Effect, - (failure: HandledE) => PubSub.publish(pubsub, Cause.fail(failure)).pipe( - Effect.andThen(Effect.failCause(Cause.empty)) - ), - (defect: unknown) => PubSub.publish(pubsub, Cause.die(defect)).pipe( - Effect.andThen(Effect.failCause(Cause.empty)) - ), - ) - - return { errors, handle } - })) - - return TagClass - } + return { errors, handle } + }) ) - - -export class DefaultQueryErrorHandler extends Service()( - "@reffuse/extension-query/DefaultQueryErrorHandler", - identity, -) {}