orpc-nuxt 0.5.0 → 0.7.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 +26 -20
- package/dist/module.json +1 -1
- package/dist/runtime/client/create.js +5 -0
- package/dist/runtime/client/error.d.ts +17 -6
- package/dist/runtime/client/error.js +3 -0
- package/dist/runtime/testing.d.ts +24 -22
- package/dist/runtime/testing.js +19 -5
- package/dist/runtime/types.d.ts +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -173,15 +173,10 @@ const post = await orpc.blog.posts.get.call({ id: 1 })
|
|
|
173
173
|
|
|
174
174
|
### Declared errors
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
Let every other error propagate to the application's error handler, including an `ORPCError` whose code was not declared by that procedure.
|
|
178
|
-
|
|
179
|
-
Use `catchORPCError()` from the client entrypoint to handle selected declared errors while preserving their code and data types:
|
|
176
|
+
Use `.callCatching()` to call a procedure and handle errors it declares with `.errors()`, with their code and data types:
|
|
180
177
|
|
|
181
178
|
```ts
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const post = await catchORPCError(orpc.blog.posts.update.call(input), {
|
|
179
|
+
const post = await orpc.blog.posts.update.callCatching(input, {
|
|
185
180
|
CONFLICT: (error) => {
|
|
186
181
|
message.value = error.message
|
|
187
182
|
conflictingField.value = error.data.field
|
|
@@ -195,16 +190,26 @@ Undeclared errors and declared codes without a handler are rethrown unchanged.
|
|
|
195
190
|
|
|
196
191
|
When one declared error simply means there is no result, map it to `undefined` or `null` directly:
|
|
197
192
|
|
|
193
|
+
```ts
|
|
194
|
+
const post = await orpc.blog.posts.get.callCatching({ id }, { NOT_FOUND: null })
|
|
195
|
+
// post is the procedure output or null.
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Pass call options such as `context` or `signal` as the third argument.
|
|
199
|
+
Pass `undefined` as input for procedures without input.
|
|
200
|
+
|
|
201
|
+
`.callCatching()` is built on `catchORPCError()`, which handles errors of a promise returned directly by any typed oRPC client call.
|
|
202
|
+
Import it from the client entrypoint when you do not call the procedure through this package's client:
|
|
203
|
+
|
|
198
204
|
```ts
|
|
199
205
|
import { catchORPCError } from "orpc-nuxt/client"
|
|
200
206
|
|
|
201
|
-
const post = await catchORPCError(
|
|
202
|
-
NOT_FOUND: null,
|
|
203
|
-
})
|
|
204
|
-
// post is the procedure output or null.
|
|
207
|
+
const post = await catchORPCError(client.blog.posts.get({ id }), { NOT_FOUND: null })
|
|
205
208
|
```
|
|
206
209
|
|
|
207
|
-
|
|
210
|
+
### oRPC utilities
|
|
211
|
+
|
|
212
|
+
The client also exposes oRPC's TanStack Query utilities:
|
|
208
213
|
|
|
209
214
|
- `.key()` builds a cache key prefix for a procedure or router branch.
|
|
210
215
|
- `.queryKey()` builds a query key for a specific input.
|
|
@@ -557,19 +562,20 @@ test("renders the posts", async () => {
|
|
|
557
562
|
})
|
|
558
563
|
```
|
|
559
564
|
|
|
560
|
-
|
|
565
|
+
The second handler argument provides typed error constructors derived from that procedure's `.errors()` map:
|
|
561
566
|
|
|
562
567
|
```ts
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
568
|
+
procedures.blog.posts.update.handle((input, { errors }) => {
|
|
569
|
+
if (input.title === "taken") {
|
|
570
|
+
throw errors.CONFLICT({
|
|
571
|
+
message: "Already exists",
|
|
572
|
+
data: { field: "title" },
|
|
573
|
+
})
|
|
574
|
+
}
|
|
575
|
+
return { id: 1, ...input }
|
|
567
576
|
})
|
|
568
577
|
```
|
|
569
578
|
|
|
570
|
-
Because this helper is standalone, it cannot infer which fake procedure will throw the error.
|
|
571
|
-
Its code and data are therefore not checked against that procedure's `.errors()` map.
|
|
572
|
-
|
|
573
579
|
### Outside Vue components
|
|
574
580
|
|
|
575
581
|
You can call `.useQuery()` and `.useMutation()` outside a component, for example in a script or in a test that never mounts one.
|
package/dist/module.json
CHANGED
|
@@ -4,6 +4,7 @@ import { useORPCMutation } from "../vue-query/mutation.js";
|
|
|
4
4
|
import { useORPCQuery } from "../vue-query/query.js";
|
|
5
5
|
import { resolveQueryClient } from "../vue-query/query-client.js";
|
|
6
6
|
import { decorateClient } from "./decorate.js";
|
|
7
|
+
import { catchDefinedErrors } from "./error.js";
|
|
7
8
|
export function createORPCNuxtClient(client, options = {}) {
|
|
8
9
|
const utils = createTanstackQueryUtils(client, { prefix: options.prefix });
|
|
9
10
|
let queryClient = options.queryClient ?? resolveQueryClient();
|
|
@@ -18,6 +19,10 @@ export function createORPCNuxtClient(client, options = {}) {
|
|
|
18
19
|
useMutation(mutationOptions) {
|
|
19
20
|
return useORPCMutation(target, mutationOptions, getQueryClient());
|
|
20
21
|
},
|
|
22
|
+
callCatching(input, handlers, callOptions) {
|
|
23
|
+
const utils2 = target;
|
|
24
|
+
return catchDefinedErrors(utils2.call(input, callOptions), handlers);
|
|
25
|
+
},
|
|
21
26
|
invalidate() {
|
|
22
27
|
const utils2 = target;
|
|
23
28
|
return getQueryClient().invalidateQueries({ queryKey: utils2.key() });
|
|
@@ -4,17 +4,20 @@ type ErrorOf<TPromise extends Promise<unknown>> = TPromise extends {
|
|
|
4
4
|
type: infer Error;
|
|
5
5
|
};
|
|
6
6
|
} ? Error : never;
|
|
7
|
-
type DefinedError<
|
|
8
|
-
|
|
7
|
+
type DefinedError<TError> = Extract<TError, AnyORPCError>;
|
|
8
|
+
/** Error codes declared by a procedure whose client error union is `TError`. */
|
|
9
|
+
export type DefinedErrorCode<TError> = DefinedError<TError>["code"] & string;
|
|
9
10
|
type ErrorResultValue = string | number | boolean | bigint | symbol | null | undefined | readonly unknown[] | {
|
|
10
11
|
readonly [key: string]: unknown;
|
|
11
12
|
};
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
/** Handlers or result values keyed by the codes declared in the client error union `TError`. */
|
|
14
|
+
export type DefinedErrorHandlers<TError> = Partial<{
|
|
15
|
+
[Code in DefinedErrorCode<TError>]: ((error: Extract<DefinedError<TError>, {
|
|
14
16
|
code: Code;
|
|
15
17
|
}>) => unknown) | ErrorResultValue;
|
|
16
18
|
}>;
|
|
17
|
-
|
|
19
|
+
/** The awaited result of any handler or value in `Handlers`. */
|
|
20
|
+
export type HandledResult<Handlers> = {
|
|
18
21
|
[Code in keyof Handlers]: Handlers[Code] extends (...args: never[]) => infer Result ? Awaited<Result> : Awaited<Handlers[Code]>;
|
|
19
22
|
}[keyof Handlers];
|
|
20
23
|
/**
|
|
@@ -28,5 +31,13 @@ type HandledResult<Handlers> = {
|
|
|
28
31
|
* @param handlers - Handlers keyed by declared error code.
|
|
29
32
|
* @returns The procedure output or the result of the matching handler.
|
|
30
33
|
*/
|
|
31
|
-
export declare function catchORPCError<TPromise extends Promise<unknown>, Handlers extends object & DefinedErrorHandlers<NoInfer<TPromise
|
|
34
|
+
export declare function catchORPCError<TPromise extends Promise<unknown>, Handlers extends object & DefinedErrorHandlers<ErrorOf<NoInfer<TPromise>>>>(promise: TPromise, handlers: "__error" extends keyof TPromise ? Handlers & Record<Exclude<keyof Handlers, DefinedErrorCode<ErrorOf<NoInfer<TPromise>>>>, never> : never): Promise<Awaited<TPromise> | HandledResult<Handlers>>;
|
|
35
|
+
/**
|
|
36
|
+
* Untyped runtime of `catchORPCError()` for callers that enforce handler types themselves.
|
|
37
|
+
*
|
|
38
|
+
* @param promise - A procedure call result.
|
|
39
|
+
* @param handlers - Handlers or result values keyed by declared error code.
|
|
40
|
+
* @returns The procedure output or the result of the matching handler.
|
|
41
|
+
*/
|
|
42
|
+
export declare function catchDefinedErrors(promise: Promise<unknown>, handlers: Record<string, unknown>): Promise<unknown>;
|
|
32
43
|
export {};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { isDefinedError } from "@orpc/client";
|
|
2
2
|
export function catchORPCError(promise, handlers) {
|
|
3
|
+
return catchDefinedErrors(promise, handlers);
|
|
4
|
+
}
|
|
5
|
+
export function catchDefinedErrors(promise, handlers) {
|
|
3
6
|
return promise.catch((error) => {
|
|
4
7
|
const orpcError = error;
|
|
5
8
|
if (!isDefinedError(orpcError)) throw error;
|
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
import type { AnyNestedClient, Client
|
|
1
|
+
import type { AnyORPCError, AnyNestedClient, Client } from "@orpc/client";
|
|
2
|
+
import { ORPCError } from "@orpc/client";
|
|
2
3
|
import { QueryClient } from "@tanstack/vue-query";
|
|
3
4
|
import type { Mock } from "vitest";
|
|
4
5
|
import type { ORPCNuxtClient, ORPCNuxtClientOptions } from "./types.js";
|
|
5
6
|
type MaybePromise<T> = T | Promise<T>;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
7
|
+
type ProcedureError<TProcedure> = TProcedure extends Client<any, any, any, infer Error> ? Extract<Error, AnyORPCError> : never;
|
|
8
|
+
type ErrorConstructorOptions<Data> = ErrorOptions & {
|
|
9
|
+
message?: string;
|
|
10
|
+
} & (undefined extends Data ? {
|
|
11
|
+
data?: Data;
|
|
12
|
+
} : {
|
|
13
|
+
data: Data;
|
|
14
|
+
});
|
|
15
|
+
type ErrorConstructor<ErrorType extends AnyORPCError> = ErrorType extends ORPCError<infer _Code, infer Data> ? (...args: undefined extends Data ? [options?: ErrorConstructorOptions<Data>] : [options: ErrorConstructorOptions<Data>]) => ErrorType : never;
|
|
16
|
+
type TestORPCErrors<TProcedure> = {
|
|
17
|
+
[Code in ProcedureError<TProcedure>["code"] & string]: ErrorConstructor<Extract<ProcedureError<TProcedure>, {
|
|
18
|
+
code: Code;
|
|
19
|
+
}>>;
|
|
20
|
+
};
|
|
21
|
+
/** Error constructors derived from the procedure's declared error union. */
|
|
22
|
+
export interface TestORPCHandlerOptions<TProcedure> {
|
|
23
|
+
/** Construct an error declared by this procedure, with its corresponding data type. */
|
|
24
|
+
errors: TestORPCErrors<TProcedure>;
|
|
25
|
+
}
|
|
25
26
|
/** A procedure implementation registered for a test client. */
|
|
26
|
-
export type TestORPCHandler<TProcedure> = TProcedure extends Client<infer _Context, infer Input, infer Output, infer _Error> ? (input: Input) => MaybePromise<Awaited<Output>> : never;
|
|
27
|
+
export type TestORPCHandler<TProcedure> = TProcedure extends Client<infer _Context, infer Input, infer Output, infer _Error> ? (input: Input, options: TestORPCHandlerOptions<TProcedure>) => MaybePromise<Awaited<Output>> : never;
|
|
28
|
+
type TestORPCMockHandler<TProcedure> = TProcedure extends Client<infer _Context, infer Input, infer Output, infer _Error> ? (input: Input) => MaybePromise<Awaited<Output>> : never;
|
|
27
29
|
/** Registration methods for one procedure in a test client. */
|
|
28
30
|
export interface TestORPCProcedure<TProcedure> {
|
|
29
31
|
/** Register the implementation used by subsequent calls and return its Vitest mock. */
|
|
30
|
-
handle(handler: TestORPCHandler<TProcedure>): Mock<
|
|
32
|
+
handle(handler: TestORPCHandler<TProcedure>): Mock<TestORPCMockHandler<TProcedure>>;
|
|
31
33
|
}
|
|
32
34
|
/** A router-shaped tree whose procedure leaves register test implementations. */
|
|
33
35
|
export type TestORPCProcedures<T extends AnyNestedClient> = T extends Client<infer Context, infer Input, infer Output, infer Error> ? TestORPCProcedure<Client<Context, Input, Output, Error>> : {
|
package/dist/runtime/testing.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { createORPCClient, createORPCErrorFromJson } from "@orpc/client";
|
|
1
|
+
import { createORPCClient, createORPCErrorFromJson, ORPCError } from "@orpc/client";
|
|
2
2
|
import { QueryClient } from "@tanstack/vue-query";
|
|
3
3
|
import { vi } from "vitest";
|
|
4
4
|
import { createORPCNuxtClient } from "./client/create.js";
|
|
5
|
-
export function createORPCError(code, message, data) {
|
|
6
|
-
return createORPCErrorFromJson({ defined: true, code, message, data });
|
|
7
|
-
}
|
|
8
5
|
export function createTestORPCClient(options = {}) {
|
|
9
6
|
const registrations = /* @__PURE__ */ new Map();
|
|
10
7
|
const queryClient = options.queryClient ?? new QueryClient({
|
|
@@ -14,7 +11,8 @@ export function createTestORPCClient(options = {}) {
|
|
|
14
11
|
}
|
|
15
12
|
});
|
|
16
13
|
function registerHandler(path, handler) {
|
|
17
|
-
const
|
|
14
|
+
const options2 = { errors: createErrorConstructors() };
|
|
15
|
+
const mock = vi.fn((input) => handler(input, options2));
|
|
18
16
|
registrations.set(path, mock);
|
|
19
17
|
return mock;
|
|
20
18
|
}
|
|
@@ -53,6 +51,22 @@ export function createTestORPCClient(options = {}) {
|
|
|
53
51
|
}
|
|
54
52
|
};
|
|
55
53
|
}
|
|
54
|
+
function createErrorConstructors() {
|
|
55
|
+
const constructors = /* @__PURE__ */ new Map();
|
|
56
|
+
return new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
57
|
+
get(target, property, receiver) {
|
|
58
|
+
if (typeof property !== "string") return Reflect.get(target, property, receiver);
|
|
59
|
+
const cached = constructors.get(property);
|
|
60
|
+
if (cached) return cached;
|
|
61
|
+
const constructor = (options) => {
|
|
62
|
+
const error = new ORPCError(property, options);
|
|
63
|
+
return createORPCErrorFromJson({ ...error.toJSON(), defined: true }, { cause: error.cause });
|
|
64
|
+
};
|
|
65
|
+
constructors.set(property, constructor);
|
|
66
|
+
return constructor;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
56
70
|
function createRecursiveProxy(path, call) {
|
|
57
71
|
const target = () => {
|
|
58
72
|
};
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { AnyNestedClient, Client, ClientContext } from "@orpc/client";
|
|
1
|
+
import type { AnyNestedClient, Client, ClientContext, FriendlyClientOptions } from "@orpc/client";
|
|
2
2
|
import type { RouterUtils } from "@orpc/tanstack-query";
|
|
3
3
|
import type { QueryClient, SkipToken, UseMutationOptions, UseMutationReturnType, UseQueryOptions, UseQueryReturnType } from "@tanstack/vue-query";
|
|
4
4
|
import type { ComputedRef, DeepReadonly, MaybeRefOrGetter, Ref, WritableComputedRef } from "vue";
|
|
5
|
+
import type { DefinedErrorCode, DefinedErrorHandlers, HandledResult } from "./client/error.js";
|
|
5
6
|
/** Configure cache ownership and key namespacing when wrapping an application-owned client. */
|
|
6
7
|
export interface ORPCNuxtClientOptions {
|
|
7
8
|
/** Separate the cache keys of clients whose procedure paths overlap. */
|
|
@@ -11,7 +12,8 @@ export interface ORPCNuxtClientOptions {
|
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* An oRPC client decorated with Vue composables and the official TanStack Query utilities.
|
|
14
|
-
* Router branches retain their names; finite procedures gain query and mutation composables
|
|
15
|
+
* Router branches retain their names; finite procedures gain query and mutation composables
|
|
16
|
+
* and `callCatching()`.
|
|
15
17
|
* Procedures whose output includes an async iterable retain only the upstream utilities.
|
|
16
18
|
*/
|
|
17
19
|
export type ORPCNuxtClient<T extends AnyNestedClient> = RouterUtils<T> & {
|
|
@@ -85,5 +87,10 @@ interface ProcedureHooks<C extends ClientContext, I, O, E> {
|
|
|
85
87
|
}>>): AwaitableQuery<ORPCQueryResult<O, E>>;
|
|
86
88
|
/** Create a mutation observer; call mutate or mutateAsync to execute the procedure. */
|
|
87
89
|
useMutation<M = unknown>(...args: object extends C ? [options?: MaybeRefOrGetter<ORPCMutationOptions<C, I, O, E, M>>] : [options: MaybeRefOrGetter<ORPCMutationOptions<C, I, O, E, M>>]): UseMutationReturnType<O, E, I, M>;
|
|
90
|
+
/**
|
|
91
|
+
* Call the procedure and handle selected declared errors like `catchORPCError()`.
|
|
92
|
+
* Pass `undefined` as input for procedures without input.
|
|
93
|
+
*/
|
|
94
|
+
callCatching<Handlers extends object & DefinedErrorHandlers<E>>(input: I, handlers: Handlers & Record<Exclude<keyof Handlers, DefinedErrorCode<E>>, never>, ...rest: object extends C ? [options?: FriendlyClientOptions<C>] : [options: FriendlyClientOptions<C>]): Promise<O | HandledResult<Handlers>>;
|
|
88
95
|
}
|
|
89
96
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orpc-nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "oRPC integration for Nuxt with TanStack Vue Query composables.",
|
|
5
5
|
"homepage": "https://github.com/IlyaSemenov/orpc-nuxt#readme",
|
|
6
6
|
"bugs": "https://github.com/IlyaSemenov/orpc-nuxt/issues",
|