react-router-effect 1.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +548 -173
- package/dist/index.d.mts +2 -342
- package/dist/index.mjs +122 -34
- package/dist/rolldown-runtime-D8jQpDG7.mjs +12 -0
- package/dist/route-data-MgSnnR33.d.mts +543 -0
- package/dist/server.d.mts +218 -0
- package/dist/server.mjs +337 -0
- package/dist/vite.d.mts +27 -0
- package/dist/vite.mjs +46 -0
- package/package.json +22 -10
- package/src/action-meta.ts +58 -0
- package/src/check.ts +205 -0
- package/src/errors.ts +3 -3
- package/src/factory.ts +291 -84
- package/src/index.ts +22 -1
- package/src/route-data.ts +151 -0
- package/src/server-entry.ts +10 -0
- package/src/server.ts +647 -0
- package/src/vite-entry.ts +2 -0
- package/src/vite.ts +92 -0
package/src/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { actionMetaKey } from "./action-meta.ts";
|
|
2
|
+
export type { WithActionMeta } from "./action-meta.ts";
|
|
1
3
|
export {
|
|
2
4
|
isRouteError,
|
|
3
5
|
ReturnableDataError,
|
|
@@ -6,4 +8,23 @@ export {
|
|
|
6
8
|
} from "./errors.ts";
|
|
7
9
|
export type { AnyRouteError } from "./errors.ts";
|
|
8
10
|
export { makeEffectRouteFactory } from "./factory.ts";
|
|
9
|
-
export type {
|
|
11
|
+
export type {
|
|
12
|
+
ActionMetaArgs,
|
|
13
|
+
EffectMiddlewareArgs,
|
|
14
|
+
EffectNext,
|
|
15
|
+
ErrorHandler,
|
|
16
|
+
FactoryTypes,
|
|
17
|
+
RequestContextKey,
|
|
18
|
+
RouteHandlers,
|
|
19
|
+
RouteResult,
|
|
20
|
+
} from "./factory.ts";
|
|
21
|
+
export type {
|
|
22
|
+
BrandData,
|
|
23
|
+
EffectData,
|
|
24
|
+
EffectRouteArgs,
|
|
25
|
+
Register,
|
|
26
|
+
RegisteredMeta,
|
|
27
|
+
RegisteredRecover,
|
|
28
|
+
RouteData,
|
|
29
|
+
Serialize,
|
|
30
|
+
} from "./route-data.ts";
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { Effect } from "effect";
|
|
2
|
+
import type { UNSAFE_DataWithResponseInit, useLoaderData } from "react-router";
|
|
3
|
+
|
|
4
|
+
import type { WithActionMeta } from "./action-meta.ts";
|
|
5
|
+
import type { ReturnableDataError } from "./errors.ts";
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Route data of plain Effect loaders and actions.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Registers what the app's route factory turns failures into, so that a
|
|
13
|
+
* loader or action written as a plain `Effect.fn` types its data the way a
|
|
14
|
+
* `makeLoader` call would.
|
|
15
|
+
*
|
|
16
|
+
* A `makeLoader` call sees the factory's handlers as type arguments. A plain
|
|
17
|
+
* Effect loader does not, so without this an error that a handler recovers
|
|
18
|
+
* into data, such as a form error that becomes `{ reply }`, would be missing
|
|
19
|
+
* from `useLoaderData`, `useFetcher` and `Route.ComponentProps`.
|
|
20
|
+
*
|
|
21
|
+
* Declare the types directly rather than reading them from the factory's
|
|
22
|
+
* value: the factory's type depends on every Effect in the app, and so on
|
|
23
|
+
* this registration. The server adapter's `RouteEffectCheck` fails when the
|
|
24
|
+
* registration and the factory disagree.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* declare module "react-router-effect" {
|
|
28
|
+
* interface Register {
|
|
29
|
+
* // Each error tag a handler recovers, and the data it recovers into.
|
|
30
|
+
* recover: { FormValidationError: { reply: SubmissionResponse } };
|
|
31
|
+
* // What `actionMeta` returns.
|
|
32
|
+
* actionMeta: { realtimeEventIds: ReadonlyArray<string> };
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export interface Register {}
|
|
38
|
+
|
|
39
|
+
/** The registered recover map: error tag to recovered data. */
|
|
40
|
+
export type RegisteredRecover = Register extends { readonly recover: infer Recover } ? Recover : {};
|
|
41
|
+
|
|
42
|
+
/** The registered action metadata, or `never` when there is none. */
|
|
43
|
+
export type RegisteredMeta = Register extends { readonly actionMeta: infer Meta } ? Meta : never;
|
|
44
|
+
|
|
45
|
+
/** What a failure of type `E` recovers into: `Respond.early` bodies and registered handlers. */
|
|
46
|
+
type Recovered<E> =
|
|
47
|
+
| (E extends ReturnableDataError<infer Body> ? Body : never)
|
|
48
|
+
| (E extends { readonly _tag: infer Tag extends keyof RegisteredRecover }
|
|
49
|
+
? RegisteredRecover[Tag]
|
|
50
|
+
: never);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* React Router's own serialization of loader data. React Router does not
|
|
54
|
+
* export it, but a zero-argument function is a server loader to its public
|
|
55
|
+
* `useLoaderData` type, so this gives the same type.
|
|
56
|
+
*/
|
|
57
|
+
export type Serialize<T> = ReturnType<typeof useLoaderData<() => Promise<T>>>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The data an Effect that succeeds with `A` and fails with `E` gives the
|
|
61
|
+
* component: its success and what its failures recover into, serialized as
|
|
62
|
+
* React Router serializes them, with the registered action metadata.
|
|
63
|
+
*/
|
|
64
|
+
export type EffectData<A, E> = WithActionMeta<Serialize<A | Recovered<E>>, RegisteredMeta>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The route data of a loader or action, for React Router's hooks:
|
|
68
|
+
* `useFetcher<RouteData<typeof action>>()`. A function that returns an Effect
|
|
69
|
+
* gives its `EffectData`. Any other function passes through, so the hook
|
|
70
|
+
* reads it as it always has.
|
|
71
|
+
*/
|
|
72
|
+
export type RouteData<F> = F extends (...args: never[]) => Effect.Effect<infer A, infer E, infer _R>
|
|
73
|
+
? EffectData<A, E>
|
|
74
|
+
: F;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Loader data as React Router reads it before serializing: a `Response` adds
|
|
78
|
+
* nothing, and a `data(...)` wrapper gives its body.
|
|
79
|
+
*/
|
|
80
|
+
type Unwrapped<A> = A extends Response
|
|
81
|
+
? never
|
|
82
|
+
: A extends UNSAFE_DataWithResponseInit<infer Body>
|
|
83
|
+
? Body
|
|
84
|
+
: A;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The data React Router reads from an Effect through `unstable_SerializesTo`.
|
|
88
|
+
*
|
|
89
|
+
* TypeScript compares this brand whenever it relates two Effects
|
|
90
|
+
* structurally, so it must keep the subtype order of `A` and `E`: when
|
|
91
|
+
* `Effect<A1, E1>` is assignable to `Effect<A2, E2>`, `BrandData<A1, E1>` must
|
|
92
|
+
* be assignable to `BrandData<A2, E2>`. React Router's `Serialize` does not
|
|
93
|
+
* (it turns `unknown` into `undefined`), so the brand holds the data before
|
|
94
|
+
* serialization; `RouteData` applies the actual serialization.
|
|
95
|
+
*
|
|
96
|
+
* - A `void` or `any` success gives `unknown`, which accepts every other brand.
|
|
97
|
+
* - A `never` success gives only what the failures recover into.
|
|
98
|
+
*
|
|
99
|
+
* `Effect` declares its parameters `out`, and TypeScript verifies that
|
|
100
|
+
* annotation against this member, and against it again in every Effect type
|
|
101
|
+
* that declares variance (`Exit`, `Config`, ...). It relates two conditional
|
|
102
|
+
* types branch by branch when their check types are related and their
|
|
103
|
+
* `extends` types are identical. So every conditional here, and in the types
|
|
104
|
+
* it uses, checks `A` or `E` (or a tuple of one) against a type that does not
|
|
105
|
+
* depend on them. A conditional with `A` or `E` on its `extends` side, such as
|
|
106
|
+
* `0 extends 1 & A` to detect `any`, fails the verification: a consumer that
|
|
107
|
+
* checks library types (`skipLibCheck: false`) then gets TS2636 in Effect's own
|
|
108
|
+
* declarations. `test/consumer` checks the built package for that.
|
|
109
|
+
*/
|
|
110
|
+
export type BrandData<A, E> = [A] extends [never]
|
|
111
|
+
? WithActionMeta<Recovered<E>, RegisteredMeta>
|
|
112
|
+
: [A] extends [void]
|
|
113
|
+
? unknown
|
|
114
|
+
: WithActionMeta<Unwrapped<A> | Recovered<E>, RegisteredMeta>;
|
|
115
|
+
|
|
116
|
+
declare module "effect/Effect" {
|
|
117
|
+
// React Router reads loader and action data through this brand, so an
|
|
118
|
+
// Effect-returning loader types `Route.ComponentProps` like a promise one.
|
|
119
|
+
interface Effect<out A, out E, out R> {
|
|
120
|
+
readonly unstable__ReactRouter_SerializesTo: [BrandData<A, E>];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
declare module "effect/Cause" {
|
|
125
|
+
// A yieldable error is structurally an Effect that never succeeds, so it
|
|
126
|
+
// needs the brand too, or it stops being assignable to `Effect`.
|
|
127
|
+
interface YieldableError {
|
|
128
|
+
readonly unstable__ReactRouter_SerializesTo: [never];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Route args for a loader or action written as a plain `Effect.fn`, such as
|
|
134
|
+
* `EffectRouteArgs<Route.ActionArgs>`. It keeps hooks that take `typeof loader`
|
|
135
|
+
* or `typeof action` typed, such as `useFetcher<typeof action>()`.
|
|
136
|
+
*
|
|
137
|
+
* Those hooks read a function's data through React Router's `SerializeFrom`.
|
|
138
|
+
* It treats a function whose argument extends `ClientDataFunctionArgs` as a
|
|
139
|
+
* client function, and gives its raw return value without serialization.
|
|
140
|
+
* Typegen's `Route.LoaderArgs` and `Route.ActionArgs` have the same members as
|
|
141
|
+
* `ClientDataFunctionArgs`, so a server function typed with them matches too.
|
|
142
|
+
* For an Effect, the raw return value is the Effect itself: only serialization
|
|
143
|
+
* reads the `unstable_SerializesTo` brand.
|
|
144
|
+
*
|
|
145
|
+
* `pattern` is optional here, so the argument no longer matches and the hooks
|
|
146
|
+
* read the brand. `Route.ComponentProps` and `RouteData` don't go through
|
|
147
|
+
* `SerializeFrom`, so they are correct without it.
|
|
148
|
+
*/
|
|
149
|
+
export type EffectRouteArgs<Args extends { readonly pattern: string }> = Omit<Args, "pattern"> & {
|
|
150
|
+
readonly pattern?: string;
|
|
151
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** `react-router-effect/server`: serve React Router from the Effect HTTP server. */
|
|
2
|
+
import "./route-data.ts";
|
|
3
|
+
|
|
4
|
+
export * as ReactRouterHttp from "./server.ts";
|
|
5
|
+
export type {
|
|
6
|
+
RegisterMismatch,
|
|
7
|
+
RouteEffectCheck,
|
|
8
|
+
RouteEffectFailure,
|
|
9
|
+
RouteEffectFailures,
|
|
10
|
+
} from "./check.ts";
|