orpc-nuxt 0.3.1 → 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 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.
@@ -289,9 +322,42 @@ Relative URLs resolve against the current request URL during SSR.
289
322
  Only headers listed in `forwardHeaders` are forwarded from the incoming SSR request.
290
323
  `credentials: "include"` allows browser cookies on cross-origin requests.
291
324
 
325
+ ### Custom link
326
+
327
+ Return a custom `link` when the server needs a request-scoped transport while the browser should keep a regular HTTP transport:
328
+
329
+ ```ts
330
+ // app/plugins/orpc.ts
331
+ import { RPCLink } from "@orpc/client/fetch"
332
+ import type { RouterClient } from "@orpc/server"
333
+ import { getRequestURL } from "h3"
334
+ import { defineNuxtPlugin } from "orpc-nuxt/plugin"
335
+ import type { router } from "~~/server/rpc/router"
336
+
337
+ export default defineNuxtPlugin<RouterClient<typeof router>>(() => ({
338
+ link: ({ event }) => {
339
+ if (!event) return new RPCLink({ url: "/api/rpc" })
340
+
341
+ const requestFetch = event.context.nuxtMultiApp.createFetch("web")
342
+ const endpoint = new URL("/api/rpc", getRequestURL(event))
343
+
344
+ return new RPCLink({
345
+ origin: endpoint.origin,
346
+ url: endpoint.pathname as `/${string}`,
347
+ fetch: requestFetch,
348
+ })
349
+ },
350
+ }))
351
+ ```
352
+
353
+ The link factory receives `{ nuxtApp, event }`, where `event` is the current H3 event during SSR and `undefined` in the browser.
354
+ It runs once per Nuxt application, so every SSR request gets its own link.
355
+ The plugin setup callback has the same lifecycle: once per SSR request and once when the browser app starts.
356
+ `orpc-nuxt` still creates the typed client, decorates it with Vue Query composables, and provides `$orpc`.
357
+
292
358
  ### Manual client setup
293
359
 
294
- Use `createORPCNuxtClient` when you need a custom transport or want SSR to call the router directly.
360
+ Use `createORPCNuxtClient` when you want SSR to call the router directly or need to own the complete client setup.
295
361
  Instead of the shared HTTP plugin above, add a browser plugin and a server plugin.
296
362
 
297
363
  Keep `orpc-nuxt` in `modules`: manual setup replaces that plugin, not the module that installs the QueryClient and the composables.
@@ -491,6 +557,19 @@ test("renders the posts", async () => {
491
557
  })
492
558
  ```
493
559
 
560
+ Use `createORPCError()` when a fake procedure must reproduce a declared rejection:
561
+
562
+ ```ts
563
+ import { createORPCError } from "orpc-nuxt/testing"
564
+
565
+ procedures.blog.posts.update.handle(() => {
566
+ throw createORPCError("CONFLICT", "Already exists", { field: "title" })
567
+ })
568
+ ```
569
+
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
+
494
573
  ### Outside Vue components
495
574
 
496
575
  You can call `.useQuery()` and `.useMutation()` outside a component, for example in a script or in a test that never mounts one.
@@ -520,14 +599,16 @@ try {
520
599
 
521
600
  ## Development
522
601
 
523
- Install dependencies with `bun install`, then run `bun run build`, `bun run types`, and `bun run test`.
602
+ Install dependencies from the repository root with `bun install`.
603
+ Run `bun run build`, `bun run types`, and `bun run test` from this package's directory.
524
604
 
525
605
  Run `bun run test:component` to check the documented component-test recipe in the fixture application; it uses the built package, so build first.
526
606
 
527
607
  To try the package in a Nuxt app, build it and run `bunx nuxt dev tests/fixtures/nuxt`.
528
608
  The example app uses the built package, so rebuild after changing its source.
529
609
 
530
- Run `bunx playwright install chromium-headless-shell`, then `bun run test:nuxt` to check the packed npm archive with the Nuxt version installed in the workspace.
610
+ From the repository root, install the shared test browser with `bunx playwright install chromium-headless-shell`.
611
+ 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.
531
612
  It is checked with both module-managed and app-managed QueryClients, including types, SSR, and hydration in development and production.
532
613
 
533
614
  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 _nuxt_schema from '@nuxt/schema';
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: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
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
@@ -4,9 +4,9 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.14.1592 || ^4.0.1"
6
6
  },
7
- "version": "0.3.1",
7
+ "version": "0.5.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
- "unbuild": "3.6.1"
10
+ "unbuild": "unknown"
11
11
  }
12
12
  }
@@ -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
+ }
@@ -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";
@@ -1 +1,2 @@
1
1
  export { createORPCNuxtClient } from "./client/create.js";
2
+ export { catchORPCError } from "./client/error.js";
@@ -1,8 +1,23 @@
1
- import { type AnyNestedClient } from "@orpc/client";
1
+ import { type AnyNestedClient, type ClientLink, type InferClientContext } from "@orpc/client";
2
2
  import { type NuxtApp, type Plugin } from "nuxt/app";
3
3
  import type { ORPCNuxtClient, ORPCNuxtClientOptions } from "../types.js";
4
- /** Configure the HTTP transport and cache used by a Nuxt app's injected oRPC client. */
5
- export interface OrpcPluginOptions extends ORPCNuxtClientOptions {
4
+ /** Nuxt state available while creating one app's custom oRPC link. */
5
+ export interface OrpcLinkContext {
6
+ /** The Nuxt application that will own the client. */
7
+ nuxtApp: NuxtApp;
8
+ /** The current H3 request event during SSR, or undefined in the browser. */
9
+ event: NonNullable<NuxtApp["ssrContext"]>["event"] | undefined;
10
+ }
11
+ /** Configure the transport and cache used by a Nuxt app's injected oRPC client. */
12
+ export type OrpcPluginOptions<T extends AnyNestedClient = AnyNestedClient> = ORPCNuxtClientOptions & ({
13
+ /** Use an application-owned link or create one for each Nuxt application. */
14
+ link: ClientLink<InferClientContext<T>> | ((context: OrpcLinkContext) => ClientLink<InferClientContext<T>>);
15
+ url?: never;
16
+ serverUrl?: never;
17
+ credentials?: never;
18
+ forwardHeaders?: never;
19
+ } | {
20
+ link?: never;
6
21
  /** RPC handler URL, including its path; relative URLs resolve against the current page URL. */
7
22
  url: string;
8
23
  /** Optional SSR handler URL; an absent or empty value falls back to url. */
@@ -11,13 +26,14 @@ export interface OrpcPluginOptions extends ORPCNuxtClientOptions {
11
26
  credentials?: RequestCredentials;
12
27
  /** Incoming headers to forward during SSR; only explicitly listed headers are forwarded. */
13
28
  forwardHeaders?: readonly string[];
14
- }
29
+ });
15
30
  /**
16
- * Create an HTTP client per Nuxt app and provide it as $orpc with inferred router types.
31
+ * Create an oRPC client per Nuxt app and provide it as $orpc with inferred router types.
17
32
  * The setup callback runs once per SSR request and once when the browser app starts.
33
+ * A custom link factory also runs once per app and receives its Nuxt app and H3 event.
18
34
  * Install Vue Query before this plugin, or return an explicit queryClient from setup.
19
35
  * Its QueryClient is captured immediately, so invalidate works before any composable runs.
20
36
  */
21
- export declare function defineNuxtPlugin<T extends AnyNestedClient>(setup: (nuxtApp: NuxtApp) => OrpcPluginOptions): Plugin<{
37
+ export declare function defineNuxtPlugin<T extends AnyNestedClient>(setup: (nuxtApp: NuxtApp) => OrpcPluginOptions<T>): Plugin<{
22
38
  orpc: ORPCNuxtClient<T>;
23
39
  }>;
@@ -1,4 +1,6 @@
1
- import { createORPCClient } from "@orpc/client";
1
+ import {
2
+ createORPCClient
3
+ } from "@orpc/client";
2
4
  import { RPCLink } from "@orpc/client/fetch";
3
5
  import {
4
6
  defineNuxtPlugin as createNuxtPlugin,
@@ -16,25 +18,7 @@ export function defineNuxtPlugin(setup) {
16
18
  "orpc-nuxt: install Vue Query before the oRPC plugin. Enable the module's QueryClient, use enforce: 'pre' in your Vue Query plugin, or pass queryClient explicitly."
17
19
  );
18
20
  }
19
- const url = import.meta.server ? options.serverUrl || options.url : options.url;
20
- const endpoint = new URL(url, useRequestURL());
21
- if (endpoint.protocol !== "http:" && endpoint.protocol !== "https:") {
22
- throw new Error("orpc-nuxt: the RPC handler URL must use HTTP or HTTPS.");
23
- }
24
- const link = new RPCLink({
25
- // oRPC v2 accepts the origin separately from the handler path and query string.
26
- origin: endpoint.origin,
27
- // HTTP URL.pathname always starts with the slash required by StandardUrl.
28
- url: `${endpoint.pathname}${endpoint.search}`,
29
- // An explicit empty list is essential: undefined would forward every incoming header.
30
- headers: useRequestHeaders([...options.forwardHeaders ?? []]),
31
- fetch(url2, init) {
32
- return globalThis.fetch(url2, {
33
- ...init,
34
- credentials: options.credentials
35
- });
36
- }
37
- });
21
+ const link = typeof options.link === "function" ? options.link({ nuxtApp, event: nuxtApp.ssrContext?.event }) : options.link ?? createHTTPLink(options);
38
22
  const client = createORPCClient(link);
39
23
  const orpc = createORPCNuxtClient(client, {
40
24
  prefix: options.prefix,
@@ -45,3 +29,24 @@ export function defineNuxtPlugin(setup) {
45
29
  };
46
30
  });
47
31
  }
32
+ function createHTTPLink(options) {
33
+ const url = import.meta.server ? options.serverUrl || options.url : options.url;
34
+ const endpoint = new URL(url, useRequestURL());
35
+ if (endpoint.protocol !== "http:" && endpoint.protocol !== "https:") {
36
+ throw new Error("orpc-nuxt: the RPC handler URL must use HTTP or HTTPS.");
37
+ }
38
+ return new RPCLink({
39
+ // oRPC v2 accepts the origin separately from the handler path and query string.
40
+ origin: endpoint.origin,
41
+ // HTTP URL.pathname always starts with the slash required by StandardUrl.
42
+ url: `${endpoint.pathname}${endpoint.search}`,
43
+ // An explicit empty list is essential: undefined would forward every incoming header.
44
+ headers: useRequestHeaders([...options.forwardHeaders ?? []]),
45
+ fetch(url2, init) {
46
+ return globalThis.fetch(url2, {
47
+ ...init,
48
+ credentials: options.credentials
49
+ });
50
+ }
51
+ });
52
+ }
@@ -1,2 +1,2 @@
1
1
  export { defineNuxtPlugin } from "./nuxt/define-plugin.js";
2
- export type { OrpcPluginOptions } from "./nuxt/define-plugin.js";
2
+ export type { OrpcLinkContext, OrpcPluginOptions } from "./nuxt/define-plugin.js";
@@ -1,8 +1,27 @@
1
- import type { AnyNestedClient, Client } from "@orpc/client";
1
+ import type { AnyNestedClient, Client, ORPCError, ORPCErrorCode } from "@orpc/client";
2
2
  import { QueryClient } from "@tanstack/vue-query";
3
3
  import type { Mock } from "vitest";
4
4
  import type { ORPCNuxtClient, ORPCNuxtClientOptions } from "./types.js";
5
5
  type MaybePromise<T> = T | Promise<T>;
6
+ /**
7
+ * Create an oRPC error marked as declared for a fake client procedure.
8
+ * The standalone helper cannot infer a specific procedure, so its code is not limited to that procedure's `.errors()` map.
9
+ *
10
+ * @param code - The error code exposed to the client.
11
+ * @param message - The human-readable error message.
12
+ * @returns An error that `isDefinedError()` and `catchORPCError()` recognize as declared.
13
+ */
14
+ export declare function createORPCError<Code extends ORPCErrorCode>(code: Code, message: string): ORPCError<Code, undefined>;
15
+ /**
16
+ * Create an oRPC error with typed data marked as declared for a fake client procedure.
17
+ * The standalone helper cannot infer a specific procedure, so its code and data are not checked against that procedure's `.errors()` map.
18
+ *
19
+ * @param code - The error code exposed to the client.
20
+ * @param message - The human-readable error message.
21
+ * @param data - Error data exposed to the client.
22
+ * @returns An error that `isDefinedError()` and `catchORPCError()` recognize as declared.
23
+ */
24
+ export declare function createORPCError<Code extends ORPCErrorCode, Data>(code: Code, message: string, data: Data): ORPCError<Code, Data>;
6
25
  /** A procedure implementation registered for a test client. */
7
26
  export type TestORPCHandler<TProcedure> = TProcedure extends Client<infer _Context, infer Input, infer Output, infer _Error> ? (input: Input) => MaybePromise<Awaited<Output>> : never;
8
27
  /** Registration methods for one procedure in a test client. */
@@ -1,7 +1,10 @@
1
- import { createORPCClient } from "@orpc/client";
1
+ import { createORPCClient, createORPCErrorFromJson } 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
+ }
5
8
  export function createTestORPCClient(options = {}) {
6
9
  const registrations = /* @__PURE__ */ new Map();
7
10
  const queryClient = options.queryClient ?? new QueryClient({
@@ -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-core").MutationObserverIdleResult<unknown, Error, unknown, unknown>, "mutate" | "reset"> | Omit<import("@tanstack/query-core").MutationObserverLoadingResult<unknown, Error, unknown, unknown>, "mutate" | "reset"> | Omit<import("@tanstack/query-core").MutationObserverErrorResult<unknown, Error, unknown, unknown>, "mutate" | "reset"> | Omit<import("@tanstack/query-core").MutationObserverSuccessResult<unknown, Error, unknown, unknown>, "mutate" | "reset">>;
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.1",
3
+ "version": "0.5.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
- "@playwright/test": "1.58.2",
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
  }