Compare commits
1 Commits
ca448c6bae
...
3242f64ef4
| Author | SHA1 | Date | |
|---|---|---|---|
| 3242f64ef4 |
4
bun.lock
4
bun.lock
@@ -5,7 +5,7 @@
|
|||||||
"name": "@effect-fc/monorepo",
|
"name": "@effect-fc/monorepo",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.2.5",
|
"@biomejs/biome": "^2.2.5",
|
||||||
"@effect/language-service": "^0.52.0",
|
"@effect/language-service": "^0.51.0",
|
||||||
"@types/bun": "^1.2.23",
|
"@types/bun": "^1.2.23",
|
||||||
"npm-check-updates": "^19.0.0",
|
"npm-check-updates": "^19.0.0",
|
||||||
"npm-sort": "^0.0.4",
|
"npm-sort": "^0.0.4",
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
|
|
||||||
"@effect-fc/example": ["@effect-fc/example@workspace:packages/example"],
|
"@effect-fc/example": ["@effect-fc/example@workspace:packages/example"],
|
||||||
|
|
||||||
"@effect/language-service": ["@effect/language-service@0.52.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-1azwmMvg5UhJ2HgJ7iNhdbrbCvXgPNvszjOKBZmxEWLUSlvzki/e0JX33nz6pW15GTO2ZkuCf2ExwnsFX9atnQ=="],
|
"@effect/language-service": ["@effect/language-service@0.51.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-JdKKw7EP5Na9ZdvQgs3rHBsrWAvvMh99yAaB0jgX7kKEkx0L8mdIndxtOqc3DrZ/H6qJxCLSjxa873Z5TEecVg=="],
|
||||||
|
|
||||||
"@effect/platform": ["@effect/platform@0.92.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.18.1" } }, "sha512-XXWCBVwyhaKZISN7aM1fv/3fWDGyxr84ObywnUrL8aHvJLoIeskWFAP/fqw3c5MFCrJ3ZV97RWLbv6JiBQugdg=="],
|
"@effect/platform": ["@effect/platform@0.92.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.18.1" } }, "sha512-XXWCBVwyhaKZISN7aM1fv/3fWDGyxr84ObywnUrL8aHvJLoIeskWFAP/fqw3c5MFCrJ3ZV97RWLbv6JiBQugdg=="],
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.2.5",
|
"@biomejs/biome": "^2.2.5",
|
||||||
"@effect/language-service": "^0.52.0",
|
"@effect/language-service": "^0.51.0",
|
||||||
"@types/bun": "^1.2.23",
|
"@types/bun": "^1.2.23",
|
||||||
"npm-check-updates": "^19.0.0",
|
"npm-check-updates": "^19.0.0",
|
||||||
"npm-sort": "^0.0.4",
|
"npm-sort": "^0.0.4",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */
|
/** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */
|
||||||
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
||||||
import { Context, type Duration, Effect, Effectable, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, Layer, ManagedRuntime, Option, Predicate, Ref, Runtime, Scope, Stream, Tracer, type Utils } from "effect"
|
import { Context, type Duration, Effect, Effectable, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, Layer, ManagedRuntime, Option, Predicate, Ref, Runtime, Scope, Stream, Tracer, type Types, type Utils } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Memoized } from "./index.js"
|
import { Memoized } from "./index.js"
|
||||||
import * as Result from "./Result.js"
|
import * as Result from "./Result.js"
|
||||||
@@ -387,9 +387,9 @@ export const withOptions: {
|
|||||||
export const withRuntime: {
|
export const withRuntime: {
|
||||||
<P extends {}, A extends React.ReactNode, E, R>(
|
<P extends {}, A extends React.ReactNode, E, R>(
|
||||||
context: React.Context<Runtime.Runtime<R>>,
|
context: React.Context<Runtime.Runtime<R>>,
|
||||||
): (self: Component<P, A, E, Scope.Scope | NoInfer<R>>) => (props: P) => A
|
): (self: Component<P, A, E, Types.NoInfer<R>>) => (props: P) => A
|
||||||
<P extends {}, A extends React.ReactNode, E, R>(
|
<P extends {}, A extends React.ReactNode, E, R>(
|
||||||
self: Component<P, A, E, Scope.Scope | NoInfer<R>>,
|
self: Component<P, A, E, Types.NoInfer<R>>,
|
||||||
context: React.Context<Runtime.Runtime<R>>,
|
context: React.Context<Runtime.Runtime<R>>,
|
||||||
): (props: P) => A
|
): (props: P) => A
|
||||||
} = Function.dual(2, <P extends {}, A extends React.ReactNode, E, R>(
|
} = Function.dual(2, <P extends {}, A extends React.ReactNode, E, R>(
|
||||||
@@ -403,7 +403,7 @@ export const withRuntime: {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
export class ScopeMap extends Effect.Service<ScopeMap>()("@effect-fc/Component/ScopeMap", {
|
export class ScopeMap extends Effect.Service<ScopeMap>()("effect-fc/Component/ScopeMap", {
|
||||||
effect: Effect.bind(Effect.Do, "ref", () => Ref.make(HashMap.empty<object, ScopeMap.Entry>()))
|
effect: Effect.bind(Effect.Do, "ref", () => Ref.make(HashMap.empty<object, ScopeMap.Entry>()))
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Cause, Context, Data, Effect, Equal, Exit, Hash, Layer, Match, Option, Pipeable, Predicate, pipe, Queue, Ref, Scope } from "effect"
|
import { Cause, Context, Data, Effect, Equal, Exit, Hash, Layer, Match, Option, Pipeable, Predicate, pipe, Queue, Ref, type Scope } from "effect"
|
||||||
|
|
||||||
|
|
||||||
export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result")
|
export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result")
|
||||||
@@ -177,46 +177,6 @@ export const makeProgressLayer = <A, E, P = never>(
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
export namespace forkEffect {
|
|
||||||
export type InputContext<R, P> = R extends Progress<infer X> ? [X] extends [P] ? R : never : R
|
|
||||||
export type OutputContext<R> = Scope.Scope | Exclude<R, Progress<any> | Progress<never>>
|
|
||||||
|
|
||||||
export interface Options<P> {
|
|
||||||
readonly initialProgress?: P
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const forkEffect = <A, E, R, P = never>(
|
|
||||||
effect: Effect.Effect<A, E, forkEffect.InputContext<R, NoInfer<P>>>,
|
|
||||||
options?: forkEffect.Options<P>,
|
|
||||||
): Effect.Effect<
|
|
||||||
Queue.Dequeue<Result<A, E, P>>,
|
|
||||||
never,
|
|
||||||
forkEffect.OutputContext<R>
|
|
||||||
> => Effect.Do.pipe(
|
|
||||||
Effect.bind("scope", () => Scope.Scope),
|
|
||||||
Effect.bind("queue", () => Queue.unbounded<Result<A, E, P>>()),
|
|
||||||
Effect.bind("ref", () => Ref.make<Result<A, E, P>>(initial())),
|
|
||||||
Effect.tap(({ queue, ref }) => Effect.andThen(ref, v => Queue.offer(queue, v))),
|
|
||||||
Effect.tap(({ scope, queue, ref }) => Effect.forkScoped(
|
|
||||||
Effect.addFinalizer(() => Queue.shutdown(queue)).pipe(
|
|
||||||
Effect.andThen(Effect.succeed(running(options?.initialProgress)).pipe(
|
|
||||||
Effect.tap(v => Ref.set(ref, v)),
|
|
||||||
Effect.tap(v => Queue.offer(queue, v)),
|
|
||||||
)),
|
|
||||||
Effect.andThen(Effect.provideService(effect, Scope.Scope, scope)),
|
|
||||||
Effect.exit,
|
|
||||||
Effect.andThen(exit => Effect.succeed(fromExit(exit)).pipe(
|
|
||||||
Effect.tap(v => Ref.set(ref, v)),
|
|
||||||
Effect.tap(v => Queue.offer(queue, v)),
|
|
||||||
)),
|
|
||||||
Effect.scoped,
|
|
||||||
Effect.provide(makeProgressLayer(queue, ref)),
|
|
||||||
)
|
|
||||||
)),
|
|
||||||
Effect.map(({ queue }) => queue),
|
|
||||||
) as Effect.Effect<Queue.Queue<Result<A, E, P>>, never, Scope.Scope>
|
|
||||||
|
|
||||||
export namespace forkEffectScoped {
|
export namespace forkEffectScoped {
|
||||||
export type InputContext<R, P> = (R extends Progress<infer X>
|
export type InputContext<R, P> = (R extends Progress<infer X>
|
||||||
? [X] extends [P]
|
? [X] extends [P]
|
||||||
@@ -240,17 +200,16 @@ export const forkEffectScoped = <A, E, R, P = never>(
|
|||||||
never,
|
never,
|
||||||
forkEffectScoped.OutputContext<R>
|
forkEffectScoped.OutputContext<R>
|
||||||
> => Effect.Do.pipe(
|
> => Effect.Do.pipe(
|
||||||
Effect.bind("scope", () => Scope.Scope),
|
|
||||||
Effect.bind("queue", () => Queue.unbounded<Result<A, E, P>>()),
|
Effect.bind("queue", () => Queue.unbounded<Result<A, E, P>>()),
|
||||||
Effect.bind("ref", () => Ref.make<Result<A, E, P>>(initial())),
|
Effect.bind("ref", () => Ref.make<Result<A, E, P>>(initial())),
|
||||||
Effect.tap(({ queue, ref }) => Effect.andThen(ref, v => Queue.offer(queue, v))),
|
Effect.tap(({ queue, ref }) => Effect.andThen(ref, v => Queue.offer(queue, v))),
|
||||||
Effect.tap(({ scope, queue, ref }) => Effect.forkScoped(
|
Effect.tap(({ queue, ref }) => Effect.forkScoped(
|
||||||
Effect.addFinalizer(() => Queue.shutdown(queue)).pipe(
|
Effect.addFinalizer(() => Queue.shutdown(queue)).pipe(
|
||||||
Effect.andThen(Effect.succeed(running(options?.initialProgress)).pipe(
|
Effect.andThen(Effect.succeed(running(options?.initialProgress)).pipe(
|
||||||
Effect.tap(v => Ref.set(ref, v)),
|
Effect.tap(v => Ref.set(ref, v)),
|
||||||
Effect.tap(v => Queue.offer(queue, v)),
|
Effect.tap(v => Queue.offer(queue, v)),
|
||||||
)),
|
)),
|
||||||
Effect.andThen(Effect.provideService(effect, Scope.Scope, scope)),
|
Effect.andThen(effect),
|
||||||
Effect.exit,
|
Effect.exit,
|
||||||
Effect.andThen(exit => Effect.succeed(fromExit(exit)).pipe(
|
Effect.andThen(exit => Effect.succeed(fromExit(exit)).pipe(
|
||||||
Effect.tap(v => Ref.set(ref, v)),
|
Effect.tap(v => Ref.set(ref, v)),
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||||
|
|
||||||
import { Route as rootRouteImport } from './routes/__root'
|
import { Route as rootRouteImport } from './routes/__root'
|
||||||
import { Route as ResultRouteImport } from './routes/result'
|
|
||||||
import { Route as FormRouteImport } from './routes/form'
|
import { Route as FormRouteImport } from './routes/form'
|
||||||
import { Route as BlankRouteImport } from './routes/blank'
|
import { Route as BlankRouteImport } from './routes/blank'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
@@ -17,11 +16,6 @@ import { Route as DevMemoRouteImport } from './routes/dev/memo'
|
|||||||
import { Route as DevContextRouteImport } from './routes/dev/context'
|
import { Route as DevContextRouteImport } from './routes/dev/context'
|
||||||
import { Route as DevAsyncRenderingRouteImport } from './routes/dev/async-rendering'
|
import { Route as DevAsyncRenderingRouteImport } from './routes/dev/async-rendering'
|
||||||
|
|
||||||
const ResultRoute = ResultRouteImport.update({
|
|
||||||
id: '/result',
|
|
||||||
path: '/result',
|
|
||||||
getParentRoute: () => rootRouteImport,
|
|
||||||
} as any)
|
|
||||||
const FormRoute = FormRouteImport.update({
|
const FormRoute = FormRouteImport.update({
|
||||||
id: '/form',
|
id: '/form',
|
||||||
path: '/form',
|
path: '/form',
|
||||||
@@ -57,7 +51,6 @@ export interface FileRoutesByFullPath {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/blank': typeof BlankRoute
|
'/blank': typeof BlankRoute
|
||||||
'/form': typeof FormRoute
|
'/form': typeof FormRoute
|
||||||
'/result': typeof ResultRoute
|
|
||||||
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
||||||
'/dev/context': typeof DevContextRoute
|
'/dev/context': typeof DevContextRoute
|
||||||
'/dev/memo': typeof DevMemoRoute
|
'/dev/memo': typeof DevMemoRoute
|
||||||
@@ -66,7 +59,6 @@ export interface FileRoutesByTo {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/blank': typeof BlankRoute
|
'/blank': typeof BlankRoute
|
||||||
'/form': typeof FormRoute
|
'/form': typeof FormRoute
|
||||||
'/result': typeof ResultRoute
|
|
||||||
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
||||||
'/dev/context': typeof DevContextRoute
|
'/dev/context': typeof DevContextRoute
|
||||||
'/dev/memo': typeof DevMemoRoute
|
'/dev/memo': typeof DevMemoRoute
|
||||||
@@ -76,7 +68,6 @@ export interface FileRoutesById {
|
|||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/blank': typeof BlankRoute
|
'/blank': typeof BlankRoute
|
||||||
'/form': typeof FormRoute
|
'/form': typeof FormRoute
|
||||||
'/result': typeof ResultRoute
|
|
||||||
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
||||||
'/dev/context': typeof DevContextRoute
|
'/dev/context': typeof DevContextRoute
|
||||||
'/dev/memo': typeof DevMemoRoute
|
'/dev/memo': typeof DevMemoRoute
|
||||||
@@ -87,7 +78,6 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/blank'
|
| '/blank'
|
||||||
| '/form'
|
| '/form'
|
||||||
| '/result'
|
|
||||||
| '/dev/async-rendering'
|
| '/dev/async-rendering'
|
||||||
| '/dev/context'
|
| '/dev/context'
|
||||||
| '/dev/memo'
|
| '/dev/memo'
|
||||||
@@ -96,7 +86,6 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/blank'
|
| '/blank'
|
||||||
| '/form'
|
| '/form'
|
||||||
| '/result'
|
|
||||||
| '/dev/async-rendering'
|
| '/dev/async-rendering'
|
||||||
| '/dev/context'
|
| '/dev/context'
|
||||||
| '/dev/memo'
|
| '/dev/memo'
|
||||||
@@ -105,7 +94,6 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/blank'
|
| '/blank'
|
||||||
| '/form'
|
| '/form'
|
||||||
| '/result'
|
|
||||||
| '/dev/async-rendering'
|
| '/dev/async-rendering'
|
||||||
| '/dev/context'
|
| '/dev/context'
|
||||||
| '/dev/memo'
|
| '/dev/memo'
|
||||||
@@ -115,7 +103,6 @@ export interface RootRouteChildren {
|
|||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
BlankRoute: typeof BlankRoute
|
BlankRoute: typeof BlankRoute
|
||||||
FormRoute: typeof FormRoute
|
FormRoute: typeof FormRoute
|
||||||
ResultRoute: typeof ResultRoute
|
|
||||||
DevAsyncRenderingRoute: typeof DevAsyncRenderingRoute
|
DevAsyncRenderingRoute: typeof DevAsyncRenderingRoute
|
||||||
DevContextRoute: typeof DevContextRoute
|
DevContextRoute: typeof DevContextRoute
|
||||||
DevMemoRoute: typeof DevMemoRoute
|
DevMemoRoute: typeof DevMemoRoute
|
||||||
@@ -123,13 +110,6 @@ export interface RootRouteChildren {
|
|||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
interface FileRoutesByPath {
|
interface FileRoutesByPath {
|
||||||
'/result': {
|
|
||||||
id: '/result'
|
|
||||||
path: '/result'
|
|
||||||
fullPath: '/result'
|
|
||||||
preLoaderRoute: typeof ResultRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
|
||||||
'/form': {
|
'/form': {
|
||||||
id: '/form'
|
id: '/form'
|
||||||
path: '/form'
|
path: '/form'
|
||||||
@@ -179,7 +159,6 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
BlankRoute: BlankRoute,
|
BlankRoute: BlankRoute,
|
||||||
FormRoute: FormRoute,
|
FormRoute: FormRoute,
|
||||||
ResultRoute: ResultRoute,
|
|
||||||
DevAsyncRenderingRoute: DevAsyncRenderingRoute,
|
DevAsyncRenderingRoute: DevAsyncRenderingRoute,
|
||||||
DevContextRoute: DevContextRoute,
|
DevContextRoute: DevContextRoute,
|
||||||
DevMemoRoute: DevMemoRoute,
|
DevMemoRoute: DevMemoRoute,
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import { HttpClient } from "@effect/platform"
|
|
||||||
import { Container, Heading, Text } from "@radix-ui/themes"
|
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
|
||||||
import { Effect, Match, Schema } from "effect"
|
|
||||||
import { Component } from "effect-fc"
|
|
||||||
import { runtime } from "@/runtime"
|
|
||||||
|
|
||||||
|
|
||||||
const Post = Schema.Struct({
|
|
||||||
userId: Schema.Int,
|
|
||||||
id: Schema.Int,
|
|
||||||
title: Schema.String,
|
|
||||||
body: Schema.String,
|
|
||||||
})
|
|
||||||
|
|
||||||
const Result = Component.makeUntraced("Result")(function*() {
|
|
||||||
const result = yield* Component.useOnMountResult(() => HttpClient.HttpClient.pipe(
|
|
||||||
Effect.andThen(client => client.get("https://jsonplaceholder.typicode.com/posts/1")),
|
|
||||||
Effect.andThen(Schema.decodeUnknown(Post)),
|
|
||||||
Effect.tap(Effect.sleep("250 millis")),
|
|
||||||
))
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
{Match.value(result).pipe(
|
|
||||||
Match.tag("Running", () => <Text>Loading...</Text>),
|
|
||||||
Match.tag("Success", result => <>
|
|
||||||
<Heading>{result.value.title}</Heading>
|
|
||||||
<Text>{result.value.body}</Text>
|
|
||||||
</>),
|
|
||||||
Match.tag("Failure", result =>
|
|
||||||
<Text>An error has occured: {result.cause.toString()}</Text>
|
|
||||||
),
|
|
||||||
Match.orElse(() => <></>),
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}).pipe(
|
|
||||||
Component.withRuntime(runtime.context)
|
|
||||||
)
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/result")({
|
|
||||||
component: Result
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user