Compare commits
23 Commits
5b64d0d783
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d81a9fcd91 | ||
|
|
45ce747ff0 | ||
|
|
e089bf9fee | ||
|
|
c2a1a7b212 | ||
|
|
4dc336fbf4 | ||
|
|
1fe2fec325 | ||
|
|
d8b40088cb | ||
|
|
38bf3f99ea | ||
|
|
30b72b5b52 | ||
|
|
3a8a1ed0c3 | ||
|
|
7013bed037 | ||
|
|
0b7a2dbe92 | ||
|
|
0d3e09354e | ||
|
|
dc46d03aab | ||
|
|
37ffc161d3 | ||
|
|
e8a267f4cb | ||
|
|
6dc0a548cd | ||
|
|
53c06e3dae | ||
|
|
82d154ac54 | ||
|
|
ed788af128 | ||
|
|
f4e380ddcb | ||
|
|
e58bd7ab5a | ||
|
|
21d011dd12 |
@@ -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))),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
HttpClientError.HttpClientError
|
||||
>()(
|
||||
"AppQueryErrorHandler",
|
||||
|
||||
(self, failure, defect) => self.pipe(
|
||||
Effect.catchTag("RequestError", "ResponseError", failure),
|
||||
Effect.catchAllDefect(defect),
|
||||
),
|
||||
) {}
|
||||
|
||||
export class AppQueryClient extends QueryClient.Service<AppQueryClient>()({ ErrorHandler: AppQueryErrorHandler }) {}
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,16 @@ import { Console, Effect, Option } from "effect"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
|
||||
interface Node {
|
||||
value: string
|
||||
left?: Leaf
|
||||
right?: Leaf
|
||||
}
|
||||
interface Leaf {
|
||||
node: Node
|
||||
}
|
||||
|
||||
|
||||
const makeUuid = Effect.provide(makeUuid4, GetRandomValues.CryptoRandom)
|
||||
|
||||
|
||||
@@ -32,6 +42,11 @@ function RouteComponent() {
|
||||
runSync,
|
||||
), [scopeLayer, runSync])
|
||||
|
||||
|
||||
const nodeRef = R.useRef(() => Effect.succeed<Node>({ value: "prout" }))
|
||||
const nodeValueRef = R.useSubRefFromPath(nodeRef, ["value"])
|
||||
|
||||
|
||||
return (
|
||||
<Flex direction="column" justify="center" align="center" gap="2">
|
||||
<Text>{uuid}</Text>
|
||||
|
||||
7
packages/example/src/services/AppQueryClient.ts
Normal file
7
packages/example/src/services/AppQueryClient.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { QueryClient } from "@reffuse/extension-query"
|
||||
import * as AppQueryErrorHandler from "./AppQueryErrorHandler"
|
||||
|
||||
|
||||
export class AppQueryClient extends QueryClient.Service<AppQueryClient>()({
|
||||
errorHandler: AppQueryErrorHandler.AppQueryErrorHandler
|
||||
}) {}
|
||||
13
packages/example/src/services/AppQueryErrorHandler.ts
Normal file
13
packages/example/src/services/AppQueryErrorHandler.ts
Normal file
@@ -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>()("AppQueryErrorHandler", {
|
||||
effect: QueryErrorHandler.make<HttpClientError.HttpClientError>()(
|
||||
(self, failure, defect) => self.pipe(
|
||||
Effect.catchTag("RequestError", "ResponseError", failure),
|
||||
Effect.catchAllDefect(defect),
|
||||
)
|
||||
)
|
||||
}) {}
|
||||
@@ -1 +1,2 @@
|
||||
export {}
|
||||
export * as AppQueryClient from "./AppQueryClient"
|
||||
export * as AppQueryErrorHandler from "./AppQueryErrorHandler"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@reffuse/extension-query",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"./README.md",
|
||||
|
||||
@@ -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<FallbackA, HandledE> {
|
||||
}
|
||||
|
||||
|
||||
export interface MakeProps<FallbackA, HandledE> {
|
||||
readonly errorHandler: QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>
|
||||
}
|
||||
|
||||
export const make = <FallbackA, HandledE>(
|
||||
{ errorHandler }: MakeProps<FallbackA, HandledE>
|
||||
): Effect.Effect<QueryClient<FallbackA, HandledE>> => Effect.Do.pipe(
|
||||
Effect.let("errorHandler", () => errorHandler)
|
||||
)
|
||||
|
||||
|
||||
const id = "@reffuse/extension-query/QueryClient"
|
||||
|
||||
export type TagClassShape<FallbackA, HandledE> = Context.TagClassShape<typeof id, QueryClient<FallbackA, HandledE>>
|
||||
@@ -19,46 +30,28 @@ export type GenericTagClass<FallbackA, HandledE> = Context.TagClass<
|
||||
export const makeGenericTagClass = <FallbackA = never, HandledE = never>(): GenericTagClass<FallbackA, HandledE> => Context.Tag(id)()
|
||||
|
||||
|
||||
export interface ServiceProps<EH, FallbackA, HandledE> {
|
||||
readonly ErrorHandler?: Context.Tag<EH, QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>>
|
||||
export interface ServiceProps<FallbackA = never, HandledE = never, E = never, R = never> {
|
||||
readonly errorHandler?: Effect.Effect<QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>, E, R>
|
||||
}
|
||||
|
||||
export interface ServiceResult<Self, EH, FallbackA, HandledE> extends Context.TagClass<
|
||||
export interface ServiceResult<Self, FallbackA, HandledE, E, R> extends Context.TagClass<
|
||||
Self,
|
||||
typeof id,
|
||||
QueryClient<FallbackA, HandledE>
|
||||
> {
|
||||
readonly Default: Layer.Layer<
|
||||
Self | (EH extends QueryErrorHandler.DefaultQueryErrorHandler ? EH : never),
|
||||
never,
|
||||
EH extends QueryErrorHandler.DefaultQueryErrorHandler ? never : EH
|
||||
>
|
||||
readonly Default: Layer.Layer<Self, E, R>
|
||||
}
|
||||
|
||||
export const Service = <Self>() => (
|
||||
<
|
||||
EH = QueryErrorHandler.DefaultQueryErrorHandler,
|
||||
FallbackA = QueryErrorHandler.Fallback<Context.Tag.Service<QueryErrorHandler.DefaultQueryErrorHandler>>,
|
||||
HandledE = QueryErrorHandler.Error<Context.Tag.Service<QueryErrorHandler.DefaultQueryErrorHandler>>,
|
||||
>(
|
||||
props?: ServiceProps<EH, FallbackA, HandledE>
|
||||
): ServiceResult<Self, EH, FallbackA, HandledE> => {
|
||||
const TagClass = Context.Tag(id)() as ServiceResult<Self, EH, FallbackA, HandledE>
|
||||
<FallbackA = never, HandledE = never, E = never, R = never>(
|
||||
props?: ServiceProps<FallbackA, HandledE, E, R>
|
||||
): ServiceResult<Self, FallbackA, HandledE, E, R> => {
|
||||
const TagClass = Context.Tag(id)() as ServiceResult<Self, FallbackA, HandledE, E, R>
|
||||
|
||||
(TagClass as Mutable<typeof TagClass>).Default = Layer.effect(TagClass, Effect.Do.pipe(
|
||||
Effect.bind("errorHandler", () =>
|
||||
(props?.ErrorHandler ?? QueryErrorHandler.DefaultQueryErrorHandler) as Effect.Effect<
|
||||
QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>,
|
||||
never,
|
||||
EH extends QueryErrorHandler.DefaultQueryErrorHandler ? never : EH
|
||||
>
|
||||
)
|
||||
)).pipe(
|
||||
Layer.provideMerge((props?.ErrorHandler
|
||||
? Layer.empty
|
||||
: QueryErrorHandler.DefaultQueryErrorHandler.Default
|
||||
) as Layer.Layer<EH>)
|
||||
)
|
||||
(TagClass as Mutable<typeof TagClass>).Default = Layer.effect(TagClass, Effect.flatMap(
|
||||
props?.errorHandler ?? QueryErrorHandler.make<never>()(identity),
|
||||
errorHandler => make({ errorHandler }),
|
||||
))
|
||||
|
||||
return TagClass
|
||||
}
|
||||
|
||||
@@ -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<FallbackA, HandledE> {
|
||||
@@ -11,31 +10,14 @@ export type Fallback<T> = T extends QueryErrorHandler<infer A, any> ? A : never
|
||||
export type Error<T> = T extends QueryErrorHandler<any, infer E> ? E : never
|
||||
|
||||
|
||||
export interface ServiceResult<
|
||||
Self,
|
||||
Id extends string,
|
||||
FallbackA,
|
||||
HandledE,
|
||||
> extends Context.TagClass<
|
||||
Self,
|
||||
Id,
|
||||
QueryErrorHandler<FallbackA, HandledE>
|
||||
> {
|
||||
readonly Default: Layer.Layer<Self>
|
||||
}
|
||||
|
||||
export const Service = <Self, HandledE = never>() => (
|
||||
<const Id extends string, FallbackA>(
|
||||
id: Id,
|
||||
export const make = <HandledE = never>() => (
|
||||
<FallbackA>(
|
||||
f: (
|
||||
self: Effect.Effect<never, HandledE>,
|
||||
failure: (failure: HandledE) => Effect.Effect<never>,
|
||||
defect: (defect: unknown) => Effect.Effect<never>,
|
||||
) => Effect.Effect<FallbackA>,
|
||||
): ServiceResult<Self, Id, FallbackA, HandledE> => {
|
||||
const TagClass = Context.Tag(id)() as ServiceResult<Self, Id, FallbackA, HandledE>
|
||||
|
||||
(TagClass as Mutable<typeof TagClass>).Default = Layer.effect(TagClass, Effect.gen(function*() {
|
||||
) => Effect.Effect<FallbackA>
|
||||
): Effect.Effect<QueryErrorHandler<FallbackA, HandledE>> => Effect.gen(function*() {
|
||||
const pubsub = yield* PubSub.unbounded<Cause.Cause<HandledE>>()
|
||||
const errors = Stream.fromPubSub(pubsub)
|
||||
|
||||
@@ -43,23 +25,16 @@ export const Service = <Self, HandledE = never>() => (
|
||||
self: Effect.Effect<A, E, R>
|
||||
): Effect.Effect<A | FallbackA, Exclude<E, HandledE>, R> => f(
|
||||
self as unknown as Effect.Effect<never, HandledE, never>,
|
||||
(failure: HandledE) => PubSub.publish(pubsub, Cause.fail(failure)).pipe(
|
||||
Effect.andThen(Effect.failCause(Cause.empty))
|
||||
(failure: HandledE) => Effect.andThen(
|
||||
PubSub.publish(pubsub, Cause.fail(failure)),
|
||||
Effect.failCause(Cause.empty),
|
||||
),
|
||||
(defect: unknown) => PubSub.publish(pubsub, Cause.die(defect)).pipe(
|
||||
Effect.andThen(Effect.failCause(Cause.empty))
|
||||
(defect: unknown) => Effect.andThen(
|
||||
PubSub.publish(pubsub, Cause.die(defect)),
|
||||
Effect.failCause(Cause.empty),
|
||||
),
|
||||
)
|
||||
|
||||
return { errors, handle }
|
||||
}))
|
||||
|
||||
return TagClass
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
export class DefaultQueryErrorHandler extends Service<DefaultQueryErrorHandler>()(
|
||||
"@reffuse/extension-query/DefaultQueryErrorHandler",
|
||||
identity,
|
||||
) {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "reffuse",
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.13",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"./README.md",
|
||||
|
||||
@@ -426,7 +426,7 @@ export abstract class ReffuseNamespace<R> {
|
||||
): SubscriptionSubRef.SubscriptionSubRef<PropertyPath.ValueFromPath<B, P>, B> {
|
||||
return React.useMemo(
|
||||
() => SubscriptionSubRef.makeFromPath(parent, path),
|
||||
[parent],
|
||||
[parent, ...path],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
import { Array, Function, Option, Predicate } from "effect"
|
||||
|
||||
|
||||
export type Paths<T> = [] | (
|
||||
T extends readonly any[] ? ArrayPaths<T> :
|
||||
T extends object ? ObjectPaths<T> :
|
||||
type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
export type Paths<T, D extends number = 5, Seen = never> = [] | (
|
||||
D extends never ? [] :
|
||||
T extends Seen ? [] :
|
||||
T extends readonly any[] ? ArrayPaths<T, D, Seen | T> :
|
||||
T extends object ? ObjectPaths<T, D, Seen | T> :
|
||||
never
|
||||
)
|
||||
|
||||
export type ArrayPaths<T extends readonly any[]> = {
|
||||
export type ArrayPaths<T extends readonly any[], D extends number, Seen> = {
|
||||
[K in keyof T as K extends number ? K : never]:
|
||||
| [K]
|
||||
| [K, ...Paths<T[K]>]
|
||||
| [K, ...Paths<T[K], Prev[D], Seen>]
|
||||
} extends infer O
|
||||
? O[keyof O]
|
||||
: never
|
||||
|
||||
export type ObjectPaths<T extends object> = {
|
||||
[K in keyof T as K extends string | number | symbol ? K : never]:
|
||||
| [K]
|
||||
| [K, ...Paths<T[K]>]
|
||||
export type ObjectPaths<T extends object, D extends number, Seen> = {
|
||||
[K in keyof T as K extends string | number | symbol ? K : never]-?:
|
||||
NonNullable<T[K]> extends infer V
|
||||
? [K] | [K, ...Paths<V, Prev[D], Seen>]
|
||||
: never
|
||||
} extends infer O
|
||||
? O[keyof O]
|
||||
: never
|
||||
|
||||
Reference in New Issue
Block a user