orpc-nuxt 0.4.0 → 0.6.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 +51 -2
- package/dist/module.d.mts +2 -2
- package/dist/module.json +2 -2
- package/dist/runtime/client/error.d.ts +32 -0
- package/dist/runtime/client/error.js +10 -0
- package/dist/runtime/client.d.ts +1 -0
- package/dist/runtime/client.js +1 -0
- package/dist/runtime/testing.d.ts +24 -3
- package/dist/runtime/testing.js +19 -2
- package/dist/runtime/vue-query/mutation.d.ts +1 -1
- package/package.json +10 -13
package/README.md
CHANGED
|
@@ -171,6 +171,39 @@ Use `.call()` when you just need a procedure's response, without query state or
|
|
|
171
171
|
const post = await orpc.blog.posts.get.call({ id: 1 })
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
+
### Declared errors
|
|
175
|
+
|
|
176
|
+
Treat an expected rejection as an application outcome only when the procedure declares it with `.errors()`.
|
|
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:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import { catchORPCError } from "orpc-nuxt/client"
|
|
183
|
+
|
|
184
|
+
const post = await catchORPCError(orpc.blog.posts.update.call(input), {
|
|
185
|
+
CONFLICT: (error) => {
|
|
186
|
+
message.value = error.message
|
|
187
|
+
conflictingField.value = error.data.field
|
|
188
|
+
},
|
|
189
|
+
NOT_FOUND: () => navigateTo("/posts"),
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The successful output, matching handler's awaited result, or matching non-function value becomes the call result.
|
|
194
|
+
Undeclared errors and declared codes without a handler are rethrown unchanged.
|
|
195
|
+
|
|
196
|
+
When one declared error simply means there is no result, map it to `undefined` or `null` directly:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { catchORPCError } from "orpc-nuxt/client"
|
|
200
|
+
|
|
201
|
+
const post = await catchORPCError(orpc.blog.posts.get.call({ id }), {
|
|
202
|
+
NOT_FOUND: null,
|
|
203
|
+
})
|
|
204
|
+
// post is the procedure output or null.
|
|
205
|
+
```
|
|
206
|
+
|
|
174
207
|
The client also exposes oRPC's utilities:
|
|
175
208
|
|
|
176
209
|
- `.key()` builds a cache key prefix for a procedure or router branch.
|
|
@@ -524,6 +557,20 @@ test("renders the posts", async () => {
|
|
|
524
557
|
})
|
|
525
558
|
```
|
|
526
559
|
|
|
560
|
+
The second handler argument provides typed error constructors derived from that procedure's `.errors()` map:
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
procedures.blog.posts.update.handle((input, { errors }) => {
|
|
564
|
+
if (input.title === "taken") {
|
|
565
|
+
throw errors.CONFLICT({
|
|
566
|
+
message: "Already exists",
|
|
567
|
+
data: { field: "title" },
|
|
568
|
+
})
|
|
569
|
+
}
|
|
570
|
+
return { id: 1, ...input }
|
|
571
|
+
})
|
|
572
|
+
```
|
|
573
|
+
|
|
527
574
|
### Outside Vue components
|
|
528
575
|
|
|
529
576
|
You can call `.useQuery()` and `.useMutation()` outside a component, for example in a script or in a test that never mounts one.
|
|
@@ -553,14 +600,16 @@ try {
|
|
|
553
600
|
|
|
554
601
|
## Development
|
|
555
602
|
|
|
556
|
-
Install dependencies
|
|
603
|
+
Install dependencies from the repository root with `bun install`.
|
|
604
|
+
Run `bun run build`, `bun run types`, and `bun run test` from this package's directory.
|
|
557
605
|
|
|
558
606
|
Run `bun run test:component` to check the documented component-test recipe in the fixture application; it uses the built package, so build first.
|
|
559
607
|
|
|
560
608
|
To try the package in a Nuxt app, build it and run `bunx nuxt dev tests/fixtures/nuxt`.
|
|
561
609
|
The example app uses the built package, so rebuild after changing its source.
|
|
562
610
|
|
|
563
|
-
|
|
611
|
+
From the repository root, install the shared test browser with `bunx playwright install chromium-headless-shell`.
|
|
612
|
+
Then run `bun run test:nuxt` from this package's directory to check the packed npm archive with the Nuxt version installed in the workspace.
|
|
564
613
|
It is checked with both module-managed and app-managed QueryClients, including types, SSR, and hydration in development and production.
|
|
565
614
|
|
|
566
615
|
To check other Nuxt versions by hand, pass them explicitly: `bun run test:nuxt 3.14.1592 4.0.1`.
|
package/dist/module.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as nuxt_schema from 'nuxt/schema';
|
|
2
2
|
import { StaticQueryClientConfig } from '../dist/runtime/nuxt/query-config.js';
|
|
3
3
|
export { StaticQueryClientConfig } from '../dist/runtime/nuxt/query-config.js';
|
|
4
4
|
export { ORPCRuntimeHooks as ModuleRuntimeHooks } from '../dist/runtime/nuxt/hooks.js';
|
|
@@ -8,7 +8,7 @@ interface ModuleOptions {
|
|
|
8
8
|
/** Set static query defaults, or use false when another plugin owns Vue Query and hydration. */
|
|
9
9
|
queryClient?: boolean | StaticQueryClientConfig;
|
|
10
10
|
}
|
|
11
|
-
declare const _default:
|
|
11
|
+
declare const _default: nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
12
12
|
|
|
13
13
|
export { _default as default };
|
|
14
14
|
export type { ModuleOptions };
|
package/dist/module.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type AnyORPCError } from "@orpc/client";
|
|
2
|
+
type ErrorOf<TPromise extends Promise<unknown>> = TPromise extends {
|
|
3
|
+
__error?: {
|
|
4
|
+
type: infer Error;
|
|
5
|
+
};
|
|
6
|
+
} ? Error : never;
|
|
7
|
+
type DefinedError<TPromise extends Promise<unknown>> = Extract<ErrorOf<TPromise>, AnyORPCError>;
|
|
8
|
+
type DefinedErrorCode<TPromise extends Promise<unknown>> = DefinedError<TPromise>["code"] & string;
|
|
9
|
+
type ErrorResultValue = string | number | boolean | bigint | symbol | null | undefined | readonly unknown[] | {
|
|
10
|
+
readonly [key: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
type DefinedErrorHandlers<TPromise extends Promise<unknown>> = Partial<{
|
|
13
|
+
[Code in DefinedErrorCode<TPromise>]: ((error: Extract<DefinedError<TPromise>, {
|
|
14
|
+
code: Code;
|
|
15
|
+
}>) => unknown) | ErrorResultValue;
|
|
16
|
+
}>;
|
|
17
|
+
type HandledResult<Handlers> = {
|
|
18
|
+
[Code in keyof Handlers]: Handlers[Code] extends (...args: never[]) => infer Result ? Awaited<Result> : Awaited<Handlers[Code]>;
|
|
19
|
+
}[keyof Handlers];
|
|
20
|
+
/**
|
|
21
|
+
* Handle selected errors declared by an oRPC procedure and rethrow every other rejection.
|
|
22
|
+
* The promise must come directly from a typed client call so its declared error union is preserved.
|
|
23
|
+
* Each handler receives the declared error branch narrowed to its own code.
|
|
24
|
+
* A non-function value is returned directly when its error code matches.
|
|
25
|
+
* Synchronous and asynchronous handler results are included in the returned promise type.
|
|
26
|
+
*
|
|
27
|
+
* @param promise - The `PromiseWithError` returned by an oRPC client procedure.
|
|
28
|
+
* @param handlers - Handlers keyed by declared error code.
|
|
29
|
+
* @returns The procedure output or the result of the matching handler.
|
|
30
|
+
*/
|
|
31
|
+
export declare function catchORPCError<TPromise extends Promise<unknown>, Handlers extends object & DefinedErrorHandlers<NoInfer<TPromise>>>(promise: TPromise, handlers: "__error" extends keyof TPromise ? Handlers & Record<Exclude<keyof Handlers, DefinedErrorCode<NoInfer<TPromise>>>, never> : never): Promise<Awaited<TPromise> | HandledResult<Handlers>>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { isDefinedError } from "@orpc/client";
|
|
2
|
+
export function catchORPCError(promise, handlers) {
|
|
3
|
+
return promise.catch((error) => {
|
|
4
|
+
const orpcError = error;
|
|
5
|
+
if (!isDefinedError(orpcError)) throw error;
|
|
6
|
+
if (!Object.hasOwn(handlers, orpcError.code)) throw error;
|
|
7
|
+
const handler = handlers[orpcError.code];
|
|
8
|
+
return typeof handler === "function" ? handler(orpcError) : handler;
|
|
9
|
+
});
|
|
10
|
+
}
|
package/dist/runtime/client.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { createORPCNuxtClient } from "./client/create.js";
|
|
2
|
+
export { catchORPCError } from "./client/error.js";
|
|
2
3
|
export type { AwaitableQuery, ORPCMutationOptions, ORPCNuxtClient, ORPCNuxtClientOptions, ORPCQueryOptions, ORPCQueryResult, ORPCSelectedQueryResult, } from "./types.js";
|
package/dist/runtime/client.js
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
|
-
import type { AnyNestedClient, Client } from "@orpc/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>;
|
|
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
|
+
}
|
|
6
26
|
/** A procedure implementation registered for a test client. */
|
|
7
|
-
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;
|
|
8
29
|
/** Registration methods for one procedure in a test client. */
|
|
9
30
|
export interface TestORPCProcedure<TProcedure> {
|
|
10
31
|
/** Register the implementation used by subsequent calls and return its Vitest mock. */
|
|
11
|
-
handle(handler: TestORPCHandler<TProcedure>): Mock<
|
|
32
|
+
handle(handler: TestORPCHandler<TProcedure>): Mock<TestORPCMockHandler<TProcedure>>;
|
|
12
33
|
}
|
|
13
34
|
/** A router-shaped tree whose procedure leaves register test implementations. */
|
|
14
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,4 +1,4 @@
|
|
|
1
|
-
import { createORPCClient } 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";
|
|
@@ -11,7 +11,8 @@ export function createTestORPCClient(options = {}) {
|
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
function registerHandler(path, handler) {
|
|
14
|
-
const
|
|
14
|
+
const options2 = { errors: createErrorConstructors() };
|
|
15
|
+
const mock = vi.fn((input) => handler(input, options2));
|
|
15
16
|
registrations.set(path, mock);
|
|
16
17
|
return mock;
|
|
17
18
|
}
|
|
@@ -50,6 +51,22 @@ export function createTestORPCClient(options = {}) {
|
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
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
|
+
}
|
|
53
70
|
function createRecursiveProxy(path, call) {
|
|
54
71
|
const target = () => {
|
|
55
72
|
};
|
|
@@ -8,4 +8,4 @@ import { type QueryClient } from "@tanstack/vue-query";
|
|
|
8
8
|
* @param options - Mutation options, optionally wrapped in a ref or getter.
|
|
9
9
|
* @param queryClient - An explicit cache owner; otherwise Vue Query uses the injected client.
|
|
10
10
|
*/
|
|
11
|
-
export declare function useORPCMutation(target: object, options: unknown, queryClient?: QueryClient): import("@tanstack/vue-query").UseMutationReturnType<unknown, Error, unknown, unknown, Omit<import("@tanstack/query
|
|
11
|
+
export declare function useORPCMutation(target: object, options: unknown, queryClient?: QueryClient): import("@tanstack/vue-query").UseMutationReturnType<unknown, Error, unknown, unknown, Omit<import("@tanstack/vue-query").MutationObserverIdleResult<unknown, Error, unknown, unknown>, "mutate" | "reset"> | Omit<import("@tanstack/vue-query").MutationObserverLoadingResult<unknown, Error, unknown, unknown>, "mutate" | "reset"> | Omit<import("@tanstack/vue-query").MutationObserverErrorResult<unknown, Error, unknown, unknown>, "mutate" | "reset"> | Omit<import("@tanstack/vue-query").MutationObserverSuccessResult<unknown, Error, unknown, unknown>, "mutate" | "reset">>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orpc-nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"author": "Ilya Semenov",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/IlyaSemenov/orpc-nuxt.git"
|
|
11
|
+
"url": "git+https://github.com/IlyaSemenov/orpc-nuxt.git",
|
|
12
|
+
"directory": "packages/orpc-nuxt"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
15
|
"README.md",
|
|
@@ -45,14 +46,11 @@
|
|
|
45
46
|
},
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "nuxt-module-build build && publint",
|
|
48
|
-
"lint": "oxlint --fix && oxfmt",
|
|
49
|
-
"lint:check": "oxlint && oxfmt --check",
|
|
50
|
-
"prepare": "lefthook install",
|
|
51
|
-
"prepublishOnly": "bun run build",
|
|
52
49
|
"test": "bun test src tests/runtime",
|
|
53
50
|
"test:component": "vitest run --root tests/fixtures/nuxt",
|
|
54
51
|
"test:nuxt": "bun tests/nuxt/run.ts",
|
|
55
|
-
"types": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json && nuxt typecheck tests/fixtures/nuxt"
|
|
52
|
+
"types": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json && nuxt typecheck tests/fixtures/nuxt",
|
|
53
|
+
"prepublishOnly": "bun run build"
|
|
56
54
|
},
|
|
57
55
|
"dependencies": {
|
|
58
56
|
"@nuxt/kit": "^3.14.1592 || ^4.0.1",
|
|
@@ -60,23 +58,23 @@
|
|
|
60
58
|
"es-toolkit": "^1.52.0"
|
|
61
59
|
},
|
|
62
60
|
"devDependencies": {
|
|
63
|
-
"@changesets/cli": "^2.31.1",
|
|
64
61
|
"@nuxt/cli": "^3.37.0",
|
|
65
62
|
"@nuxt/module-builder": "^1.0.3",
|
|
66
63
|
"@nuxt/test-utils": "^4.3.2",
|
|
64
|
+
"@orpc/client": "^2.0.0-beta.35",
|
|
67
65
|
"@orpc/server": "2.0.0-beta.35",
|
|
68
|
-
"@
|
|
66
|
+
"@orpc/tanstack-query": "^2.0.0-beta.35",
|
|
67
|
+
"@tanstack/vue-query": "^5.102.8",
|
|
69
68
|
"@tsconfig/bun": "^1.0.10",
|
|
70
69
|
"@types/bun": "^1.3.14",
|
|
71
70
|
"@vue/server-renderer": "^3.5.0",
|
|
72
71
|
"@vue/test-utils": "^2.5.0",
|
|
73
72
|
"happy-dom": "^20.14.3",
|
|
74
73
|
"nuxt": "^4.5.2",
|
|
75
|
-
"oxfmt": "^0.67.0",
|
|
76
|
-
"oxlint": "^1.82.0",
|
|
77
74
|
"publint": "^0.3.22",
|
|
78
75
|
"typescript": "^5.9.3",
|
|
79
76
|
"vitest": "^5.0.0",
|
|
77
|
+
"vue": "^3.5.0",
|
|
80
78
|
"vue-tsc": "^3.3.11",
|
|
81
79
|
"zod": "^4.3.6"
|
|
82
80
|
},
|
|
@@ -92,6 +90,5 @@
|
|
|
92
90
|
"vitest": {
|
|
93
91
|
"optional": true
|
|
94
92
|
}
|
|
95
|
-
}
|
|
96
|
-
"packageManager": "bun@1.3.14"
|
|
93
|
+
}
|
|
97
94
|
}
|