react-router-effect 0.4.0 → 0.5.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 +6 -5
- package/dist/index.d.mts +40 -33
- package/dist/index.mjs +7 -7
- package/package.json +1 -1
- package/src/factory.ts +69 -77
package/README.md
CHANGED
|
@@ -77,11 +77,11 @@ type DomainErrors = FormError | BadInputError | DbError;
|
|
|
77
77
|
// Curried: pin the domain errors, then the handler types are inferred.
|
|
78
78
|
export const { makeLoader, makeAction } = makeLoaderOrActionFactory<DomainErrors>()({
|
|
79
79
|
errorHandlers: {
|
|
80
|
+
// keys autocomplete to your domain-error tags; `error` is typed from its key.
|
|
80
81
|
// recover: short-circuit and hand the reply to the component
|
|
81
|
-
FormError: (error
|
|
82
|
+
FormError: (error) => baseRespond.early({ reply: error.reply }),
|
|
82
83
|
// throw: send to the error boundary
|
|
83
|
-
BadInputError: (error:
|
|
84
|
-
Effect.fail(new Response(error.message, { status: 400 })),
|
|
84
|
+
BadInputError: (error) => Effect.fail(new Response(error.message, { status: 400 })),
|
|
85
85
|
},
|
|
86
86
|
});
|
|
87
87
|
|
|
@@ -92,8 +92,9 @@ export const Respond = {
|
|
|
92
92
|
};
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
> **
|
|
96
|
-
> the precise recover types are derived from
|
|
95
|
+
> **No annotations needed.** Handler keys autocomplete to your declared domain-error tags, each
|
|
96
|
+
> `error` parameter is typed from its key, and the precise recover types are derived from each
|
|
97
|
+
> handler's return.
|
|
97
98
|
|
|
98
99
|
### 2. Write loaders/actions as effects
|
|
99
100
|
|
package/dist/index.d.mts
CHANGED
|
@@ -76,14 +76,34 @@ type ReturnableBodyOf<E> = E extends ReturnableDataError<infer Body> ? Body : ne
|
|
|
76
76
|
* `DataWithResponseInit<never>` into its return type.
|
|
77
77
|
*/
|
|
78
78
|
type DirectRecover<E> = [ReturnableBodyOf<E>] extends [never] ? never : UNSAFE_DataWithResponseInit<ReturnableBodyOf<E>>;
|
|
79
|
+
/** What a handler may return: a library route error or an `Effect` failing with a response. */
|
|
80
|
+
type HandlerReturn = AnyRouteError | Effect.Effect<unknown, FailureResponse>;
|
|
79
81
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
+
* The inferred map of handler RETURN types, keyed by domain-error tag. Making the
|
|
83
|
+
* factory generic over *this* (rather than the whole handler map) is what lets
|
|
84
|
+
* `errorHandlers` contextually type each handler's `error` param from its key —
|
|
85
|
+
* no annotation needed — while still capturing precise return types for recovery.
|
|
86
|
+
*/
|
|
87
|
+
type HandlerReturns<DomainError extends Tagged> = Partial<Record<DomainError["_tag"], HandlerReturn>>;
|
|
88
|
+
/**
|
|
89
|
+
* The `errorHandlers` field shape for a given return map: one entry per registered
|
|
90
|
+
* tag, whose `error` is the matching domain error (so it autocompletes and types
|
|
91
|
+
* itself) and whose return is the precise, inferred `Returns[Tag]`.
|
|
92
|
+
*/
|
|
93
|
+
type ErrorHandlers<DomainError extends Tagged, Returns> = { [Tag in keyof Returns]: (error: Extract<DomainError, {
|
|
94
|
+
readonly _tag: Tag;
|
|
95
|
+
}>) => Returns[Tag] };
|
|
96
|
+
/**
|
|
97
|
+
* What the registered handlers recover into loader/action data — from each
|
|
98
|
+
* handler's return type, but only for handlers whose error can actually occur in
|
|
99
|
+
* `E`:
|
|
82
100
|
* - a remapped `ReturnableDataError<B>` recovers as `DataWithResponseInit<B>`;
|
|
83
101
|
* - an `Effect.succeed(value)` recovers as `value`;
|
|
84
102
|
* - throwables, redirects and `Effect.fail(...)` contribute nothing.
|
|
85
103
|
*/
|
|
86
|
-
type RecoverOf<
|
|
104
|
+
type RecoverOf<DomainError extends Tagged, Returns, E> = { [Tag in keyof Returns]: [Extract<E, Extract<DomainError, {
|
|
105
|
+
readonly _tag: Tag;
|
|
106
|
+
}>>] extends [never] ? never : (Returns[Tag] extends ReturnableDataError<infer B> ? UNSAFE_DataWithResponseInit<B> : never) | (Returns[Tag] extends Effect.Effect<infer SuccessValue, any, any> ? SuccessValue : never) }[keyof Returns];
|
|
87
107
|
/**
|
|
88
108
|
* Errors the library deals with on the loader's behalf — so the loader needn't
|
|
89
109
|
* handle them itself:
|
|
@@ -99,21 +119,6 @@ type LibraryHandled<DomainError> = DomainError | AnyRouteError | HttpServerRespo
|
|
|
99
119
|
* errors. The loader/action must handle these itself (catch or map them).
|
|
100
120
|
*/
|
|
101
121
|
type Unhandled<E, DomainError> = Exclude<E, LibraryHandled<DomainError>>;
|
|
102
|
-
/**
|
|
103
|
-
* The shape a handler map must satisfy: an OPTIONAL handler per declared domain
|
|
104
|
-
* error, keyed by its tag, taking that error and returning a library route error
|
|
105
|
-
* or an `Effect` failing with a response. Used only as a validation constraint —
|
|
106
|
-
* the concrete handler types stay precise via the `const Handlers` inference.
|
|
107
|
-
*/
|
|
108
|
-
type ValidHandlers<DomainError extends Tagged> = { [Tag in DomainError["_tag"]]?: (error: Extract<DomainError, {
|
|
109
|
-
readonly _tag: Tag;
|
|
110
|
-
}>) => AnyRouteError | Effect.Effect<unknown, FailureResponse> };
|
|
111
|
-
/**
|
|
112
|
-
* True when every registered handler is keyed by a declared domain error's tag and
|
|
113
|
-
* returns a library route error or a failing `Effect`. A handler for an unknown tag
|
|
114
|
-
* (`keyof Handlers` escaping the domain tags) or with a bad return makes it `false`.
|
|
115
|
-
*/
|
|
116
|
-
type HandlersAreValid<Handlers, DomainError extends Tagged> = [keyof Handlers] extends [DomainError["_tag"]] ? Handlers extends ValidHandlers<DomainError> ? true : false : false;
|
|
117
122
|
/**
|
|
118
123
|
* A React Router context key holding a per-request effect `Context.Context`. Set
|
|
119
124
|
* it in middleware and pass it to the factory's `requestContext` — the runner
|
|
@@ -146,18 +151,18 @@ type RequestContextKey<ReqServices> = RouterContext<Context.Context<ReqServices>
|
|
|
146
151
|
* Build `makeLoader` / `makeAction` for an application, wired to its domain errors.
|
|
147
152
|
*
|
|
148
153
|
* Declare the app's **domain errors** as the type argument, then register an
|
|
149
|
-
* *optional* handler per domain error in `errorHandlers
|
|
150
|
-
*
|
|
151
|
-
*
|
|
154
|
+
* *optional* handler per domain error in `errorHandlers`. The handler keys
|
|
155
|
+
* autocomplete to your domain-error tags and each `error` parameter is typed from
|
|
156
|
+
* its key — no annotation needed. It's curried so you can pin the domain errors
|
|
157
|
+
* while the handler return types are still inferred:
|
|
152
158
|
*
|
|
153
159
|
* ```ts
|
|
154
160
|
* type DomainErrors = MyDomainError | DbError | NotAuthorizedError;
|
|
155
161
|
*
|
|
156
162
|
* const { makeLoader, makeAction } = makeLoaderOrActionFactory<DomainErrors>()({
|
|
157
163
|
* errorHandlers: {
|
|
158
|
-
* // throw → error boundary
|
|
159
|
-
* MyDomainError: (error:
|
|
160
|
-
* Effect.fail(new Response(error.message, { status: 400 })),
|
|
164
|
+
* // `error` is typed as MyDomainError automatically. throw → error boundary
|
|
165
|
+
* MyDomainError: (error) => Effect.fail(new Response(error.message, { status: 400 })),
|
|
161
166
|
* // DbError has no handler → falls through to the 500 default.
|
|
162
167
|
* },
|
|
163
168
|
* });
|
|
@@ -193,13 +198,15 @@ type RequestContextKey<ReqServices> = RouterContext<Context.Context<ReqServices>
|
|
|
193
198
|
* );
|
|
194
199
|
* ```
|
|
195
200
|
*/
|
|
196
|
-
declare function makeLoaderOrActionFactory<DomainError extends Tagged = never>(): <const
|
|
201
|
+
declare function makeLoaderOrActionFactory<DomainError extends Tagged = never>(): <const Returns extends HandlerReturns<DomainError> = {}, RServices = never, ReqServices = never>(config: {
|
|
197
202
|
/**
|
|
198
|
-
* An optional handler per declared domain error
|
|
199
|
-
*
|
|
200
|
-
*
|
|
203
|
+
* An optional handler per declared domain error, keyed by its tag. The keys
|
|
204
|
+
* autocomplete to your domain-error tags and each handler's `error` parameter
|
|
205
|
+
* is typed automatically — no annotation needed. A handler *remaps* the error
|
|
206
|
+
* by returning a library route error (`Respond.early` / `throw` / `redirect`)
|
|
207
|
+
* or an `Effect` (`Effect.succeed`/`fail`). Omit entirely to register none.
|
|
201
208
|
*/
|
|
202
|
-
errorHandlers?:
|
|
209
|
+
errorHandlers?: ErrorHandlers<DomainError, Returns>;
|
|
203
210
|
/**
|
|
204
211
|
* The app runtime that provides services to loader/action effects. When
|
|
205
212
|
* set, effects may require its services (`RServices`, inferred from here)
|
|
@@ -208,16 +215,16 @@ declare function makeLoaderOrActionFactory<DomainError extends Tagged = never>()
|
|
|
208
215
|
*/
|
|
209
216
|
runtime?: ManagedRuntime.ManagedRuntime<RServices, any>;
|
|
210
217
|
/**
|
|
211
|
-
* A React Router context key (
|
|
218
|
+
* A React Router context key (a {@link RequestContextKey}) holding a
|
|
212
219
|
* per-request effect `Context.Context`. Middleware sets it for each request;
|
|
213
220
|
* the runner reads `args.context.get(requestContext)` and provides those
|
|
214
221
|
* services to the effect. Loader/action effects may then require
|
|
215
222
|
* `ReqServices` (inferred from here) in addition to the runtime's services.
|
|
216
223
|
*/
|
|
217
224
|
requestContext?: RequestContextKey<ReqServices>;
|
|
218
|
-
}
|
|
219
|
-
makeLoader: <Args extends LoaderFunctionArgs | ActionFunctionArgs, A, E>(fn: (args: Args) => Effect.Effect<A, E, RServices | ReqServices>, ..._handle: [Unhandled<E, DomainError>] extends [never] ? [] : [theseErrorsAreNotDomainErrorsAndMustBeHandledInTheLoaderOrAction: Exclude<E, LibraryHandled<DomainError>>]) => (args: Args) => Promise<A | RecoverOf<
|
|
220
|
-
makeAction: <Args extends LoaderFunctionArgs | ActionFunctionArgs, A, E>(fn: (args: Args) => Effect.Effect<A, E, RServices | ReqServices>, ..._handle: [Unhandled<E, DomainError>] extends [never] ? [] : [theseErrorsAreNotDomainErrorsAndMustBeHandledInTheLoaderOrAction: Exclude<E, LibraryHandled<DomainError>>]) => (args: Args) => Promise<A | RecoverOf<
|
|
225
|
+
}) => {
|
|
226
|
+
makeLoader: <Args extends LoaderFunctionArgs | ActionFunctionArgs, A, E>(fn: (args: Args) => Effect.Effect<A, E, RServices | ReqServices>, ..._handle: [Unhandled<E, DomainError>] extends [never] ? [] : [theseErrorsAreNotDomainErrorsAndMustBeHandledInTheLoaderOrAction: Exclude<E, LibraryHandled<DomainError>>]) => (args: Args) => Promise<A | RecoverOf<DomainError, Returns, E> | DirectRecover<E>>;
|
|
227
|
+
makeAction: <Args extends LoaderFunctionArgs | ActionFunctionArgs, A, E>(fn: (args: Args) => Effect.Effect<A, E, RServices | ReqServices>, ..._handle: [Unhandled<E, DomainError>] extends [never] ? [] : [theseErrorsAreNotDomainErrorsAndMustBeHandledInTheLoaderOrAction: Exclude<E, LibraryHandled<DomainError>>]) => (args: Args) => Promise<A | RecoverOf<DomainError, Returns, E> | DirectRecover<E>>;
|
|
221
228
|
};
|
|
222
229
|
//#endregion
|
|
223
230
|
export { type AnyRouteError, type ErrorHandler, type RequestContextKey, Respond, ReturnableDataError, ThrowableDataError, ThrowableRedirectError, isRouteError, makeLoaderOrActionFactory };
|
package/dist/index.mjs
CHANGED
|
@@ -52,18 +52,18 @@ const processRouteError = (e) => {
|
|
|
52
52
|
* Build `makeLoader` / `makeAction` for an application, wired to its domain errors.
|
|
53
53
|
*
|
|
54
54
|
* Declare the app's **domain errors** as the type argument, then register an
|
|
55
|
-
* *optional* handler per domain error in `errorHandlers
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* *optional* handler per domain error in `errorHandlers`. The handler keys
|
|
56
|
+
* autocomplete to your domain-error tags and each `error` parameter is typed from
|
|
57
|
+
* its key — no annotation needed. It's curried so you can pin the domain errors
|
|
58
|
+
* while the handler return types are still inferred:
|
|
58
59
|
*
|
|
59
60
|
* ```ts
|
|
60
61
|
* type DomainErrors = MyDomainError | DbError | NotAuthorizedError;
|
|
61
62
|
*
|
|
62
63
|
* const { makeLoader, makeAction } = makeLoaderOrActionFactory<DomainErrors>()({
|
|
63
64
|
* errorHandlers: {
|
|
64
|
-
* // throw → error boundary
|
|
65
|
-
* MyDomainError: (error:
|
|
66
|
-
* Effect.fail(new Response(error.message, { status: 400 })),
|
|
65
|
+
* // `error` is typed as MyDomainError automatically. throw → error boundary
|
|
66
|
+
* MyDomainError: (error) => Effect.fail(new Response(error.message, { status: 400 })),
|
|
67
67
|
* // DbError has no handler → falls through to the 500 default.
|
|
68
68
|
* },
|
|
69
69
|
* });
|
|
@@ -100,7 +100,7 @@ const processRouteError = (e) => {
|
|
|
100
100
|
* ```
|
|
101
101
|
*/
|
|
102
102
|
function makeLoaderOrActionFactory() {
|
|
103
|
-
return function defineErrorHandlers(config
|
|
103
|
+
return function defineErrorHandlers(config) {
|
|
104
104
|
const runtime = config.runtime;
|
|
105
105
|
const requestContextKey = config.requestContext;
|
|
106
106
|
const userHandlers = config.errorHandlers ?? {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://schemastore.org/package.json",
|
|
3
3
|
"name": "react-router-effect",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "Wrap React Router loaders/actions with Effect, with typed recover/throw/redirect error handling.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"action",
|
package/src/factory.ts
CHANGED
|
@@ -53,22 +53,43 @@ type DirectRecover<E> = [ReturnableBodyOf<E>] extends [never]
|
|
|
53
53
|
? never
|
|
54
54
|
: DataWithResponseInit<ReturnableBodyOf<E>>;
|
|
55
55
|
|
|
56
|
+
/** What a handler may return: a library route error or an `Effect` failing with a response. */
|
|
57
|
+
type HandlerReturn = AnyRouteError | Effect.Effect<unknown, FailureResponse>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The inferred map of handler RETURN types, keyed by domain-error tag. Making the
|
|
61
|
+
* factory generic over *this* (rather than the whole handler map) is what lets
|
|
62
|
+
* `errorHandlers` contextually type each handler's `error` param from its key —
|
|
63
|
+
* no annotation needed — while still capturing precise return types for recovery.
|
|
64
|
+
*/
|
|
65
|
+
type HandlerReturns<DomainError extends Tagged> = Partial<
|
|
66
|
+
Record<DomainError["_tag"], HandlerReturn>
|
|
67
|
+
>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The `errorHandlers` field shape for a given return map: one entry per registered
|
|
71
|
+
* tag, whose `error` is the matching domain error (so it autocompletes and types
|
|
72
|
+
* itself) and whose return is the precise, inferred `Returns[Tag]`.
|
|
73
|
+
*/
|
|
74
|
+
type ErrorHandlers<DomainError extends Tagged, Returns> = {
|
|
75
|
+
[Tag in keyof Returns]: (error: Extract<DomainError, { readonly _tag: Tag }>) => Returns[Tag];
|
|
76
|
+
};
|
|
77
|
+
|
|
56
78
|
/**
|
|
57
|
-
* What the registered handlers recover into loader/action data —
|
|
58
|
-
*
|
|
79
|
+
* What the registered handlers recover into loader/action data — from each
|
|
80
|
+
* handler's return type, but only for handlers whose error can actually occur in
|
|
81
|
+
* `E`:
|
|
59
82
|
* - a remapped `ReturnableDataError<B>` recovers as `DataWithResponseInit<B>`;
|
|
60
83
|
* - an `Effect.succeed(value)` recovers as `value`;
|
|
61
84
|
* - throwables, redirects and `Effect.fail(...)` contribute nothing.
|
|
62
85
|
*/
|
|
63
|
-
type RecoverOf<
|
|
64
|
-
[Tag in keyof
|
|
65
|
-
?
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
: never;
|
|
71
|
-
}[keyof Handlers];
|
|
86
|
+
type RecoverOf<DomainError extends Tagged, Returns, E> = {
|
|
87
|
+
[Tag in keyof Returns]: [Extract<E, Extract<DomainError, { readonly _tag: Tag }>>] extends [never]
|
|
88
|
+
? never
|
|
89
|
+
:
|
|
90
|
+
| (Returns[Tag] extends ReturnableDataError<infer B> ? DataWithResponseInit<B> : never)
|
|
91
|
+
| (Returns[Tag] extends Effect.Effect<infer SuccessValue, any, any> ? SuccessValue : never);
|
|
92
|
+
}[keyof Returns];
|
|
72
93
|
|
|
73
94
|
/**
|
|
74
95
|
* Errors the library deals with on the loader's behalf — so the loader needn't
|
|
@@ -87,31 +108,6 @@ type LibraryHandled<DomainError> = DomainError | AnyRouteError | HttpServerRespo
|
|
|
87
108
|
*/
|
|
88
109
|
type Unhandled<E, DomainError> = Exclude<E, LibraryHandled<DomainError>>;
|
|
89
110
|
|
|
90
|
-
/**
|
|
91
|
-
* The shape a handler map must satisfy: an OPTIONAL handler per declared domain
|
|
92
|
-
* error, keyed by its tag, taking that error and returning a library route error
|
|
93
|
-
* or an `Effect` failing with a response. Used only as a validation constraint —
|
|
94
|
-
* the concrete handler types stay precise via the `const Handlers` inference.
|
|
95
|
-
*/
|
|
96
|
-
type ValidHandlers<DomainError extends Tagged> = {
|
|
97
|
-
[Tag in DomainError["_tag"]]?: (
|
|
98
|
-
error: Extract<DomainError, { readonly _tag: Tag }>,
|
|
99
|
-
) => AnyRouteError | Effect.Effect<unknown, FailureResponse>;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* True when every registered handler is keyed by a declared domain error's tag and
|
|
104
|
-
* returns a library route error or a failing `Effect`. A handler for an unknown tag
|
|
105
|
-
* (`keyof Handlers` escaping the domain tags) or with a bad return makes it `false`.
|
|
106
|
-
*/
|
|
107
|
-
type HandlersAreValid<Handlers, DomainError extends Tagged> = [keyof Handlers] extends [
|
|
108
|
-
DomainError["_tag"],
|
|
109
|
-
]
|
|
110
|
-
? Handlers extends ValidHandlers<DomainError>
|
|
111
|
-
? true
|
|
112
|
-
: false
|
|
113
|
-
: false;
|
|
114
|
-
|
|
115
111
|
// ---------------------------------------------------------------------------
|
|
116
112
|
// Internal runtime helpers.
|
|
117
113
|
// ---------------------------------------------------------------------------
|
|
@@ -169,18 +165,18 @@ export type RequestContextKey<ReqServices> = RouterContext<Context.Context<ReqSe
|
|
|
169
165
|
* Build `makeLoader` / `makeAction` for an application, wired to its domain errors.
|
|
170
166
|
*
|
|
171
167
|
* Declare the app's **domain errors** as the type argument, then register an
|
|
172
|
-
* *optional* handler per domain error in `errorHandlers
|
|
173
|
-
*
|
|
174
|
-
*
|
|
168
|
+
* *optional* handler per domain error in `errorHandlers`. The handler keys
|
|
169
|
+
* autocomplete to your domain-error tags and each `error` parameter is typed from
|
|
170
|
+
* its key — no annotation needed. It's curried so you can pin the domain errors
|
|
171
|
+
* while the handler return types are still inferred:
|
|
175
172
|
*
|
|
176
173
|
* ```ts
|
|
177
174
|
* type DomainErrors = MyDomainError | DbError | NotAuthorizedError;
|
|
178
175
|
*
|
|
179
176
|
* const { makeLoader, makeAction } = makeLoaderOrActionFactory<DomainErrors>()({
|
|
180
177
|
* errorHandlers: {
|
|
181
|
-
* // throw → error boundary
|
|
182
|
-
* MyDomainError: (error:
|
|
183
|
-
* Effect.fail(new Response(error.message, { status: 400 })),
|
|
178
|
+
* // `error` is typed as MyDomainError automatically. throw → error boundary
|
|
179
|
+
* MyDomainError: (error) => Effect.fail(new Response(error.message, { status: 400 })),
|
|
184
180
|
* // DbError has no handler → falls through to the 500 default.
|
|
185
181
|
* },
|
|
186
182
|
* });
|
|
@@ -217,39 +213,35 @@ export type RequestContextKey<ReqServices> = RouterContext<Context.Context<ReqSe
|
|
|
217
213
|
* ```
|
|
218
214
|
*/
|
|
219
215
|
export function makeLoaderOrActionFactory<DomainError extends Tagged = never>() {
|
|
220
|
-
return function defineErrorHandlers<
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
: [
|
|
250
|
-
eachHandlerMustBeForADeclaredDomainErrorAndReturnARouteErrorOrAnEffect: ValidHandlers<DomainError>,
|
|
251
|
-
]
|
|
252
|
-
) {
|
|
216
|
+
return function defineErrorHandlers<
|
|
217
|
+
const Returns extends HandlerReturns<DomainError> = {},
|
|
218
|
+
RServices = never,
|
|
219
|
+
ReqServices = never,
|
|
220
|
+
>(config: {
|
|
221
|
+
/**
|
|
222
|
+
* An optional handler per declared domain error, keyed by its tag. The keys
|
|
223
|
+
* autocomplete to your domain-error tags and each handler's `error` parameter
|
|
224
|
+
* is typed automatically — no annotation needed. A handler *remaps* the error
|
|
225
|
+
* by returning a library route error (`Respond.early` / `throw` / `redirect`)
|
|
226
|
+
* or an `Effect` (`Effect.succeed`/`fail`). Omit entirely to register none.
|
|
227
|
+
*/
|
|
228
|
+
errorHandlers?: ErrorHandlers<DomainError, Returns>;
|
|
229
|
+
/**
|
|
230
|
+
* The app runtime that provides services to loader/action effects. When
|
|
231
|
+
* set, effects may require its services (`RServices`, inferred from here)
|
|
232
|
+
* without providing layers, and runs go through `runtime.runPromise`. When
|
|
233
|
+
* omitted, `RServices` is `never` and effects must require nothing.
|
|
234
|
+
*/
|
|
235
|
+
runtime?: ManagedRuntime.ManagedRuntime<RServices, any>;
|
|
236
|
+
/**
|
|
237
|
+
* A React Router context key (a {@link RequestContextKey}) holding a
|
|
238
|
+
* per-request effect `Context.Context`. Middleware sets it for each request;
|
|
239
|
+
* the runner reads `args.context.get(requestContext)` and provides those
|
|
240
|
+
* services to the effect. Loader/action effects may then require
|
|
241
|
+
* `ReqServices` (inferred from here) in addition to the runtime's services.
|
|
242
|
+
*/
|
|
243
|
+
requestContext?: RequestContextKey<ReqServices>;
|
|
244
|
+
}) {
|
|
253
245
|
const runtime = config.runtime;
|
|
254
246
|
const requestContextKey = config.requestContext;
|
|
255
247
|
|
|
@@ -281,7 +273,7 @@ export function makeLoaderOrActionFactory<DomainError extends Tagged = never>()
|
|
|
281
273
|
DomainError
|
|
282
274
|
>,
|
|
283
275
|
]
|
|
284
|
-
): (args: Args) => Promise<A | DirectRecover<E> | RecoverOf<
|
|
276
|
+
): (args: Args) => Promise<A | DirectRecover<E> | RecoverOf<DomainError, Returns, E>> {
|
|
285
277
|
return (args: Args) => {
|
|
286
278
|
// The internal channel is deliberately loose (`unknown` success); the outer
|
|
287
279
|
// cast restores the precise resolved type (computed from `E` and the handler
|
|
@@ -328,7 +320,7 @@ export function makeLoaderOrActionFactory<DomainError extends Tagged = never>()
|
|
|
328
320
|
const result = runtime
|
|
329
321
|
? runtime.runPromise(provided)
|
|
330
322
|
: Effect.runPromise(provided as Effect.Effect<unknown, FailureResponse>);
|
|
331
|
-
return result as Promise<A | DirectRecover<E> | RecoverOf<
|
|
323
|
+
return result as Promise<A | DirectRecover<E> | RecoverOf<DomainError, Returns, E>>;
|
|
332
324
|
};
|
|
333
325
|
}
|
|
334
326
|
|