132 Commits

Author SHA1 Message Date
Julien Valverdé b04860aa25 Cleanup
Lint / lint (push) Successful in 13s
2025-03-16 04:32:51 +01:00
Julien Valverdé e9e17ac211 Fix
Lint / lint (push) Successful in 12s
2025-03-16 04:11:25 +01:00
Julien Valverdé 1f0ff725ff Fix
Lint / lint (push) Successful in 13s
2025-03-16 04:05:39 +01:00
Julien Valverdé 447d89982c Fix
Lint / lint (push) Successful in 13s
2025-03-16 03:34:54 +01:00
Julien Valverdé 778ee27795 ErrorHandler refactoring
Lint / lint (push) Successful in 16s
2025-03-16 03:33:01 +01:00
Julien Valverdé 077816efb6 Fix
Lint / lint (push) Successful in 13s
2025-03-16 03:23:12 +01:00
Julien Valverdé e4bacd1ca7 Working QueryClient refactoring
Lint / lint (push) Successful in 13s
2025-03-16 03:19:12 +01:00
Julien Valverdé 0e2c0db28f QueryClient refactoring
Lint / lint (push) Successful in 13s
2025-03-16 02:52:49 +01:00
Julien Valverdé c943d81702 QueryClient.make
Lint / lint (push) Successful in 12s
2025-03-15 22:27:15 +01:00
Julien Valverdé c2bc406a5f Fixed query error handler
Lint / lint (push) Successful in 13s
2025-03-15 06:43:47 +01:00
Julien Valverdé 4e778b6c95 VQueryErrorHandler
Lint / lint (push) Successful in 12s
2025-03-15 05:12:38 +01:00
Julien Valverdé 0437fa5dcc QueryErrorHandler work
Lint / lint (push) Successful in 16s
2025-03-15 02:30:37 +01:00
Julien Valverdé 5614b8df38 Fix
Lint / lint (push) Successful in 13s
2025-03-15 00:52:00 +01:00
Julien Valverdé 70b6c4434e Tests
Lint / lint (push) Successful in 16s
2025-03-14 22:07:53 +01:00
Julien Valverdé 2e8dfbc988 QueryClient
Lint / lint (push) Successful in 13s
2025-03-14 22:00:53 +01:00
Julien Valverdé abc47c4647 Fix
Lint / lint (push) Failing after 12s
2025-03-14 05:04:49 +01:00
Julien Valverdé eedd2a7f2a makeTag
Lint / lint (push) Failing after 12s
2025-03-14 04:57:07 +01:00
Julien Valverdé f4ab575a8d QueryExtension work
Lint / lint (push) Failing after 13s
2025-03-14 04:24:56 +01:00
Julien Valverdé 747e2c6056 Done QueryClient
Lint / lint (push) Successful in 14s
2025-03-14 04:13:14 +01:00
Julien Valverdé 68c68417d8 QueryClient work
Lint / lint (push) Successful in 12s
2025-03-14 03:56:54 +01:00
Julien Valverdé ed384a62a8 QueryClient work
Lint / lint (push) Failing after 15s
2025-03-14 03:26:28 +01:00
Julien Valverdé 3a1748bb39 QueryClient tests
Lint / lint (push) Successful in 18s
2025-03-13 22:31:50 +01:00
Julien Valverdé 66b8fd2c2e Fix
Lint / lint (push) Successful in 13s
2025-03-12 06:37:39 +01:00
Julien Valverdé bc81c443ab Query work
Lint / lint (push) Successful in 13s
2025-03-11 21:19:57 +01:00
Julien Valverdé ee5dbe3766 Query work
Lint / lint (push) Successful in 13s
2025-03-11 20:39:56 +01:00
Julien Valverdé 825de84cef Cleanup
Lint / lint (push) Successful in 13s
2025-03-11 03:12:36 +01:00
Julien Valverdé d6011f7897 MutationRunner
Lint / lint (push) Failing after 18s
2025-03-11 02:17:50 +01:00
Julien Valverdé 8d4bce9e53 Merge branch 'next' of git.valverde.cloud:Thilawyn/reffuse into next
Lint / lint (push) Successful in 12s
2025-03-11 01:45:49 +01:00
Julien Valverdé f7dd4e51f5 Doc update
Lint / lint (push) Successful in 13s
Test build / test-build (pull_request) Successful in 16s
2025-03-11 01:36:13 +01:00
Julien Valverdé 8772e25ff5 CI update
Lint / lint (push) Successful in 13s
2025-03-10 18:44:08 +01:00
Julien Valverdé 94a0864132 Query refactoring
Lint / lint (push) Successful in 13s
2025-03-10 18:37:45 +01:00
Julien Valverdé be8098fb7d Query work
Lint / lint (push) Successful in 12s
2025-03-10 01:56:11 +01:00
Julien Valverdé 7021e604ed Cleanup
Lint / lint (push) Successful in 13s
2025-03-09 19:14:20 +01:00
Julien Valverdé 1fd2a9ffbe Cleanup
Lint / lint (push) Successful in 17s
2025-03-09 18:35:48 +01:00
Julien Valverdé 1ed73dc3ac Cleanup
Lint / lint (push) Successful in 13s
2025-03-09 18:21:35 +01:00
Julien Valverdé c689778cea Working query
Lint / lint (push) Successful in 15s
2025-03-09 18:08:52 +01:00
Julien Valverdé da2a32001c Query work
Lint / lint (push) Failing after 13s
2025-03-08 01:56:50 +01:00
Julien Valverdé 5ac3a932d9 Query work
Lint / lint (push) Failing after 12s
2025-03-07 23:17:32 +01:00
Julien Valverdé 7935293bc3 Query work
Lint / lint (push) Successful in 15s
2025-03-07 22:23:44 +01:00
Julien Valverdé cabceaffcd Key work
Lint / lint (push) Successful in 13s
2025-03-07 04:26:39 +01:00
Julien Valverdé d239a11cdc Service query
Lint / lint (push) Successful in 13s
2025-03-06 20:00:40 +01:00
Julien Valverdé fad61afce7 Fix
Lint / lint (push) Successful in 12s
2025-03-06 17:32:30 +01:00
Julien Valverdé 11fd4941c0 Fix
Lint / lint (push) Successful in 12s
2025-03-06 17:26:08 +01:00
Julien Valverdé 7bebc39a87 Fix
Lint / lint (push) Successful in 12s
2025-03-06 17:22:27 +01:00
Julien Valverdé 3bc0cc6586 Cleanup
Lint / lint (push) Successful in 12s
2025-03-06 03:31:21 +01:00
Julien Valverdé f99d18b846 Cleanup fix
Lint / lint (push) Successful in 13s
2025-03-06 03:15:43 +01:00
Julien Valverdé d61339ea6a Query work
Lint / lint (push) Successful in 12s
2025-03-05 02:23:43 +01:00
Julien Valverdé 3659d3f342 Version bump
Lint / lint (push) Successful in 13s
2025-03-05 01:50:08 +01:00
Julien Valverdé 1e8a5d412f Refresh on window focus
Lint / lint (push) Successful in 13s
2025-03-05 00:44:13 +01:00
Julien Valverdé 86539f33f0 Fix
Lint / lint (push) Successful in 13s
2025-03-05 00:24:38 +01:00
Julien Valverdé 8fa24b1791 Query work
Lint / lint (push) Successful in 13s
2025-03-05 00:16:13 +01:00
Julien Valverdé adaadf13b2 Working service query
Lint / lint (push) Successful in 13s
2025-03-04 23:18:35 +01:00
Julien Valverdé 3af7c3bf7a Query service work
Lint / lint (push) Failing after 11s
2025-03-04 22:44:40 +01:00
Julien Valverdé 00b7228073 Refetch on focus
Lint / lint (push) Successful in 13s
2025-03-04 02:15:31 +01:00
Julien Valverdé c2b2b1b96e Dependencies fix
Lint / lint (push) Successful in 13s
2025-03-04 02:04:29 +01:00
Julien Valverdé 74cf37e3a3 Query example
Lint / lint (push) Successful in 14s
2025-03-04 01:35:52 +01:00
Julien Valverdé 98091d4598 Refactoring
Lint / lint (push) Successful in 12s
2025-03-04 01:22:51 +01:00
Julien Valverdé b2f1626268 Working query
Lint / lint (push) Successful in 13s
2025-03-04 01:19:42 +01:00
Julien Valverdé 40e8bf6a1f Query work
Lint / lint (push) Successful in 13s
2025-03-03 19:42:33 +01:00
Julien Valverdé 9c96741c8e Fix
Lint / lint (push) Successful in 13s
2025-03-03 03:37:39 +01:00
Julien Valverdé 3fa9b7d821 Working query
Lint / lint (push) Successful in 13s
2025-03-02 20:14:45 +01:00
Julien Valverdé 6b0f2f33cb Query work
Lint / lint (push) Failing after 13s
2025-03-02 02:48:19 +01:00
Julien Valverdé 2e00db5778 Query work
Lint / lint (push) Failing after 43s
2025-03-02 01:11:18 +01:00
Julien Valverdé 660f32a171 Fix
Lint / lint (push) Successful in 13s
2025-02-28 17:24:40 +01:00
Julien Valverdé 3f2639fda1 Query work
Lint / lint (push) Successful in 13s
2025-02-28 16:08:08 +01:00
Julien Valverdé f76b3f333a Query work
Lint / lint (push) Successful in 13s
2025-02-28 02:13:23 +01:00
Julien Valverdé 3b407c6b4f Query work
Lint / lint (push) Successful in 17s
2025-02-28 01:06:11 +01:00
Julien Valverdé b01b95a9d5 Query work
Lint / lint (push) Successful in 12s
2025-02-28 00:27:22 +01:00
Julien Valverdé 91b95ea6af useRefState refactpro
Lint / lint (push) Successful in 13s
2025-02-27 18:32:57 +01:00
Julien Valverdé 7c99d1ff3d Working useQuery
Lint / lint (push) Successful in 13s
2025-02-27 01:19:09 +01:00
Julien Valverdé ae815553f2 Query work
Lint / lint (push) Successful in 13s
2025-02-26 23:57:03 +01:00
Julien Valverdé 86a96cbcce extension-query
Lint / lint (push) Failing after 7s
2025-02-26 21:17:09 +01:00
Julien Valverdé 538b3a415d Cleanup
Lint / lint (push) Successful in 12s
2025-02-26 20:50:05 +01:00
Julien Valverdé 5b023678f4 Merge branch 'master' into next
Lint / lint (push) Successful in 11s
2025-02-26 20:47:00 +01:00
Julien Valverdé 9266697aa4 Merge branch 'next' of git.valverde.cloud:Thilawyn/reffuse into next
Lint / lint (push) Successful in 12s
2025-02-26 19:29:48 +01:00
Julien Valverdé ad81bf9ed8 Cleanup
Lint / lint (push) Successful in 11s
Test build / test-build (pull_request) Successful in 13s
2025-02-26 19:25:21 +01:00
Julien Valverdé e92087e593 Turbo fix
Lint / lint (push) Successful in 12s
Test build / test-build (pull_request) Successful in 13s
2025-02-26 19:23:49 +01:00
Julien Valverdé e182e6ab5c README update
Lint / lint (push) Successful in 12s
Test build / test-build (pull_request) Failing after 15s
2025-02-26 19:13:49 +01:00
Julien Valverdé 89175be558 README work
Lint / lint (push) Successful in 12s
2025-02-26 14:30:59 +01:00
Julien Valverdé 4df90a0f1c README work
Lint / lint (push) Successful in 12s
2025-02-26 14:05:12 +01:00
Julien Valverdé 693c7b2db8 Reffuse context refactoring
Lint / lint (push) Successful in 12s
2025-02-26 13:40:52 +01:00
Julien Valverdé 5f60d03d83 Fix
Lint / lint (push) Successful in 12s
2025-02-25 23:19:44 +01:00
Julien Valverdé ea768218a0 Deps API change
Lint / lint (push) Successful in 12s
2025-02-25 23:11:58 +01:00
Julien Valverdé 3b4eb750ed Version bump
Lint / lint (push) Successful in 12s
2025-02-25 22:55:45 +01:00
Julien Valverdé 47aa130486 CI fix
Lint / lint (push) Successful in 12s
2025-02-25 22:53:07 +01:00
Julien Valverdé 02da3df8eb CI fix
Lint / lint (push) Successful in 12s
2025-02-25 22:45:55 +01:00
Julien Valverdé 8d276d2fbf Dependencies
Lint / lint (push) Failing after 8s
2025-02-25 22:16:53 +01:00
Julien Valverdé af077d34aa Turbo setup
Lint / lint (push) Failing after 13s
2025-02-25 22:07:18 +01:00
Julien Valverdé 618cee4028 Callback tests
Lint / lint (push) Failing after 11s
2025-02-25 18:39:19 +01:00
Julien Valverdé 8244c34d2a Callback helpers
Lint / lint (push) Failing after 10s
2025-02-25 18:29:00 +01:00
Julien Valverdé 523d835d00 Fix
Lint / lint (push) Failing after 10s
2025-02-25 17:14:07 +01:00
Julien Valverdé 15e96b8fa9 Merge branch 'plugins' of git.valverde.cloud:Thilawyn/reffuse into plugins
Lint / lint (push) Failing after 11s
2025-02-25 14:49:22 +01:00
Julien Valverdé 44de864713 API update 2025-02-25 14:48:58 +01:00
Julien Valverdé 8e1f0a27cf Lockfile
Lint / lint (push) Failing after 11s
2025-02-25 13:45:09 +01:00
Julien Valverdé 8754020323 Working lazyref extension
Lint / lint (push) Failing after 12s
2025-02-25 12:17:45 +01:00
Julien Valverdé d9a01dae0f withLazyRef
Lint / lint (push) Failing after 11s
2025-02-25 11:22:49 +01:00
Julien Valverdé 8873e81f7c Dependencies
Lint / lint (push) Successful in 10s
2025-02-25 10:31:43 +01:00
Julien Valverdé 38fcafb15c Dependencies
Lint / lint (push) Successful in 10s
2025-02-25 10:21:34 +01:00
Julien Valverdé 411397c7de Fix
Lint / lint (push) Successful in 12s
2025-02-24 21:30:13 +01:00
Julien Valverdé 85e7b54962 extension-lazyref
Lint / lint (push) Successful in 11s
2025-02-24 21:24:38 +01:00
Julien Valverdé ce3989ab77 Extension fix
Lint / lint (push) Successful in 10s
2025-02-24 21:09:44 +01:00
Julien Valverdé da0f6168f0 Fix
Lint / lint (push) Successful in 11s
2025-02-24 20:47:49 +01:00
Julien Valverdé 690dec1f1a Finalized
Lint / lint (push) Successful in 11s
2025-02-24 20:18:56 +01:00
Julien Valverdé 60274266da Extension work
Lint / lint (push) Failing after 10s
2025-02-24 20:00:02 +01:00
Julien Valverdé 28424b63cb Working extension
Lint / lint (push) Failing after 10s
2025-02-24 13:47:29 +01:00
Julien Valverdé e063eb06f7 Extension work
Lint / lint (push) Failing after 11s
2025-02-24 13:17:10 +01:00
Julien Valverdé fb5bb7fcef Cleanup
Lint / lint (push) Failing after 10s
2025-02-24 02:21:37 +01:00
Julien Valverdé 1f57f7d127 Tests
Lint / lint (push) Failing after 10s
2025-02-24 01:55:47 +01:00
Julien Valverdé e8742e5aa6 Fix
Lint / lint (push) Failing after 10s
2025-02-23 23:38:24 +01:00
Julien Valverdé be79d24d6e Tests
Lint / lint (push) Failing after 11s
2025-02-22 01:03:15 +01:00
Julien Valverdé e1349e5e03 Tests
Lint / lint (push) Failing after 11s
2025-02-21 15:44:28 +01:00
Julien Valverdé 837dcbb1cb Extension work
Lint / lint (push) Failing after 11s
2025-02-21 15:27:11 +01:00
Julien Valverdé 8252b6cbdf Extension work
Lint / lint (push) Failing after 10s
2025-02-21 05:22:19 +01:00
Julien Valverdé 256638bc06 ReffuseHelper
Lint / lint (push) Failing after 11s
2025-02-21 04:22:48 +01:00
Julien Valverdé c0097bbe81 Extension tests
Lint / lint (push) Successful in 10s
2025-02-20 14:57:46 +01:00
Julien Valverdé febeaa05d0 ReffuseExtension
Lint / lint (push) Successful in 11s
2025-02-20 14:12:56 +01:00
Julien Valverdé a71640d493 Cleanup
Lint / lint (push) Successful in 10s
2025-02-20 00:41:37 +01:00
Julien Valverdé b636a709f3 Tests
Lint / lint (push) Successful in 11s
2025-02-20 00:39:15 +01:00
Julien Valverdé fffbd01b5e Pipeable API tests
Lint / lint (push) Successful in 11s
2025-02-20 00:21:43 +01:00
Julien Valverdé 36d5414d10 Fix
Lint / lint (push) Successful in 44s
2025-02-19 23:59:34 +01:00
Julien Valverdé 65810a6d79 usePromise
Lint / lint (push) Successful in 10s
2025-02-19 23:44:02 +01:00
Julien Valverdé 9e7b30fbb4 useFork refactoring
Lint / lint (push) Successful in 11s
2025-02-19 23:24:15 +01:00
Julien Valverdé 6c843562ab usePromiseScoped fork implementation
Lint / lint (push) Successful in 10s
2025-02-18 23:47:32 +01:00
Julien Valverdé 809f512d11 Fix
Lint / lint (push) Successful in 10s
2025-02-18 22:33:49 +01:00
Julien Valverdé e71239b903 usePromiseScoped
Lint / lint (push) Successful in 10s
2025-02-18 22:28:49 +01:00
Julien Valverdé bfcc097882 usePromise
Lint / lint (push) Successful in 11s
2025-02-18 15:25:46 +01:00
Julien Valverdé 933b061b5d Promise tests
Lint / lint (push) Successful in 11s
2025-02-18 05:18:34 +01:00
Julien Valverdé 734c84824c Implement Pipeable
Lint / lint (push) Successful in 10s
2025-02-18 04:30:10 +01:00
Julien Valverdé e83e86f8f1 Promise tests
Lint / lint (push) Successful in 11s
2025-02-18 02:56:05 +01:00
Julien Valverdé bebbc1d7de Promise tests
Lint / lint (push) Successful in 11s
2025-02-18 02:23:40 +01:00
Julien Valverdé a7a0951b61 Dependencies fix
Lint / lint (push) Successful in 11s
2025-02-18 01:08:26 +01:00
Julien Valverdé 1b1a1961bc Dependencies upgrade
Lint / lint (push) Failing after 13s
2025-02-17 00:16:41 +01:00
13 changed files with 344 additions and 70 deletions
@@ -0,0 +1,50 @@
import { AlertDialog, Button, Flex, Text } from "@radix-ui/themes"
import { ErrorHandler } from "@reffuse/extension-query"
import { Cause, Chunk, Context, Effect, Match, Option, Stream } from "effect"
import { useState } from "react"
import { AppQueryErrorHandler } from "./query"
import { R } from "./reffuse"
export function VQueryErrorHandler() {
const [failure, setFailure] = useState(Option.none<Cause.Cause<
ErrorHandler.Error<Context.Tag.Service<AppQueryErrorHandler>>
>>())
R.useFork(() => AppQueryErrorHandler.pipe(Effect.flatMap(handler =>
Stream.runForEach(handler.errors, v => Effect.sync(() =>
setFailure(Option.some(v))
))
)), [])
return Option.match(failure, {
onSome: v => (
<AlertDialog.Root open>
<AlertDialog.Content maxWidth="450px">
<AlertDialog.Title>Error</AlertDialog.Title>
<AlertDialog.Description size="2">
{Cause.failures(v).pipe(
Chunk.head,
Option.getOrThrow,
Match.value,
Match.tag("RequestError", () => <Text>HTTP request error</Text>),
Match.tag("ResponseError", () => <Text>HTTP response error</Text>),
Match.exhaustive,
)}
</AlertDialog.Description>
<Flex gap="3" mt="4" justify="end">
<AlertDialog.Action>
<Button variant="solid" color="red" onClick={() => setFailure(Option.none())}>
Ok
</Button>
</AlertDialog.Action>
</Flex>
</AlertDialog.Content>
</AlertDialog.Root>
),
onNone: () => <></>,
})
}
+3
View File
@@ -5,6 +5,7 @@ import { Layer } from "effect"
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import { ReffuseRuntime } from "reffuse"
import { AppQueryClient, AppQueryErrorHandler } from "./query"
import { GlobalContext } from "./reffuse"
import { routeTree } from "./routeTree.gen"
@@ -14,6 +15,8 @@ const layer = Layer.empty.pipe(
Layer.provideMerge(Geolocation.layer),
Layer.provideMerge(Permissions.layer),
Layer.provideMerge(FetchHttpClient.layer),
Layer.provideMerge(AppQueryClient.Live),
Layer.provideMerge(AppQueryErrorHandler.Live),
)
const router = createRouter({ routeTree })
+9
View File
@@ -0,0 +1,9 @@
import { HttpClientError } from "@effect/platform"
import { ErrorHandler, QueryClient } from "@reffuse/extension-query"
export class AppQueryErrorHandler extends ErrorHandler.Service("AppQueryErrorHandler")<AppQueryErrorHandler,
HttpClientError.HttpClientError
>() {}
export class AppQueryClient extends QueryClient.Service({ ErrorHandler: AppQueryErrorHandler })<AppQueryClient>() {}
@@ -1,4 +1,3 @@
import { HttpClientError } from "@effect/platform"
import { QueryService } from "@reffuse/extension-query"
import { ParseResult, Schema } from "effect"
@@ -8,5 +7,5 @@ export const Result = Schema.Array(Schema.String)
export class Uuid4Query extends QueryService.Tag("Uuid4Query")<Uuid4Query,
readonly ["uuid4", number],
typeof Result.Type,
HttpClientError.HttpClientError | ParseResult.ParseError
ParseResult.ParseError
>() {}
+3
View File
@@ -3,6 +3,7 @@ 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, AppQueryErrorHandler } from "./query"
export const GlobalContext = ReffuseContext.make<
@@ -10,6 +11,8 @@ export const GlobalContext = ReffuseContext.make<
| Geolocation.Geolocation
| Permissions.Permissions
| HttpClient.HttpClient
| AppQueryClient
| AppQueryErrorHandler
>()
export class GlobalReffuse extends Reffuse.Reffuse.pipe(
+3
View File
@@ -1,3 +1,4 @@
import { VQueryErrorHandler } from "@/QueryErrorHandler"
import { Container, Flex, Theme } from "@radix-ui/themes"
import { createRootRoute, Link, Outlet } from "@tanstack/react-router"
import { TanStackRouterDevtools } from "@tanstack/router-devtools"
@@ -26,6 +27,8 @@ function Root() {
</Container>
<Outlet />
<VQueryErrorHandler />
<TanStackRouterDevtools />
</Theme>
)
@@ -0,0 +1,37 @@
import { type Cause, Context, Effect, Layer, Queue, Stream } from "effect"
import type { Mutable } from "effect/Types"
export interface ErrorHandler<E> {
readonly errors: Stream.Stream<Cause.Cause<E>>
readonly handle: <A, SelfE, R>(self: Effect.Effect<A, SelfE, R>) => Effect.Effect<A, Exclude<SelfE, E>, R>
}
export type Error<T> = T extends ErrorHandler<infer E> ? E : never
export interface ServiceResult<Self, Id extends string, E> extends Context.TagClass<Self, Id, ErrorHandler<E>> {
readonly Live: Layer.Layer<Self>
}
export const Service = <const Id extends string>(id: Id) => (
<Self, E = never>(): ServiceResult<Self, Id, E> => {
const TagClass = Context.Tag(id)() as ServiceResult<Self, Id, E>
(TagClass as Mutable<typeof TagClass>).Live = Layer.effect(TagClass, Effect.gen(function*() {
const queue = yield* Queue.unbounded<Cause.Cause<E>>()
const errors = Stream.fromQueue(queue)
const handle = <A, SelfE, R>(
self: Effect.Effect<A, SelfE, R>
) => Effect.tapErrorCause(self, cause =>
Queue.offer(queue, cause as Cause.Cause<E>)
) as Effect.Effect<A, Exclude<SelfE, E>, R>
return { errors, handle }
}))
return TagClass
}
)
export class DefaultErrorHandler extends Service("@reffuse/extension-query/DefaultErrorHandler")<DefaultErrorHandler>() {}
@@ -0,0 +1,134 @@
// import { BrowserStream } from "@effect/platform-browser"
// import * as AsyncData from "@typed/async-data"
// import { type Cause, Effect, Fiber, identity, Option, Ref, type Scope, Stream, SubscriptionRef } from "effect"
// export interface MutationRunner<K extends readonly unknown[], A, E, R> {
// readonly mutation: (...args: K) => Effect.Effect<A, E, R>
// readonly stateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
// readonly forkMutate: Effect.Effect<Fiber.RuntimeFiber<void>>
// }
// export interface MakeProps<K extends readonly unknown[], A, E, R> {
// readonly mutation: (...args: K) => Effect.Effect<A, E, R>
// }
// export const make = <K extends readonly unknown[], A, E, R>(
// { key, query }: MakeProps<K, A, E, R>
// ): Effect.Effect<MutationRunner<K, A, E, R>, never, R> => Effect.gen(function*() {
// const context = yield* Effect.context<R>()
// const stateRef = yield* SubscriptionRef.make(AsyncData.noData<A, E>())
// const interrupt = fiberRef.pipe(
// Effect.flatMap(Option.match({
// onSome: fiber => Ref.set(fiberRef, Option.none()).pipe(
// Effect.andThen(Fiber.interrupt(fiber))
// ),
// onNone: () => Effect.void,
// }))
// )
// const forkInterrupt = fiberRef.pipe(
// Effect.flatMap(Option.match({
// onSome: fiber => Ref.set(fiberRef, Option.none()).pipe(
// Effect.andThen(Fiber.interrupt(fiber).pipe(
// Effect.asVoid,
// Effect.forkDaemon,
// ))
// ),
// onNone: () => Effect.forkDaemon(Effect.void),
// }))
// )
// const forkFetch = interrupt.pipe(
// Effect.andThen(
// Ref.set(stateRef, AsyncData.loading()).pipe(
// Effect.andThen(latestKeyRef),
// Effect.flatMap(identity),
// Effect.flatMap(key => query(key).pipe(
// Effect.matchCauseEffect({
// onSuccess: v => Ref.set(stateRef, AsyncData.success(v)),
// onFailure: c => Ref.set(stateRef, AsyncData.failure(c)),
// })
// )),
// Effect.provide(context),
// Effect.fork,
// )
// ),
// Effect.flatMap(fiber =>
// Ref.set(fiberRef, Option.some(fiber)).pipe(
// Effect.andThen(Fiber.join(fiber)),
// Effect.andThen(Ref.set(fiberRef, Option.none())),
// )
// ),
// Effect.forkDaemon,
// )
// const forkRefresh = interrupt.pipe(
// Effect.andThen(
// Ref.update(stateRef, previous => {
// if (AsyncData.isSuccess(previous) || AsyncData.isFailure(previous))
// return AsyncData.refreshing(previous)
// if (AsyncData.isRefreshing(previous))
// return AsyncData.refreshing(previous.previous)
// return AsyncData.loading()
// }).pipe(
// Effect.andThen(latestKeyRef),
// Effect.flatMap(identity),
// Effect.flatMap(key => query(key).pipe(
// Effect.matchCauseEffect({
// onSuccess: v => Ref.set(stateRef, AsyncData.success(v)),
// onFailure: c => Ref.set(stateRef, AsyncData.failure(c)),
// })
// )),
// Effect.provide(context),
// Effect.fork,
// )
// ),
// Effect.flatMap(fiber =>
// Ref.set(fiberRef, Option.some(fiber)).pipe(
// Effect.andThen(Fiber.join(fiber)),
// Effect.andThen(Ref.set(fiberRef, Option.none())),
// )
// ),
// Effect.forkDaemon,
// )
// const fetchOnKeyChange = Effect.addFinalizer(() => interrupt).pipe(
// Effect.andThen(Stream.runForEach(key, latestKey =>
// Ref.set(latestKeyRef, Option.some(latestKey)).pipe(
// Effect.andThen(forkFetch)
// )
// ))
// )
// const refreshOnWindowFocus = Stream.runForEach(
// BrowserStream.fromEventListenerWindow("focus"),
// () => forkRefresh,
// )
// return {
// query,
// latestKeyRef,
// stateRef,
// fiberRef,
// forkInterrupt,
// forkFetch,
// forkRefresh,
// fetchOnKeyChange,
// refreshOnWindowFocus,
// }
// })
@@ -0,0 +1,39 @@
import { Context, Layer } from "effect"
import type { Mutable } from "effect/Types"
import * as ErrorHandler from "./ErrorHandler.js"
export interface QueryClient<EH, HandledE> {
readonly ErrorHandler: Context.Tag<EH, ErrorHandler.ErrorHandler<HandledE>>
}
const id = "@reffuse/extension-query/QueryClient"
export type TagClassShape<EH, HandledE> = Context.TagClassShape<typeof id, QueryClient<EH, HandledE>>
export type GenericTagClass<EH, HandledE> = Context.TagClass<TagClassShape<EH, HandledE>, typeof id, QueryClient<EH, HandledE>>
export const makeGenericTagClass = <EH = never, HandledE = never>(): GenericTagClass<EH, HandledE> => Context.Tag(id)()
export interface ServiceProps<EH, HandledE> {
readonly ErrorHandler?: Context.Tag<EH, ErrorHandler.ErrorHandler<HandledE>>
}
export interface ServiceResult<Self, EH, HandledE> extends Context.TagClass<Self, typeof id, QueryClient<EH, HandledE>> {
readonly Live: Layer.Layer<Self>
}
export const Service = <
EH = ErrorHandler.DefaultErrorHandler,
HandledE = ErrorHandler.Error<Context.Tag.Service<ErrorHandler.DefaultErrorHandler>>,
>(
props?: ServiceProps<EH, HandledE>
) => (
<Self>(): ServiceResult<Self, EH, HandledE> => {
const TagClass = Context.Tag(id)() as ServiceResult<Self, EH, HandledE>
(TagClass as Mutable<typeof TagClass>).Live = Layer.succeed(TagClass, {
ErrorHandler: (props?.ErrorHandler ?? ErrorHandler.DefaultErrorHandler) as Context.Tag<EH, ErrorHandler.ErrorHandler<HandledE>>
})
return TagClass
}
)
+14 -4
View File
@@ -2,6 +2,7 @@ import type * as AsyncData from "@typed/async-data"
import { type Cause, type Context, Effect, type Fiber, Layer, type Option, type Stream, type SubscriptionRef } from "effect"
import * as React from "react"
import { ReffuseExtension, type ReffuseHelpers } from "reffuse"
import * as QueryClient from "./QueryClient.js"
import * as QueryRunner from "./QueryRunner.js"
import type * as QueryService from "./QueryService.js"
@@ -24,11 +25,20 @@ export interface UseQueryResult<K extends readonly unknown[], A, E> {
export const QueryExtension = ReffuseExtension.make(() => ({
useQuery<K extends readonly unknown[], A, E, R>(
this: ReffuseHelpers.ReffuseHelpers<R>,
props: UseQueryProps<K, A, E, R>,
): UseQueryResult<K, A, E> {
useQuery<
EH,
QK extends readonly unknown[],
QA,
QE,
HandledE,
QR extends R,
R,
>(
this: ReffuseHelpers.ReffuseHelpers<R | QueryClient.TagClassShape<EH, HandledE> | EH>,
props: UseQueryProps<QK, QA, QE, QR>,
): UseQueryResult<QK, QA, Exclude<QE, HandledE>> {
const runner = this.useMemo(() => QueryRunner.make({
QueryClient: QueryClient.makeGenericTagClass<EH, HandledE>(),
key: props.key,
query: props.query,
}), [props.key])
+49 -45
View File
@@ -1,10 +1,11 @@
import { BrowserStream } from "@effect/platform-browser"
import * as AsyncData from "@typed/async-data"
import { type Cause, Effect, Fiber, identity, Option, Ref, type Scope, Stream, SubscriptionRef } from "effect"
import { type Cause, type Context, Effect, Fiber, identity, Option, Ref, type Scope, Stream, SubscriptionRef } from "effect"
import type * as QueryClient from "./QueryClient.js"
export interface QueryRunner<K extends readonly unknown[], A, E, R> {
readonly query: (key: K) => Effect.Effect<A, E, R>
readonly context: Context.Context<R>
readonly latestKeyRef: SubscriptionRef.SubscriptionRef<Option.Option<K>>
readonly stateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
@@ -19,18 +20,27 @@ export interface QueryRunner<K extends readonly unknown[], A, E, R> {
}
export interface MakeProps<K extends readonly unknown[], A, E, R> {
export interface MakeProps<EH, K extends readonly unknown[], A, E, HandledE, R> {
readonly QueryClient: QueryClient.GenericTagClass<EH, HandledE>
readonly key: Stream.Stream<K>
readonly query: (key: K) => Effect.Effect<A, E, R>
}
export const make = <K extends readonly unknown[], A, E, R>(
{ key, query }: MakeProps<K, A, E, R>
): Effect.Effect<QueryRunner<K, A, E, R>, never, R> => Effect.gen(function*() {
const context = yield* Effect.context<R>()
export const make = <EH, K extends readonly unknown[], A, E, HandledE, R>(
{
QueryClient,
key,
query,
}: MakeProps<EH, K, A, E, HandledE, R>
): Effect.Effect<
QueryRunner<K, A, Exclude<E, HandledE>, R>,
never,
R | QueryClient.TagClassShape<EH, HandledE> | EH
> => Effect.gen(function*() {
const context = yield* Effect.context<R | QueryClient.TagClassShape<EH, HandledE> | EH>()
const latestKeyRef = yield* SubscriptionRef.make(Option.none<K>())
const stateRef = yield* SubscriptionRef.make(AsyncData.noData<A, E>())
const stateRef = yield* SubscriptionRef.make(AsyncData.noData<A, Exclude<E, HandledE>>())
const fiberRef = yield* SubscriptionRef.make(Option.none<Fiber.RuntimeFiber<void, Cause.NoSuchElementException>>())
const interrupt = fiberRef.pipe(
@@ -54,22 +64,27 @@ export const make = <K extends readonly unknown[], A, E, R>(
}))
)
const forkFetch = interrupt.pipe(
Effect.andThen(
Ref.set(stateRef, AsyncData.loading()).pipe(
Effect.andThen(latestKeyRef),
Effect.flatMap(identity),
Effect.flatMap(key => query(key).pipe(
Effect.matchCauseEffect({
onSuccess: v => Ref.set(stateRef, AsyncData.success(v)),
onFailure: c => Ref.set(stateRef, AsyncData.failure(c)),
})
)),
const run = QueryClient.pipe(
Effect.flatMap(client => client.ErrorHandler),
Effect.flatMap(errorHandler => latestKeyRef.pipe(
Effect.flatMap(identity),
Effect.flatMap(key => query(key).pipe(
errorHandler.handle,
Effect.matchCauseEffect({
onSuccess: v => Ref.set(stateRef, AsyncData.success(v)),
onFailure: c => Ref.set(stateRef, AsyncData.failure(c)),
}),
)),
)),
Effect.provide(context),
Effect.fork,
)
),
Effect.provide(context),
)
const forkFetch = interrupt.pipe(
Effect.andThen(Ref.set(stateRef, AsyncData.loading()).pipe(
Effect.andThen(run),
Effect.fork,
)),
Effect.flatMap(fiber =>
Ref.set(fiberRef, Option.some(fiber)).pipe(
@@ -82,27 +97,16 @@ export const make = <K extends readonly unknown[], A, E, R>(
)
const forkRefresh = interrupt.pipe(
Effect.andThen(
Ref.update(stateRef, previous => {
if (AsyncData.isSuccess(previous) || AsyncData.isFailure(previous))
return AsyncData.refreshing(previous)
if (AsyncData.isRefreshing(previous))
return AsyncData.refreshing(previous.previous)
return AsyncData.loading()
}).pipe(
Effect.andThen(latestKeyRef),
Effect.flatMap(identity),
Effect.flatMap(key => query(key).pipe(
Effect.matchCauseEffect({
onSuccess: v => Ref.set(stateRef, AsyncData.success(v)),
onFailure: c => Ref.set(stateRef, AsyncData.failure(c)),
})
)),
Effect.provide(context),
Effect.fork,
)
),
Effect.andThen(Ref.update(stateRef, previous => {
if (AsyncData.isSuccess(previous) || AsyncData.isFailure(previous))
return AsyncData.refreshing(previous)
if (AsyncData.isRefreshing(previous))
return AsyncData.refreshing(previous.previous)
return AsyncData.loading()
}).pipe(
Effect.andThen(run),
Effect.fork,
)),
Effect.flatMap(fiber =>
Ref.set(fiberRef, Option.some(fiber)).pipe(
@@ -128,7 +132,7 @@ export const make = <K extends readonly unknown[], A, E, R>(
)
return {
query,
context,
latestKeyRef,
stateRef,
@@ -11,22 +11,3 @@ export interface QueryService<K extends readonly unknown[], A, E> {
export const Tag = <const Id extends string>(id: Id) => <
Self, K extends readonly unknown[], A, E = never,
>() => Effect.Tag(id)<Self, QueryService<K, A, E>>()
// export interface LayerProps<A, E, R> {
// readonly query: Effect.Effect<A, E, R>
// }
// export const layer = <Self, Id extends string, A, E, R>(
// tag: Context.TagClass<Self, Id, QueryService<A, E>>,
// props: LayerProps<A, E, R>,
// ): Layer.Layer<Self, never, R> => Layer.effect(tag, Effect.gen(function*() {
// const runner = yield* QueryRunner.make({
// query: props.query
// })
// return {
// state: runner.stateRef,
// refresh: runner.forkRefresh,
// }
// }))
+2
View File
@@ -1,3 +1,5 @@
export * as ErrorHandler from "./ErrorHandler.js"
export * as QueryClient from "./QueryClient.js"
export * from "./QueryExtension.js"
export * as QueryRunner from "./QueryRunner.js"
export * as QueryService from "./QueryService.js"