diff --git a/.github/workflows/release-pr.yaml b/.github/workflows/release-pr.yaml index 1baa2328..9c005632 100644 --- a/.github/workflows/release-pr.yaml +++ b/.github/workflows/release-pr.yaml @@ -35,7 +35,7 @@ jobs: with: issue-number: ${{ github.event.pull_request.number }} comment-author: 'github-actions[bot]' - body-includes: '' + body-includes: - name: Create or update pr-release comment with in process status uses: peter-evans/create-or-update-comment@v4 @@ -147,4 +147,4 @@ jobs: - name: Exit with failed status if: steps.publish.outcome != 'success' - run: exit 1 \ No newline at end of file + run: exit 1 diff --git a/apps/content/app/(home)/layout.tsx b/apps/content/app/(home)/layout.tsx index 886afecf..c00bb56e 100644 --- a/apps/content/app/(home)/layout.tsx +++ b/apps/content/app/(home)/layout.tsx @@ -1,6 +1,6 @@ +import type { ReactNode } from 'react' import { baseOptions } from '@/app/layout.config' import { HomeLayout } from 'fumadocs-ui/layouts/home' -import type { ReactNode } from 'react' export default function Layout({ children, @@ -121,7 +121,8 @@ function Footer() {

- Built with ❤️ by{' '} + Built with ❤️ by + {' '} { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log('Server is available at http://localhost:2026') }) ``` diff --git a/apps/content/examples/contract.ts b/apps/content/examples/contract.ts index 9df1bedd..b6e58f80 100644 --- a/apps/content/examples/contract.ts +++ b/apps/content/examples/contract.ts @@ -2,6 +2,19 @@ import { oc } from '@orpc/contract' import { oz } from '@orpc/zod' import { z } from 'zod' +// Implement the contract + +import { ORPCError, os } from '@orpc/server' + +// Expose apis to the internet with fetch handler + +import { createFetchHandler } from '@orpc/server/fetch' + +// Modern runtime that support fetch api like deno, bun, cloudflare workers, even node can used + +import { createServer } from 'node:http' +import { createServerAdapter } from '@whatwg-node/server' + // Define your contract first // This contract can replace server router in most-case @@ -55,10 +68,6 @@ export const contract = oc.router({ }), }) -// Implement the contract - -import { os, ORPCError } from '@orpc/server' - export type Context = { user?: { id: string } } export const base = os.context() export const pub /** os with ... */ = base.contract(contract) // Ensure every implement must be match contract @@ -112,20 +121,11 @@ export const router = pub.router({ }, }) -// Expose apis to the internet with fetch handler - -import { createFetchHandler } from '@orpc/server/fetch' - const handler = createFetchHandler({ router, serverless: false, // set true will improve cold start times }) -// Modern runtime that support fetch api like deno, bun, cloudflare workers, even node can used - -import { createServer } from 'node:http' -import { createServerAdapter } from '@whatwg-node/server' - const server = createServer( createServerAdapter((request: Request) => { const url = new URL(request.url) @@ -143,7 +143,7 @@ const server = createServer( ) server.listen(2026, () => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log('Server is available at http://localhost:2026') }) diff --git a/apps/content/examples/middleware.ts b/apps/content/examples/middleware.ts index 00b1cb03..648df954 100644 --- a/apps/content/examples/middleware.ts +++ b/apps/content/examples/middleware.ts @@ -1,4 +1,4 @@ -import { os, ORPCError } from '@orpc/server' +import { ORPCError, os } from '@orpc/server' import { z } from 'zod' // in oRPC middleware is so powerful @@ -12,7 +12,7 @@ export const pub /** public access */ = os const start = Date.now() meta.onFinish((output, error) => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log(`middleware cost ${Date.now() - start}ms`) }) }) @@ -23,11 +23,11 @@ export const authMid = pub.middleware(async (input, context, meta) => { } meta.onSuccess((output) => {}) - meta.onSuccess((error) => {}) + meta.onSuccess((_error) => {}) meta.onFinish((output, error) => {}) - meta.path // for analyze - meta.procedure // for analyze + const _path = meta.path // for analyze + const _procedure = meta.procedure // for analyze return { context: { @@ -51,7 +51,7 @@ export const editPost = authed .input(z.object({ id: z.string() })) .output(z.string()) .use(canEditPost) // if input not match, will throw type error - .use(canEditPost, (old) => ({ id: old.id })) // Can map input if needed + .use(canEditPost, old => ({ id: old.id })) // Can map input if needed .use((input, context, meta) => { // If middleware create after .input and .output them will be typed diff --git a/apps/content/examples/open-api.ts b/apps/content/examples/open-api.ts index 4a7d931d..2366a23d 100644 --- a/apps/content/examples/open-api.ts +++ b/apps/content/examples/open-api.ts @@ -1,6 +1,9 @@ import { generateOpenAPI } from '@orpc/openapi' import { router } from 'examples/server' +// or generate from contract +import { contract } from 'examples/contract' + export const specFromServerRouter = generateOpenAPI({ router, info: { @@ -9,9 +12,6 @@ export const specFromServerRouter = generateOpenAPI({ }, }) -// or generate from contract -import { contract } from 'examples/contract' - export const specFromContractRouter = generateOpenAPI({ router: contract, info: { @@ -47,7 +47,7 @@ const _exampleSpec = { }, }, responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { @@ -80,7 +80,7 @@ const _exampleSpec = { }, ], responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { @@ -132,7 +132,7 @@ const _exampleSpec = { }, }, responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { diff --git a/apps/content/examples/react.ts b/apps/content/examples/react.ts index 1bcdc153..bde0496b 100644 --- a/apps/content/examples/react.ts +++ b/apps/content/examples/react.ts @@ -1,7 +1,6 @@ -import { createORPCReact } from '@orpc/react' import type { router } from 'examples/server' +import { createORPCReact } from '@orpc/react' // biome-ignore lint/correctness/noUnusedImports: -import * as React from 'react' -export const { orpc, ORPCContext } = - createORPCReact() +export const { orpc, ORPCContext } + = createORPCReact() diff --git a/apps/content/examples/server-action.tsx b/apps/content/examples/server-action.tsx index 6e059508..f19dae45 100644 --- a/apps/content/examples/server-action.tsx +++ b/apps/content/examples/server-action.tsx @@ -1,11 +1,11 @@ 'use server' -// biome-ignore lint/correctness/noUnusedImports: -import * as React from 'react' - import { os } from '@orpc/server' + import { oz } from '@orpc/zod' import { redirect } from 'next/navigation' +// biome-ignore lint/correctness/noUnusedImports: +import * as React from 'react' import { z } from 'zod' export const createPost = os diff --git a/apps/content/examples/server.ts b/apps/content/examples/server.ts index b2b6126d..2034e082 100644 --- a/apps/content/examples/server.ts +++ b/apps/content/examples/server.ts @@ -1,7 +1,16 @@ -import { os, ORPCError } from '@orpc/server' +import { ORPCError, os } from '@orpc/server' import { oz } from '@orpc/zod' import { z } from 'zod' +// Expose apis to the internet with fetch handler + +import { createFetchHandler } from '@orpc/server/fetch' + +// Modern runtime that support fetch api like deno, bun, cloudflare workers, even node can used + +import { createServer } from 'node:http' +import { createServerAdapter } from '@whatwg-node/server' + export type Context = { user?: { id: string } } // global pub, authed completely optional @@ -75,7 +84,7 @@ export const router = pub.router({ }), ) .handler(async (input, context, meta) => { - input.thumb // file upload out of the box + const _thumb = input.thumb // file upload out of the box return { id: 'example', @@ -86,20 +95,11 @@ export const router = pub.router({ }), }) -// Expose apis to the internet with fetch handler - -import { createFetchHandler } from '@orpc/server/fetch' - const handler = createFetchHandler({ router, serverless: false, // set true will improve cold start times }) -// Modern runtime that support fetch api like deno, bun, cloudflare workers, even node can used - -import { createServer } from 'node:http' -import { createServerAdapter } from '@whatwg-node/server' - const server = createServer( createServerAdapter((request: Request) => { const url = new URL(request.url) @@ -117,7 +117,7 @@ const server = createServer( ) server.listen(2026, () => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log('Server is available at http://localhost:2026') }) diff --git a/apps/content/lib/source.ts b/apps/content/lib/source.ts index 2e601648..7905732b 100644 --- a/apps/content/lib/source.ts +++ b/apps/content/lib/source.ts @@ -9,10 +9,11 @@ export const source = loader({ baseUrl: '/docs', source: createMDXSource(docs, meta), icon(icon) { - if (icon && icon in icons) + if (icon && icon in icons) { return createElement(IconContainer, { icon: icons[icon as keyof typeof icons], }) + } }, }) diff --git a/apps/content/tsconfig.json b/apps/content/tsconfig.json index 1bb76006..0b35c5c4 100644 --- a/apps/content/tsconfig.json +++ b/apps/content/tsconfig.json @@ -1,23 +1,23 @@ { "compilerOptions": { - "baseUrl": ".", + "incremental": true, "target": "ESNext", + "jsx": "preserve", "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, + "baseUrl": ".", "module": "esnext", "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, "paths": { "@/*": ["./*"] }, + "resolveJsonModule": true, + "allowJs": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "skipLibCheck": true, "plugins": [ { "name": "next" diff --git a/eslint.config.js b/eslint.config.js index e6b09160..fcde1927 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,7 +1,6 @@ import antfu from '@antfu/eslint-config' export default antfu({ - type: 'lib', react: true, formatters: true, }, { @@ -16,11 +15,6 @@ export default antfu({ 'unused-imports/no-unused-vars': 'off', 'antfu/no-top-level-await': 'off', }, -}, { - files: ['apps/**', 'playgrounds/**'], - rules: { - 'ts/explicit-function-return-type': 'off', - }, }, { files: ['playgrounds/**'], rules: { diff --git a/packages/client/package.json b/packages/client/package.json index f0135562..8714960d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -40,15 +40,15 @@ "build:watch": "pnpm run build --watch", "type:check": "tsc -b" }, - "devDependencies": { - "zod": "^3.23.8" + "peerDependencies": { + "@orpc/contract": "workspace:*", + "@orpc/server": "workspace:*" }, "dependencies": { "@orpc/shared": "workspace:*", "@orpc/transformer": "workspace:*" }, - "peerDependencies": { - "@orpc/contract": "workspace:*", - "@orpc/server": "workspace:*" + "devDependencies": { + "zod": "^3.23.8" } } diff --git a/packages/client/src/procedure.test.ts b/packages/client/src/procedure.test.ts index 6a9593d0..58dd57b9 100644 --- a/packages/client/src/procedure.test.ts +++ b/packages/client/src/procedure.test.ts @@ -1,4 +1,4 @@ -import { os, ORPCError } from '@orpc/server' +import { ORPCError, os } from '@orpc/server' import { createFetchHandler } from '@orpc/server/fetch' import { z } from 'zod' import { createProcedureClient } from './procedure' @@ -111,7 +111,7 @@ describe('createProcedureClient', () => { const router = os.router({ ping: os .input(z.object({ value: z.date() })) - .handler((input) => input.value), + .handler(input => input.value), }) const handler = createFetchHandler({ @@ -167,7 +167,8 @@ describe('createProcedureClient', () => { let error: any try { await client(undefined) - } catch (e) { + } + catch (e) { error = e } diff --git a/packages/client/src/procedure.ts b/packages/client/src/procedure.ts index 210cb607..15ff6e83 100644 --- a/packages/client/src/procedure.ts +++ b/packages/client/src/procedure.ts @@ -1,6 +1,7 @@ /// /// +import type { Promisable } from '@orpc/shared' import { ORPC_HEADER, ORPC_HEADER_VALUE, @@ -8,7 +9,6 @@ import { type SchemaInput, type SchemaOutput, } from '@orpc/contract' -import type { Promisable } from '@orpc/shared' import { trim } from '@orpc/shared' import { ORPCError } from '@orpc/shared/error' import { ORPCDeserializer, ORPCSerializer } from '@orpc/transformer' @@ -80,7 +80,8 @@ export function createProcedureClient< const json = await (async () => { try { return await deserializer.deserialize(response) - } catch (e) { + } + catch (e) { throw new ORPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Cannot parse response.', @@ -91,8 +92,8 @@ export function createProcedureClient< if (!response.ok) { throw ( - ORPCError.fromJSON(json) ?? - new ORPCError({ + ORPCError.fromJSON(json) + ?? new ORPCError({ status: response.status, code: 'INTERNAL_SERVER_ERROR', message: 'Internal server error', diff --git a/packages/client/src/router.test.ts b/packages/client/src/router.test.ts index 4098b487..253ce467 100644 --- a/packages/client/src/router.test.ts +++ b/packages/client/src/router.test.ts @@ -113,7 +113,7 @@ describe('createRouterClient', () => { fetch: orpcFetch, }) - // @ts-expect-error + // @ts-expect-error - invalid input expect(client.ping({ value: {} })).rejects.toThrowError( 'Validation input failed', ) @@ -123,7 +123,7 @@ describe('createRouterClient', () => { const router = os.router({ ping: os .input(z.object({ value: z.date() })) - .handler((input) => input.value), + .handler(input => input.value), }) const handler = createFetchHandler({ diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index eb37795c..2b69b593 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -6,7 +6,7 @@ import type { SchemaOutput, } from '@orpc/contract' import type { Procedure, Promisable, Router } from '@orpc/server' -import { type ProcedureClient, createProcedureClient } from './procedure' +import { createProcedureClient, type ProcedureClient } from './procedure' export type RouterClientWithContractRouter = { [K in keyof TRouter]: TRouter[K] extends ContractProcedure< @@ -64,10 +64,10 @@ export function createRouterClient< >( options: CreateRouterClientOptions, ): TRouter extends Router - ? RouterClientWithRouter - : TRouter extends ContractRouter - ? RouterClientWithContractRouter - : never { + ? RouterClientWithRouter + : TRouter extends ContractRouter + ? RouterClientWithContractRouter + : never { const path = options?.path ?? [] const client = new Proxy( diff --git a/packages/contract/package.json b/packages/contract/package.json index 86930132..7cc73b1f 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -40,10 +40,10 @@ "build:watch": "pnpm run build --watch", "type:check": "tsc -b" }, - "dependencies": { - "@orpc/shared": "workspace:*" - }, "peerDependencies": { "zod": "^3" + }, + "dependencies": { + "@orpc/shared": "workspace:*" } } diff --git a/packages/contract/src/builder.test.ts b/packages/contract/src/builder.test.ts index c212f510..5223e3e2 100644 --- a/packages/contract/src/builder.test.ts +++ b/packages/contract/src/builder.test.ts @@ -2,7 +2,7 @@ import { z } from 'zod' import { type DecoratedContractProcedure, oc } from '.' describe('define a procedure', () => { - test('use route method', () => { + it('use route method', () => { const procedure = oc.route({ method: 'GET', path: '/users/{id}', @@ -26,7 +26,7 @@ describe('define a procedure', () => { }) }) - test('use input method', () => { + it('use input method', () => { const schema = z.object({ id: z.string(), }) @@ -43,7 +43,7 @@ describe('define a procedure', () => { }) }) - test('use output method', () => { + it('use output method', () => { const schema = z.object({ id: z.string() }) const procedure = oc.output(schema, { id: '123' }) diff --git a/packages/contract/src/builder.ts b/packages/contract/src/builder.ts index 1e3aac13..6604ab90 100644 --- a/packages/contract/src/builder.ts +++ b/packages/contract/src/builder.ts @@ -1,18 +1,18 @@ -import { DecoratedContractProcedure, type RouteOptions } from './procedure' import type { ContractRouter } from './router' -import { ContractRouterBuilder } from './router-builder' import type { HTTPPath, Schema, SchemaInput, SchemaOutput } from './types' +import { DecoratedContractProcedure, type RouteOptions } from './procedure' +import { ContractRouterBuilder } from './router-builder' export class ContractBuilder { prefix(prefix: HTTPPath): ContractRouterBuilder { return new ContractRouterBuilder({ - prefix: prefix, + prefix, }) } tags(...tags: string[]): ContractRouterBuilder { return new ContractRouterBuilder({ - tags: tags, + tags, }) } diff --git a/packages/contract/src/procedure.test.ts b/packages/contract/src/procedure.test.ts index 8cf5f4a0..d258902f 100644 --- a/packages/contract/src/procedure.test.ts +++ b/packages/contract/src/procedure.test.ts @@ -1,8 +1,8 @@ +import type { DecoratedContractProcedure } from './procedure' import { z } from 'zod' import { isContractProcedure, oc } from '.' -import type { DecoratedContractProcedure } from './procedure' -test('prefix method', () => { +it('prefix method', () => { const os = oc const p1 = os.route({ method: 'GET', @@ -17,7 +17,7 @@ test('prefix method', () => { expect(p2.prefix('/1').prefix('/2').zz$cp.path).toEqual(undefined) }) -test('route method', () => { +it('route method', () => { const p = oc .route({ method: 'POST', @@ -45,7 +45,7 @@ test('route method', () => { }) }) -test('input method', () => { +it('input method', () => { const schema = z.string() const p = oc.route({}).input(schema) @@ -59,7 +59,7 @@ test('input method', () => { }) }) -test('output method', () => { +it('output method', () => { const schema = z.string() const p = oc.route({}).output(schema) @@ -88,7 +88,7 @@ it('addTags method', () => { expect(p3.zz$cp.tags).toEqual(['foo', 'bar', 'baz']) }) -test('isContractProcedure function', () => { +it('isContractProcedure function', () => { expect(isContractProcedure(oc)).toBe(false) expect(isContractProcedure(oc.router({}))).toBe(false) expect(isContractProcedure(oc.route({}))).toBe(true) diff --git a/packages/contract/src/procedure.ts b/packages/contract/src/procedure.ts index f16f7909..69cbead8 100644 --- a/packages/contract/src/procedure.ts +++ b/packages/contract/src/procedure.ts @@ -42,7 +42,8 @@ export class DecoratedContractProcedure< static decorate( cp: ContractProcedure, ): DecoratedContractProcedure { - if (cp instanceof DecoratedContractProcedure) return cp + if (cp instanceof DecoratedContractProcedure) + return cp return new DecoratedContractProcedure(cp.zz$cp) } @@ -60,7 +61,8 @@ export class DecoratedContractProcedure< prefix( prefix: HTTPPath, ): DecoratedContractProcedure { - if (!this.zz$cp.path) return this + if (!this.zz$cp.path) + return this return new DecoratedContractProcedure({ ...this.zz$cp, @@ -71,7 +73,8 @@ export class DecoratedContractProcedure< addTags( ...tags: string[] ): DecoratedContractProcedure { - if (!tags.length) return this + if (!tags.length) + return this return new DecoratedContractProcedure({ ...this.zz$cp, @@ -107,15 +110,16 @@ export type WELL_DEFINED_CONTRACT_PROCEDURE = ContractProcedure export function isContractProcedure( item: unknown, ): item is WELL_DEFINED_CONTRACT_PROCEDURE { - if (item instanceof ContractProcedure) return true + if (item instanceof ContractProcedure) + return true return ( - (typeof item === 'object' || typeof item === 'function') && - item !== null && - 'zz$cp' in item && - typeof item.zz$cp === 'object' && - item.zz$cp !== null && - 'InputSchema' in item.zz$cp && - 'OutputSchema' in item.zz$cp + (typeof item === 'object' || typeof item === 'function') + && item !== null + && 'zz$cp' in item + && typeof item.zz$cp === 'object' + && item.zz$cp !== null + && 'InputSchema' in item.zz$cp + && 'OutputSchema' in item.zz$cp ) } diff --git a/packages/contract/src/router-builder.test.ts b/packages/contract/src/router-builder.test.ts index c1d1e354..8ace9892 100644 --- a/packages/contract/src/router-builder.test.ts +++ b/packages/contract/src/router-builder.test.ts @@ -2,15 +2,15 @@ import { z } from 'zod' import { ContractProcedure, DecoratedContractProcedure, oc } from '.' import { ContractRouterBuilder } from './router-builder' -test('prefix method', () => { +it('prefix method', () => { expect(oc.prefix('/1').prefix('/2').zz$crb.prefix).toEqual('/1/2') }) -test('tags method', () => { +it('tags method', () => { expect(oc.tags('1').tags('2').zz$crb.tags).toEqual(['1', '2']) }) -test('define a router', () => { +it('define a router', () => { const ping = oc.route({ method: 'GET', path: '/ping' }) const pong = oc.input(z.object({ id: z.string() })) @@ -22,11 +22,11 @@ test('define a router', () => { .prefix('/internal') .tags('internal') .router({ - ping: ping, - pong: pong, + ping, + pong, nested: { - ping: ping, + ping, }, }), }) diff --git a/packages/contract/src/router-builder.ts b/packages/contract/src/router-builder.ts index 34e1ef58..994f4af2 100644 --- a/packages/contract/src/router-builder.ts +++ b/packages/contract/src/router-builder.ts @@ -1,9 +1,9 @@ -import { DecoratedContractProcedure, isContractProcedure } from './procedure' import type { ContractRouter, HandledContractRouter } from './router' import type { HTTPPath } from './types' +import { DecoratedContractProcedure, isContractProcedure } from './procedure' export class ContractRouterBuilder { - constructor(public zz$crb: { prefix?: HTTPPath; tags?: string[] }) {} + constructor(public zz$crb: { prefix?: HTTPPath, tags?: string[] }) {} prefix(prefix: HTTPPath): ContractRouterBuilder { return new ContractRouterBuilder({ @@ -13,7 +13,8 @@ export class ContractRouterBuilder { } tags(...tags: string[]): ContractRouterBuilder { - if (!tags.length) return this + if (!tags.length) + return this return new ContractRouterBuilder({ ...this.zz$crb, @@ -34,7 +35,8 @@ export class ContractRouterBuilder { handled[key] = this.zz$crb.prefix ? decorated.prefix(this.zz$crb.prefix) : decorated - } else { + } + else { handled[key] = this.router(item as ContractRouter) } } diff --git a/packages/contract/src/router.test.ts b/packages/contract/src/router.test.ts index 7274d131..e4298c51 100644 --- a/packages/contract/src/router.test.ts +++ b/packages/contract/src/router.test.ts @@ -1,6 +1,6 @@ import { eachContractRouterLeaf, oc } from '.' -test('each router leaf', () => { +it('each router leaf', () => { const router = { ping: oc.route({ method: 'GET', diff --git a/packages/contract/src/router.ts b/packages/contract/src/router.ts index 2943a25f..ddd1a67b 100644 --- a/packages/contract/src/router.ts +++ b/packages/contract/src/router.ts @@ -1,8 +1,8 @@ import { type ContractProcedure, type DecoratedContractProcedure, - type WELL_DEFINED_CONTRACT_PROCEDURE, isContractProcedure, + type WELL_DEFINED_CONTRACT_PROCEDURE, } from './procedure' export interface ContractRouter { @@ -30,7 +30,8 @@ export function eachContractRouterLeaf( if (isContractProcedure(item)) { callback(item, [...prefix, key]) - } else { + } + else { eachContractRouterLeaf(item as ContractRouter, callback, [...prefix, key]) } } diff --git a/packages/contract/src/types.test-d.ts b/packages/contract/src/types.test-d.ts index 6e64acb8..db0503e8 100644 --- a/packages/contract/src/types.test-d.ts +++ b/packages/contract/src/types.test-d.ts @@ -1,5 +1,5 @@ -import { z } from 'zod' import type { SchemaInput, SchemaOutput } from './types' +import { z } from 'zod' test('SchemaInput', () => { const schema = z.string() @@ -9,7 +9,7 @@ test('SchemaInput', () => { }) test('SchemaOutput', () => { - const schema = z.string().transform((v) => Number.parseFloat(v)) + const schema = z.string().transform(v => Number.parseFloat(v)) expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() diff --git a/packages/contract/src/types.ts b/packages/contract/src/types.ts index 57314859..23bc2374 100644 --- a/packages/contract/src/types.ts +++ b/packages/contract/src/types.ts @@ -1,4 +1,4 @@ -import type { ZodType, input, output } from 'zod' +import type { input, output, ZodType } from 'zod' export type HTTPPath = `/${string}` export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' diff --git a/packages/contract/src/utils.test.ts b/packages/contract/src/utils.test.ts index 4f2819fd..182bd42a 100644 --- a/packages/contract/src/utils.test.ts +++ b/packages/contract/src/utils.test.ts @@ -1,13 +1,13 @@ import { prefixHTTPPath, standardizeHTTPPath } from './utils' -test('standardizeHTTPPath', () => { +it('standardizeHTTPPath', () => { expect(standardizeHTTPPath('/abc')).toBe('/abc') expect(standardizeHTTPPath('/abc/')).toBe('/abc') expect(standardizeHTTPPath('/abc//')).toBe('/abc') expect(standardizeHTTPPath('//abc//')).toBe('/abc') }) -test('prefixHTTPPath', () => { +it('prefixHTTPPath', () => { expect(prefixHTTPPath('/', '/abc')).toBe('/abc') expect(prefixHTTPPath('/', '/abc/')).toBe('/abc') expect(prefixHTTPPath('/', '/abc//')).toBe('/abc') diff --git a/packages/contract/src/utils.ts b/packages/contract/src/utils.ts index 869df062..aa11fd63 100644 --- a/packages/contract/src/utils.ts +++ b/packages/contract/src/utils.ts @@ -8,8 +8,10 @@ export function prefixHTTPPath(prefix: HTTPPath, path: HTTPPath): HTTPPath { const prefix_ = standardizeHTTPPath(prefix) const path_ = standardizeHTTPPath(path) - if (prefix_ === '/') return path_ - if (path_ === '/') return prefix_ + if (prefix_ === '/') + return path_ + if (path_ === '/') + return prefix_ return `${prefix_}${path_}` } diff --git a/packages/openapi/package.json b/packages/openapi/package.json index 56f3e96e..2e2ab124 100644 --- a/packages/openapi/package.json +++ b/packages/openapi/package.json @@ -40,10 +40,6 @@ "build:watch": "pnpm run build --watch", "type:check": "tsc -b" }, - "devDependencies": { - "@readme/openapi-parser": "^2.6.0", - "zod": "^3.23.8" - }, "dependencies": { "@orpc/contract": "workspace:*", "@orpc/server": "workspace:*", @@ -53,5 +49,9 @@ "escape-string-regexp": "^5.0.0", "json-schema-typed": "^8.0.1", "openapi3-ts": "^4.4.0" + }, + "devDependencies": { + "@readme/openapi-parser": "^2.6.0", + "zod": "^3.23.8" } } diff --git a/packages/openapi/src/generator.test.ts b/packages/openapi/src/generator.test.ts index a1ae4871..bbae37e5 100644 --- a/packages/openapi/src/generator.test.ts +++ b/packages/openapi/src/generator.test.ts @@ -1,6 +1,6 @@ +import type { OpenAPIObject } from 'openapi3-ts/oas31' import { oc } from '@orpc/contract' import { oz } from '@orpc/zod' -import type { OpenAPIObject } from 'openapi3-ts/oas31' import { z } from 'zod' import { generateOpenAPI } from './generator' @@ -36,7 +36,7 @@ it('works', () => { '/ping': { post: { responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { @@ -63,7 +63,7 @@ it('works', () => { }, ], responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { @@ -133,7 +133,7 @@ it('throwOnMissingTagDefinition option', () => { '/ping': { post: { responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { @@ -160,7 +160,7 @@ it('throwOnMissingTagDefinition option', () => { }, ], responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { @@ -237,7 +237,7 @@ it('support single file upload', () => { }, }, responses: { - '200': { + 200: { content: { 'image/jpg': { schema: { @@ -310,7 +310,7 @@ it('support multipart/form-data', () => { }, }, responses: { - '200': { + 200: { content: { 'image/*': { schema: { @@ -415,7 +415,7 @@ it('work with example', () => { }, }, responses: { - '200': { + 200: { content: { 'application/json': { schema: { @@ -527,7 +527,7 @@ it('should remove params on body', () => { }, }, responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { schema: {}, example: undefined }, @@ -629,7 +629,7 @@ it('should remove params on query', () => { ], requestBody: undefined, responses: { - '200': { + 200: { description: 'OK', content: { 'application/json': { schema: {}, example: undefined }, diff --git a/packages/openapi/src/generator.ts b/packages/openapi/src/generator.ts index f5a6a7f6..c3fa5489 100644 --- a/packages/openapi/src/generator.ts +++ b/packages/openapi/src/generator.ts @@ -1,20 +1,20 @@ +import type { JSONSchema } from 'json-schema-typed/draft-2020-12' import { type ContractRouter, eachContractRouterLeaf } from '@orpc/contract' import { type Router, toContractRouter } from '@orpc/server' import { findDeepMatches, isPlainObject, omit } from '@orpc/shared' import { preSerialize } from '@orpc/transformer' -import type { JSONSchema } from 'json-schema-typed/draft-2020-12' import { type MediaTypeObject, - type OpenAPIObject, OpenApiBuilder, + type OpenAPIObject, type OperationObject, type ParameterObject, type RequestBodyObject, type ResponseObject, } from 'openapi3-ts/oas31' import { - UNSUPPORTED_JSON_SCHEMA, extractJSONSchema, + UNSUPPORTED_JSON_SCHEMA, zodToJsonSchema, } from './zod-to-json-schema' @@ -45,17 +45,17 @@ export function generateOpenAPI( } & Omit, options?: GenerateOpenAPIOptions, ): OpenAPIObject { - const throwOnMissingTagDefinition = - options?.throwOnMissingTagDefinition ?? false - const ignoreUndefinedPathProcedures = - options?.ignoreUndefinedPathProcedures ?? false + const throwOnMissingTagDefinition + = options?.throwOnMissingTagDefinition ?? false + const ignoreUndefinedPathProcedures + = options?.ignoreUndefinedPathProcedures ?? false const builder = new OpenApiBuilder({ ...omit(opts, ['router']), openapi: '3.1.0', }) - const rootTags = opts.tags?.map((tag) => tag.name) ?? [] + const rootTags = opts.tags?.map(tag => tag.name) ?? [] const router = toContractRouter(opts.router) eachContractRouterLeaf(router, (procedure, path_) => { @@ -76,7 +76,7 @@ export function generateOpenAPI( : {} const params: ParameterObject[] | undefined = (() => { - const names = path.match(/{([^}]+)}/g) + const names = path.match(/\{([^}]+)\}/g) if (!names || !names.length) { return undefined @@ -89,7 +89,7 @@ export function generateOpenAPI( } return names - .map((raw) => raw.slice(1, -1)) + .map(raw => raw.slice(1, -1)) .map((name) => { let schema = inputSchema.properties?.[name] const required = inputSchema.required?.includes(name) @@ -127,19 +127,20 @@ export function generateOpenAPI( ...inputSchema, properties: inputSchema.properties ? Object.entries(inputSchema.properties).reduce( - (acc, [key, value]) => { - if (key !== name) { - acc[key] = value - } - - return acc - }, - {} as Record, - ) + (acc, [key, value]) => { + if (key !== name) { + acc[key] = value + } + + return acc + }, + {} as Record, + ) : undefined, - required: inputSchema.required?.filter((v) => v !== name), + required: inputSchema.required?.filter(v => v !== name), examples: inputSchema.examples?.map((example) => { - if (!isPlainObject(example)) return example + if (!isPlainObject(example)) + return example return Object.entries(example).reduce( (acc, [key, value]) => { @@ -220,8 +221,8 @@ export function generateOpenAPI( contentMediaType: string })[] - const isStillHasFileSchema = - findDeepMatches(isFileSchema, schema).values.length > 0 + const isStillHasFileSchema + = findDeepMatches(isFileSchema, schema).values.length > 0 if (files.length) { parameters.push({ @@ -268,8 +269,8 @@ export function generateOpenAPI( contentMediaType: string })[] - const isStillHasFileSchema = - findDeepMatches(isFileSchema, schema).values.length > 0 + const isStillHasFileSchema + = findDeepMatches(isFileSchema, schema).values.length > 0 const content: Record = {} @@ -295,7 +296,7 @@ export function generateOpenAPI( })() if (throwOnMissingTagDefinition && internal.tags) { - const missingTag = internal.tags.find((tag) => !rootTags.includes(tag)) + const missingTag = internal.tags.find(tag => !rootTags.includes(tag)) if (missingTag !== undefined) { throw new Error( @@ -313,7 +314,7 @@ export function generateOpenAPI( parameters: parameters.length ? parameters : undefined, requestBody, responses: { - '200': successResponse, + 200: successResponse, }, } @@ -326,11 +327,12 @@ export function generateOpenAPI( } function isFileSchema(schema: unknown) { - if (typeof schema !== 'object' || schema === null) return false + if (typeof schema !== 'object' || schema === null) + return false return ( - 'type' in schema && - 'contentMediaType' in schema && - typeof schema.type === 'string' && - typeof schema.contentMediaType === 'string' + 'type' in schema + && 'contentMediaType' in schema + && typeof schema.type === 'string' + && typeof schema.contentMediaType === 'string' ) } diff --git a/packages/openapi/src/zod-to-json-schema.test.ts b/packages/openapi/src/zod-to-json-schema.test.ts index 91ba392b..415ff2ac 100644 --- a/packages/openapi/src/zod-to-json-schema.test.ts +++ b/packages/openapi/src/zod-to-json-schema.test.ts @@ -244,7 +244,7 @@ describe('special types', () => { describe('transform and effects', () => { it('should handle transform effects based on mode', () => { - const schema = z.string().transform((val) => val.length) + const schema = z.string().transform(val => val.length) expect(zodToJsonSchema(schema, { mode: 'input' })).toEqual({ type: 'string', diff --git a/packages/openapi/src/zod-to-json-schema.ts b/packages/openapi/src/zod-to-json-schema.ts index b1d21089..9c90d5fa 100644 --- a/packages/openapi/src/zod-to-json-schema.ts +++ b/packages/openapi/src/zod-to-json-schema.ts @@ -14,11 +14,8 @@ import { type KeySchema, type ZodAny, type ZodArray, - type ZodBigInt, - type ZodBoolean, type ZodBranded, type ZodCatch, - type ZodDate, type ZodDefault, type ZodDiscriminatedUnion, type ZodEffects, @@ -28,7 +25,6 @@ import { type ZodLazy, type ZodLiteral, type ZodMap, - type ZodNaN, type ZodNativeEnum, type ZodNullable, type ZodNumber, @@ -213,8 +209,8 @@ export function zodToJsonSchema( json.pattern = `${escapeStringRegexp(check.value)}$` break case 'emoji': - json.pattern = - '^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$' + json.pattern + = '^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$' break case 'nanoid': json.pattern = '^[a-zA-Z0-9_-]{21}$' @@ -278,14 +274,10 @@ export function zodToJsonSchema( } case ZodFirstPartyTypeKind.ZodNaN: { - const schema_ = schema as ZodNaN - return { const: 'NaN' } } case ZodFirstPartyTypeKind.ZodBigInt: { - const schema_ = schema as ZodBigInt - const json: JSONSchema = { type: 'string', pattern: '^-?[0-9]+$' } // WARN: ignore checks @@ -294,14 +286,10 @@ export function zodToJsonSchema( } case ZodFirstPartyTypeKind.ZodBoolean: { - const schema_ = schema as ZodBoolean - return { type: 'boolean' } } case ZodFirstPartyTypeKind.ZodDate: { - const schema_ = schema as ZodDate - const jsonSchema: JSONSchema = { type: 'string', format: Format.Date } // WARN: ignore checks @@ -398,7 +386,7 @@ export function zodToJsonSchema( for (const [key, value] of Object.entries(schema_.shape)) { const { schema, matches } = extractJSONSchema( zodToJsonSchema(value, childOptions), - (schema) => schema === UNDEFINED_JSON_SCHEMA, + schema => schema === UNDEFINED_JSON_SCHEMA, ) if (schema) { @@ -423,14 +411,15 @@ export function zodToJsonSchema( childOptions, ) if (schema_._def.unknownKeys === 'strict') { - json.additionalProperties = - additionalProperties === UNSUPPORTED_JSON_SCHEMA + json.additionalProperties + = additionalProperties === UNSUPPORTED_JSON_SCHEMA ? false : additionalProperties - } else { + } + else { if ( - additionalProperties && - additionalProperties !== UNSUPPORTED_JSON_SCHEMA + additionalProperties + && additionalProperties !== UNSUPPORTED_JSON_SCHEMA ) { json.additionalProperties = additionalProperties } @@ -551,8 +540,8 @@ export function zodToJsonSchema( const schema_ = schema as ZodEffects if ( - schema_._def.effect.type === 'transform' && - childOptions?.mode === 'output' + schema_._def.effect.type === 'transform' + && childOptions?.mode === 'output' ) { return {} } @@ -602,7 +591,7 @@ export function extractJSONSchema( schema: JSONSchema, check: (schema: JSONSchema) => boolean, matches: JSONSchema[] = [], -): { schema: JSONSchema | undefined; matches: JSONSchema[] } { +): { schema: JSONSchema | undefined, matches: JSONSchema[] } { if (check(schema)) { matches.push(schema) return { schema: undefined, matches } @@ -615,14 +604,14 @@ export function extractJSONSchema( // TODO: $ref if ( - schema.anyOf && - Object.keys(schema).every( - (k) => k === 'anyOf' || NON_LOGIC_KEYWORDS.includes(k as any), + schema.anyOf + && Object.keys(schema).every( + k => k === 'anyOf' || NON_LOGIC_KEYWORDS.includes(k as any), ) ) { const anyOf = schema.anyOf - .map((s) => extractJSONSchema(s, check, matches).schema) - .filter((v) => !!v) + .map(s => extractJSONSchema(s, check, matches).schema) + .filter(v => !!v) if (anyOf.length === 1 && typeof anyOf[0] === 'object') { return { schema: { ...schema, anyOf: undefined, ...anyOf[0] }, matches } @@ -640,14 +629,14 @@ export function extractJSONSchema( // TODO: $ref if ( - schema.oneOf && - Object.keys(schema).every( - (k) => k === 'oneOf' || NON_LOGIC_KEYWORDS.includes(k as any), + schema.oneOf + && Object.keys(schema).every( + k => k === 'oneOf' || NON_LOGIC_KEYWORDS.includes(k as any), ) ) { const oneOf = schema.oneOf - .map((s) => extractJSONSchema(s, check, matches).schema) - .filter((v) => !!v) + .map(s => extractJSONSchema(s, check, matches).schema) + .filter(v => !!v) if (oneOf.length === 1 && typeof oneOf[0] === 'object') { return { schema: { ...schema, oneOf: undefined, ...oneOf[0] }, matches } diff --git a/packages/openapi/vitest.setup.ts b/packages/openapi/vitest.setup.ts index a176ae3e..4eb22dfc 100644 --- a/packages/openapi/vitest.setup.ts +++ b/packages/openapi/vitest.setup.ts @@ -1,12 +1,13 @@ +import type { generateOpenAPI } from './src/generator' import OpenAPIParser from '@readme/openapi-parser' import { expect, vi } from 'vitest' -import type { generateOpenAPI } from './src/generator' +// eslint-disable-next-line antfu/no-top-level-await const generator = await vi.importActual('./src/generator') vi.mock('./src/generator', () => ({ generateOpenAPI: vi.fn((...args) => { - // @ts-expect-error + // @ts-expect-error - untyped const spec = generator.generateOpenAPI(...args) expect( (async () => { diff --git a/packages/react/package.json b/packages/react/package.json index 8f6665d7..29e42a8b 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -40,16 +40,16 @@ "build:watch": "pnpm run build --watch", "type:check": "tsc -b" }, - "dependencies": { - "@orpc/client": "workspace:*", - "@orpc/shared": "workspace:*" - }, "peerDependencies": { "@orpc/contract": "workspace:*", "@orpc/server": "workspace:*", "@tanstack/react-query": "^5.59.9", "react": "^18.3.1" }, + "dependencies": { + "@orpc/client": "workspace:*", + "@orpc/shared": "workspace:*" + }, "devDependencies": { "@tanstack/react-query": "^5.59.9", "react": "^18.3.1", diff --git a/packages/react/src/general-hooks.test.tsx b/packages/react/src/general-hooks.test.tsx index 04cf2a8f..e0fa1408 100644 --- a/packages/react/src/general-hooks.test.tsx +++ b/packages/react/src/general-hooks.test.tsx @@ -3,10 +3,10 @@ import { useMutation, useQuery } from '@tanstack/react-query' import { renderHook } from '@testing-library/react' import { ORPCContext, + queryClient, type UserCreateInputSchema, type UserFindInputSchema, type UserSchema, - queryClient, wrapper, } from '../tests/orpc' import { createGeneralHooks } from './general-hooks' @@ -72,7 +72,7 @@ describe('useIsFetching', () => { { input: { id: '12333' }, type: 'query' }, ], queryFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise(resolve => setTimeout(resolve, 100)) }, }, queryClient, @@ -86,14 +86,14 @@ describe('useIsFetching', () => { { input: { id: '12333' }, type: 'infinite' }, ], queryFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise(resolve => setTimeout(resolve, 100)) }, }, queryClient, ), ) - await new Promise((resolve) => setTimeout(resolve, 50)) // < 100 make sure the query is not finished + await new Promise(resolve => setTimeout(resolve, 50)) // < 100 make sure the query is not finished expect(result.current).toBe(2) expect(result2.current).toBe(2) @@ -147,7 +147,7 @@ describe('useIsMutating', () => { { mutationKey: [['user', 'create']], mutationFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise(resolve => setTimeout(resolve, 100)) }, }, queryClient, @@ -156,7 +156,7 @@ describe('useIsMutating', () => { mutation.current.mutate() - await new Promise((resolve) => setTimeout(resolve, 50)) // < 100 make sure the query is not finished + await new Promise(resolve => setTimeout(resolve, 50)) // < 100 make sure the query is not finished expect(result.current).toBe(1) expect(result2.current).toBe(0) @@ -210,7 +210,7 @@ describe('useMutationState', () => { { mutationKey: [['user', 'create']], mutationFn: async () => { - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise(resolve => setTimeout(resolve, 100)) }, }, queryClient, @@ -219,7 +219,7 @@ describe('useMutationState', () => { mutation.current.mutate({ name: 'unnoq' } as any) - await new Promise((resolve) => setTimeout(resolve, 50)) // < 100 make sure the query is not finished + await new Promise(resolve => setTimeout(resolve, 50)) // < 100 make sure the query is not finished expect(result.current.length).toBe(1) expect(result2.current.length).toBe(0) diff --git a/packages/react/src/general-hooks.ts b/packages/react/src/general-hooks.ts index 34cc43f6..b7b4aab6 100644 --- a/packages/react/src/general-hooks.ts +++ b/packages/react/src/general-hooks.ts @@ -1,5 +1,6 @@ import type { Schema, SchemaInput, SchemaOutput } from '@orpc/contract' import type { PartialDeep, SetOptional } from '@orpc/shared' +import type { ORPCQueryFilters } from './tanstack-query' import { type DefaultError, type Mutation, @@ -11,19 +12,18 @@ import { } from '@tanstack/react-query' import { type ORPCContext, useORPCContext } from './react-context' import { getMutationKeyFromPath, getQueryKeyFromPath } from './tanstack-key' -import type { ORPCQueryFilters } from './tanstack-query' export interface GeneralHooks< TInputSchema extends Schema, TOutputSchema extends Schema, THandlerOutput extends SchemaOutput, > { - useIsFetching( + useIsFetching: ( filers?: ORPCQueryFilters>>, - ): number - useIsMutating(filters?: SetOptional): number + ) => number + useIsMutating: (filters?: SetOptional) => number - useMutationState< + useMutationState: < UResult = MutationState< SchemaOutput, DefaultError, @@ -38,7 +38,8 @@ export interface GeneralHooks< SchemaInput >, ) => UResult - }): UResult[] + } + ) => UResult[] } export interface CreateGeneralHooksOptions { @@ -56,7 +57,7 @@ export function createGeneralHooks< TInputSchema extends Schema = undefined, TOutputSchema extends Schema = undefined, THandlerOutput extends - SchemaOutput = SchemaOutput, + SchemaOutput = SchemaOutput, >( options: CreateGeneralHooksOptions, ): GeneralHooks { diff --git a/packages/react/src/general-utils.test-d.ts b/packages/react/src/general-utils.test-d.ts index e8a22a4a..2a10b841 100644 --- a/packages/react/src/general-utils.test-d.ts +++ b/packages/react/src/general-utils.test-d.ts @@ -2,17 +2,17 @@ import type { SchemaOutput } from '@orpc/contract' import type { Promisable } from '@orpc/shared' import type { InfiniteData } from '@tanstack/react-query' import { + queryClient, type UserCreateInputSchema, type UserFindInputSchema, type UserListInputSchema, type UserListOutputSchema, type UserSchema, - queryClient, } from '../tests/orpc' import { createGeneralUtils } from './general-utils' const user_utils = createGeneralUtils({ - queryClient: queryClient, + queryClient, path: ['user'], }) @@ -21,7 +21,7 @@ const user_find_utils = createGeneralUtils< typeof UserSchema, SchemaOutput >({ - queryClient: queryClient, + queryClient, path: ['user', 'find'], }) @@ -30,7 +30,7 @@ const user_list_utils = createGeneralUtils< typeof UserListOutputSchema, SchemaOutput >({ - queryClient: queryClient, + queryClient, path: ['user', 'list'], }) @@ -39,7 +39,7 @@ const user_create_utils = createGeneralUtils< typeof UserSchema, SchemaOutput >({ - queryClient: queryClient, + queryClient, path: ['user', 'create'], }) @@ -121,9 +121,9 @@ describe('setInfiniteQueriesData', () => { user_list_utils.setInfiniteQueriesData({}, (old) => { expectTypeOf(old).toEqualTypeOf< | InfiniteData< - SchemaOutput, + SchemaOutput, number | undefined - > + > | undefined >() diff --git a/packages/react/src/general-utils.test.tsx b/packages/react/src/general-utils.test.tsx index aa61efda..78542b0d 100644 --- a/packages/react/src/general-utils.test.tsx +++ b/packages/react/src/general-utils.test.tsx @@ -1,11 +1,4 @@ import type { SchemaOutput } from '@orpc/contract' -import { - QueryClient, - useInfiniteQuery, - useMutation, - useQuery, -} from '@tanstack/react-query' -import { renderHook } from '@testing-library/react' import type { UserCreateInputSchema, UserFindInputSchema, @@ -13,6 +6,13 @@ import type { UserListOutputSchema, UserSchema, } from '../tests/orpc' +import { + QueryClient, + useInfiniteQuery, + useMutation, + useQuery, +} from '@tanstack/react-query' +import { renderHook } from '@testing-library/react' import { createGeneralUtils } from './general-utils' let qc = new QueryClient() @@ -299,8 +299,8 @@ describe('refetch', () => { const infiniteQueryKey = [['user', 'list'], { type: 'infinite' }] const queryKey = [['user', 'list'], { type: 'query' }] - const fn1 = vi.fn(() => new Promise((resolve) => setTimeout(resolve, 100))) - const fn2 = vi.fn(() => new Promise((resolve) => setTimeout(resolve, 100))) + const fn1 = vi.fn(() => new Promise(resolve => setTimeout(resolve, 100))) + const fn2 = vi.fn(() => new Promise(resolve => setTimeout(resolve, 100))) beforeEach(() => { fn1.mockClear() @@ -321,7 +321,7 @@ describe('refetch', () => { renderHook(() => useQuery( { - queryKey: queryKey, + queryKey, queryFn: fn2, }, qc, @@ -338,7 +338,7 @@ describe('refetch', () => { expect(fn1).toHaveBeenCalledTimes(1) expect(fn2).toHaveBeenCalledTimes(1) - await new Promise((resolve) => setTimeout(resolve, 101)) + await new Promise(resolve => setTimeout(resolve, 101)) user_utils.refetch() expect(fn1).toHaveBeenCalledTimes(2) @@ -350,7 +350,7 @@ describe('refetch', () => { expect(fn1).toHaveBeenCalledTimes(1) expect(fn2).toHaveBeenCalledTimes(1) - await new Promise((resolve) => setTimeout(resolve, 101)) + await new Promise(resolve => setTimeout(resolve, 101)) user_utils.refetch({ queryType: 'query' }) expect(fn1).toHaveBeenCalledTimes(1) @@ -363,7 +363,7 @@ describe('refetch', () => { expect(fn1).toHaveBeenCalledTimes(1) expect(fn2).toHaveBeenCalledTimes(1) - await new Promise((resolve) => setTimeout(resolve, 101)) + await new Promise(resolve => setTimeout(resolve, 101)) user_list_utils.refetch() expect(fn1).toHaveBeenCalledTimes(2) @@ -376,7 +376,7 @@ describe('refetch', () => { expect(fn1).toHaveBeenCalledTimes(1) expect(fn2).toHaveBeenCalledTimes(1) - await new Promise((resolve) => setTimeout(resolve, 101)) + await new Promise(resolve => setTimeout(resolve, 101)) user_list_utils.refetch({ queryType: 'infinite' }) expect(fn1).toHaveBeenCalledTimes(2) @@ -389,7 +389,7 @@ describe('refetch', () => { expect(fn1).toHaveBeenCalledTimes(1) expect(fn2).toHaveBeenCalledTimes(1) - await new Promise((resolve) => setTimeout(resolve, 101)) + await new Promise(resolve => setTimeout(resolve, 101)) user_find_utils.refetch() expect(fn1).toHaveBeenCalledTimes(1) @@ -407,7 +407,7 @@ describe('cancel', () => { { queryKey: infiniteQueryKey, queryFn: async () => - await new Promise((resolve) => setTimeout(resolve, 100)), + await new Promise(resolve => setTimeout(resolve, 100)), getNextPageParam: () => 2, initialPageParam: 1, }, @@ -418,9 +418,9 @@ describe('cancel', () => { renderHook(() => useQuery( { - queryKey: queryKey, + queryKey, queryFn: async () => - await new Promise((resolve) => setTimeout(resolve, 100)), + await new Promise(resolve => setTimeout(resolve, 100)), }, qc, ), @@ -516,8 +516,8 @@ describe('reset', () => { const infiniteQueryKey = [['user', 'list'], { type: 'infinite' }] const queryKey = [['user', 'list'], { type: 'query' }] - const fn1 = vi.fn(() => new Promise((resolve) => setTimeout(resolve, 100))) - const fn2 = vi.fn(() => new Promise((resolve) => setTimeout(resolve, 100))) + const fn1 = vi.fn(() => new Promise(resolve => setTimeout(resolve, 100))) + const fn2 = vi.fn(() => new Promise(resolve => setTimeout(resolve, 100))) beforeEach(() => { fn1.mockClear() @@ -538,7 +538,7 @@ describe('reset', () => { renderHook(() => useQuery( { - queryKey: queryKey, + queryKey, queryFn: fn2, }, qc, @@ -594,7 +594,7 @@ it('isFetching', () => { { queryKey: infiniteQueryKey, queryFn: async () => - await new Promise((resolve) => setTimeout(resolve, 100)), + await new Promise(resolve => setTimeout(resolve, 100)), getNextPageParam: () => 2, initialPageParam: 1, }, @@ -605,9 +605,9 @@ it('isFetching', () => { renderHook(() => useQuery( { - queryKey: queryKey, + queryKey, queryFn: async () => - await new Promise((resolve) => setTimeout(resolve, 100)), + await new Promise(resolve => setTimeout(resolve, 100)), }, qc, ), @@ -630,7 +630,7 @@ it('isMutating', async () => { useMutation( { mutationKey: [['user', 'create']], - mutationFn: () => new Promise((resolve) => setTimeout(resolve, 100)), + mutationFn: () => new Promise(resolve => setTimeout(resolve, 100)), }, qc, ), diff --git a/packages/react/src/general-utils.ts b/packages/react/src/general-utils.ts index 26f9baca..4caed91f 100644 --- a/packages/react/src/general-utils.ts +++ b/packages/react/src/general-utils.ts @@ -16,41 +16,39 @@ import type { SetDataOptions, Updater, } from '@tanstack/react-query' -import { getMutationKeyFromPath, getQueryKeyFromPath } from './tanstack-key' import type { ORPCInvalidateQueryFilters, ORPCQueryFilters, } from './tanstack-query' import type { SchemaInputForInfiniteQuery } from './types' +import { getMutationKeyFromPath, getQueryKeyFromPath } from './tanstack-key' export interface GeneralUtils< TInputSchema extends Schema, TOutputSchema extends Schema, THandlerOutput extends SchemaOutput, > { - getQueriesData( + getQueriesData: ( filters?: OmitKeyof< ORPCQueryFilters>>, 'queryType' >, - ): [QueryKey, SchemaOutput | undefined][] - getInfiniteQueriesData( + ) => [QueryKey, SchemaOutput | undefined][] + getInfiniteQueriesData: ( filters?: OmitKeyof< ORPCQueryFilters>>, 'queryType' >, - ): [ + ) => [ QueryKey, - ( - | InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > - | undefined - ), + | undefined + | InfiniteData< + SchemaOutput, + SchemaInput['cursor'] + >, ][] - setQueriesData( + setQueriesData: ( filters: OmitKeyof< ORPCQueryFilters>>, 'queryType' @@ -60,78 +58,76 @@ export interface GeneralUtils< SchemaOutput | undefined >, options?: SetDataOptions, - ): [QueryKey, SchemaOutput | undefined][] - setInfiniteQueriesData( + ) => [QueryKey, SchemaOutput | undefined][] + setInfiniteQueriesData: ( filters: OmitKeyof< ORPCQueryFilters>>, 'queryType' >, updater: Updater< | InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > + SchemaOutput, + SchemaInput['cursor'] + > | undefined, | InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > + SchemaOutput, + SchemaInput['cursor'] + > | undefined >, options?: SetDataOptions, - ): [ + ) => [ QueryKey, - ( - | InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > - | undefined - ), + | undefined + | InfiniteData< + SchemaOutput, + SchemaInput['cursor'] + >, ][] - invalidate( + invalidate: ( filters?: ORPCInvalidateQueryFilters< PartialDeep> >, options?: InvalidateOptions, - ): Promise - refetch( + ) => Promise + refetch: ( filters?: ORPCQueryFilters>>, options?: RefetchOptions, - ): Promise - cancel( + ) => Promise + cancel: ( filters?: ORPCQueryFilters>>, options?: CancelOptions, - ): Promise - remove( + ) => Promise + remove: ( filters?: ORPCQueryFilters>>, - ): void - reset( + ) => void + reset: ( filters?: ORPCQueryFilters>>, options?: ResetOptions, - ): Promise + ) => Promise - isFetching( + isFetching: ( filters?: ORPCQueryFilters>>, - ): number - isMutating(filters?: SetOptional): number + ) => number + isMutating: (filters?: SetOptional) => number - getQueryDefaults( + getQueryDefaults: ( filters?: Pick< ORPCQueryFilters>>, 'input' | 'queryKey' >, - ): OmitKeyof< + ) => OmitKeyof< QueryObserverOptions>, 'queryKey' > - getInfiniteQueryDefaults( + getInfiniteQueryDefaults: ( filters?: Pick< ORPCQueryFilters>>, 'input' | 'queryKey' >, - ): OmitKeyof< + ) => OmitKeyof< QueryObserverOptions< SchemaOutput, DefaultError, @@ -143,7 +139,7 @@ export interface GeneralUtils< 'queryKey' > - setQueryDefaults( + setQueryDefaults: ( options: Partial< OmitKeyof< QueryObserverOptions>, @@ -154,8 +150,8 @@ export interface GeneralUtils< ORPCQueryFilters>>, 'input' | 'queryKey' >, - ): void - setInfiniteQueryDefaults( + ) => void + setInfiniteQueryDefaults: ( options: Partial< OmitKeyof< QueryObserverOptions< @@ -173,7 +169,7 @@ export interface GeneralUtils< ORPCQueryFilters>>, 'input' | 'queryKey' >, - ): void + ) => void getMutationDefaults: ( filters?: Pick, @@ -210,7 +206,7 @@ export function createGeneralUtils< TInputSchema extends Schema = undefined, TOutputSchema extends Schema = undefined, THandlerOutput extends - SchemaOutput = SchemaOutput, + SchemaOutput = SchemaOutput, >( options: CreateGeneralUtilsOptions, ): GeneralUtils { @@ -344,40 +340,40 @@ export function createGeneralUtils< getQueryDefaults(filters) { return options.queryClient.getQueryDefaults( - filters?.queryKey ?? - getQueryKeyFromPath(options.path, { - input: filters?.input, - type: 'query', - }), + filters?.queryKey + ?? getQueryKeyFromPath(options.path, { + input: filters?.input, + type: 'query', + }), ) }, getInfiniteQueryDefaults(filters) { return options.queryClient.getQueryDefaults( - filters?.queryKey ?? - getQueryKeyFromPath(options.path, { - input: filters?.input, - type: 'infinite', - }), + filters?.queryKey + ?? getQueryKeyFromPath(options.path, { + input: filters?.input, + type: 'infinite', + }), ) as any }, setQueryDefaults(options_, filters) { return options.queryClient.setQueryDefaults( - filters?.queryKey ?? - getQueryKeyFromPath(options.path, { - input: filters?.input, - type: 'query', - }), + filters?.queryKey + ?? getQueryKeyFromPath(options.path, { + input: filters?.input, + type: 'query', + }), options_ as any, ) }, setInfiniteQueryDefaults(options_, filters) { return options.queryClient.setQueryDefaults( - filters?.queryKey ?? - getQueryKeyFromPath(options.path, { - input: filters?.input, - type: 'infinite', - }), + filters?.queryKey + ?? getQueryKeyFromPath(options.path, { + input: filters?.input, + type: 'infinite', + }), options_ as any, ) }, diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 1c9f6a16..dfa5d58e 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -2,7 +2,7 @@ export * from './general-hooks' export * from './general-utils' export * from './procedure-hooks' export * from './procedure-utils' +export * from './react' export * from './react-context' export * from './react-hooks' export * from './react-utils' -export * from './react' diff --git a/packages/react/src/orpc-path.ts b/packages/react/src/orpc-path.ts index b7b69101..2f554d18 100644 --- a/packages/react/src/orpc-path.ts +++ b/packages/react/src/orpc-path.ts @@ -15,7 +15,7 @@ export function getORPCPath( const val = Reflect.get(orpc, orpcPathSymbol) if (!Array.isArray(val)) { - throw new Error( + throw new TypeError( 'orpcPathSymbol is not implemented, please use getORPCPath with correct instance.', ) } diff --git a/packages/react/src/procedure-hooks.test-d.ts b/packages/react/src/procedure-hooks.test-d.ts index c346e8fb..1f86c47f 100644 --- a/packages/react/src/procedure-hooks.test-d.ts +++ b/packages/react/src/procedure-hooks.test-d.ts @@ -81,9 +81,9 @@ describe('useInfiniteQuery', () => { expectTypeOf(query.data).toEqualTypeOf< | undefined | InfiniteData< - SchemaOutput, + SchemaOutput, number | undefined - > + > >() }) @@ -111,11 +111,11 @@ describe('useInfiniteQuery', () => { expectTypeOf(query.data).toEqualTypeOf< | undefined | { - select: InfiniteData< - SchemaOutput, + select: InfiniteData< + SchemaOutput, number | undefined - > - } + > + } >() }) }) diff --git a/packages/react/src/procedure-hooks.test.tsx b/packages/react/src/procedure-hooks.test.tsx index cbadcc29..f3a8b346 100644 --- a/packages/react/src/procedure-hooks.test.tsx +++ b/packages/react/src/procedure-hooks.test.tsx @@ -2,12 +2,12 @@ import type { SchemaOutput } from '@orpc/contract' import { renderHook, screen, waitFor } from '@testing-library/react' import { ORPCContext, + queryClient, type UserCreateInputSchema, type UserFindInputSchema, type UserListInputSchema, type UserListOutputSchema, type UserSchema, - queryClient, wrapper, } from '../tests/orpc' import { createProcedureHooks } from './procedure-hooks' diff --git a/packages/react/src/procedure-hooks.ts b/packages/react/src/procedure-hooks.ts index 22cb8ea7..c16e31ab 100644 --- a/packages/react/src/procedure-hooks.ts +++ b/packages/react/src/procedure-hooks.ts @@ -1,8 +1,9 @@ import type { Schema, SchemaInput, SchemaOutput } from '@orpc/contract' +import type { SchemaInputForInfiniteQuery } from './types' import { + get, type PartialOnUndefinedDeep, type SetOptional, - get, } from '@orpc/shared' import { type DefaultError, @@ -10,35 +11,34 @@ import { type FetchQueryOptions, type InfiniteData, type QueryKey, + useInfiniteQuery, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, + useMutation, type UseMutationOptions, type UseMutationResult, + usePrefetchInfiniteQuery, + usePrefetchQuery, + useQuery, type UseQueryOptions, type UseQueryResult, + useSuspenseInfiniteQuery, type UseSuspenseInfiniteQueryOptions, type UseSuspenseInfiniteQueryResult, + useSuspenseQuery, type UseSuspenseQueryOptions, type UseSuspenseQueryResult, - useInfiniteQuery, - useMutation, - usePrefetchInfiniteQuery, - usePrefetchQuery, - useQuery, - useSuspenseInfiniteQuery, - useSuspenseQuery, } from '@tanstack/react-query' import { orpcPathSymbol } from './orpc-path' import { type ORPCContext, useORPCContext } from './react-context' import { getMutationKeyFromPath, getQueryKeyFromPath } from './tanstack-key' -import type { SchemaInputForInfiniteQuery } from './types' export interface ProcedureHooks< TInputSchema extends Schema, TOutputSchema extends Schema, THandlerOutput extends SchemaOutput, > { - useQuery>( + useQuery: >( input: SchemaInput, options?: SetOptional< UseQueryOptions< @@ -48,8 +48,8 @@ export interface ProcedureHooks< >, 'queryFn' | 'queryKey' >, - ): UseQueryResult - useInfiniteQuery< + ) => UseQueryResult + useInfiniteQuery: < USelectData = InfiniteData< SchemaOutput, SchemaInput['cursor'] @@ -70,9 +70,9 @@ export interface ProcedureHooks< input: SchemaInputForInfiniteQuery } >, - ): UseInfiniteQueryResult + ) => UseInfiniteQueryResult - useSuspenseQuery>( + useSuspenseQuery: >( input: SchemaInput, options?: SetOptional< UseSuspenseQueryOptions< @@ -82,8 +82,8 @@ export interface ProcedureHooks< >, 'queryFn' | 'queryKey' >, - ): UseSuspenseQueryResult - useSuspenseInfiniteQuery< + ) => UseSuspenseQueryResult + useSuspenseInfiniteQuery: < USelectData = InfiniteData< SchemaOutput, SchemaInput['cursor'] @@ -104,13 +104,13 @@ export interface ProcedureHooks< input: SchemaInputForInfiniteQuery } >, - ): UseSuspenseInfiniteQueryResult + ) => UseSuspenseInfiniteQueryResult - usePrefetchQuery( + usePrefetchQuery: ( input: SchemaInput, options?: FetchQueryOptions>, - ): void - usePrefetchInfiniteQuery( + ) => void + usePrefetchInfiniteQuery: ( options: PartialOnUndefinedDeep< SetOptional< FetchInfiniteQueryOptions< @@ -125,9 +125,9 @@ export interface ProcedureHooks< input: SchemaInputForInfiniteQuery } >, - ): void + ) => void - useMutation( + useMutation: ( options?: SetOptional< UseMutationOptions< SchemaOutput, @@ -136,7 +136,7 @@ export interface ProcedureHooks< >, 'mutationFn' | 'mutationKey' >, - ): UseMutationResult< + ) => UseMutationResult< SchemaOutput, DefaultError, SchemaInput @@ -158,7 +158,7 @@ export function createProcedureHooks< TInputSchema extends Schema = undefined, TOutputSchema extends Schema = undefined, THandlerOutput extends - SchemaOutput = SchemaOutput, + SchemaOutput = SchemaOutput, >( options: CreateProcedureHooksOptions, ): ProcedureHooks { @@ -261,7 +261,7 @@ export function createProcedureHooks< return useMutation( { mutationKey: getMutationKeyFromPath(options.path), - mutationFn: (input) => client(input), + mutationFn: input => client(input), ...options_, }, context.queryClient, diff --git a/packages/react/src/procedure-utils.test-d.ts b/packages/react/src/procedure-utils.test-d.ts index 2cba25c3..ce43233a 100644 --- a/packages/react/src/procedure-utils.test-d.ts +++ b/packages/react/src/procedure-utils.test-d.ts @@ -233,9 +233,9 @@ describe('getInfiniteQueryData', () => { expectTypeOf(data).toEqualTypeOf< | undefined | InfiniteData< - SchemaOutput, + SchemaOutput, number | undefined - > + > >() }) }) @@ -377,11 +377,11 @@ describe('getInfiniteQueryState', () => { expectTypeOf(data).toEqualTypeOf< | undefined | QueryState< - InfiniteData< - SchemaOutput, + InfiniteData< + SchemaOutput, number | undefined - > > + > >() }) }) @@ -444,9 +444,9 @@ describe('setInfiniteQueryData', () => { expectTypeOf(data).toEqualTypeOf< | undefined | InfiniteData< - SchemaOutput, + SchemaOutput, number | undefined - > + > >() return { @@ -458,9 +458,9 @@ describe('setInfiniteQueryData', () => { expectTypeOf(data).toEqualTypeOf< | undefined | InfiniteData< - SchemaOutput, + SchemaOutput, number | undefined - > + > >() }) diff --git a/packages/react/src/procedure-utils.test.tsx b/packages/react/src/procedure-utils.test.tsx index adff071d..7ce06a4f 100644 --- a/packages/react/src/procedure-utils.test.tsx +++ b/packages/react/src/procedure-utils.test.tsx @@ -9,7 +9,7 @@ describe('fetchQuery', () => { const utils = createProcedureUtils({ client: orpcClient.user.find, path: ['user', 'find'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -40,7 +40,7 @@ describe('fetchInfiniteQuery', () => { const utils = createProcedureUtils({ client: orpcClient.user.list, path: ['user', 'list'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -76,7 +76,7 @@ describe('prefetchQuery', () => { const utils = createProcedureUtils({ client: orpcClient.user.find, path: ['user', 'find'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -109,7 +109,7 @@ describe('prefetchInfiniteQuery', () => { const utils = createProcedureUtils({ client: orpcClient.user.list, path: ['user', 'list'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -144,7 +144,7 @@ describe('ensureQueryData', () => { const utils = createProcedureUtils({ client: orpcClient.user.find, path: ['user', 'find'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -175,7 +175,7 @@ describe('ensureInfiniteQuery', () => { const utils = createProcedureUtils({ client: orpcClient.user.list, path: ['user', 'list'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -211,7 +211,7 @@ describe('getQueryData', () => { const utils = createProcedureUtils({ client: orpcClient.user.find, path: ['user', 'find'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -225,7 +225,7 @@ describe('getInfiniteQueryData', () => { const utils = createProcedureUtils({ client: orpcClient.user.list, path: ['user', 'list'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -239,7 +239,7 @@ describe('getQueryState', () => { const utils = createProcedureUtils({ client: orpcClient.user.find, path: ['user', 'find'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -256,7 +256,7 @@ describe('getInfiniteQueryState', () => { const utils = createProcedureUtils({ client: orpcClient.user.list, path: ['user', 'list'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -273,7 +273,7 @@ describe('setQueryData', () => { const utils = createProcedureUtils({ client: orpcClient.user.find, path: ['user', 'find'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { @@ -299,11 +299,11 @@ describe('setQueryData', () => { }) }) -describe('getInfiniteQueryData', () => { +describe('getInfiniteQueryData 2', () => { const utils = createProcedureUtils({ client: orpcClient.user.list, path: ['user', 'list'], - queryClient: queryClient, + queryClient, }) it('on success', async () => { diff --git a/packages/react/src/procedure-utils.ts b/packages/react/src/procedure-utils.ts index da7493c3..25ca704d 100644 --- a/packages/react/src/procedure-utils.ts +++ b/packages/react/src/procedure-utils.ts @@ -14,22 +14,22 @@ import type { SetDataOptions, Updater, } from '@tanstack/react-query' -import { getQueryKeyFromPath } from './tanstack-key' import type { SchemaInputForInfiniteQuery } from './types' +import { getQueryKeyFromPath } from './tanstack-key' export interface ProcedureUtils< TInputSchema extends Schema, TOutputSchema extends Schema, THandlerOutput extends SchemaOutput, > { - fetchQuery( + fetchQuery: ( input: SchemaInput, options?: SetOptional< FetchQueryOptions>, 'queryKey' | 'queryFn' >, - ): Promise> - fetchInfiniteQuery( + ) => Promise> + fetchInfiniteQuery: ( options: PartialOnUndefinedDeep< SetOptional< FetchInfiniteQueryOptions< @@ -44,21 +44,21 @@ export interface ProcedureUtils< input: SchemaInputForInfiniteQuery } >, - ): Promise< + ) => Promise< InfiniteData< SchemaOutput, SchemaInput['cursor'] > > - prefetchQuery( + prefetchQuery: ( input: SchemaInput, options?: SetOptional< FetchQueryOptions>, 'queryKey' | 'queryFn' >, - ): Promise - prefetchInfiniteQuery( + ) => Promise + prefetchInfiniteQuery: ( options: PartialOnUndefinedDeep< SetOptional< FetchInfiniteQueryOptions< @@ -73,28 +73,27 @@ export interface ProcedureUtils< input: SchemaInputForInfiniteQuery } >, - ): Promise + ) => Promise - getQueryData( + getQueryData: ( input: SchemaInput, - ): SchemaOutput | undefined - getInfiniteQueryData( + ) => SchemaOutput | undefined + getInfiniteQueryData: ( input: SchemaInputForInfiniteQuery, - ): - | InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > - | undefined + ) => | InfiniteData< + SchemaOutput, + SchemaInput['cursor'] + > + | undefined - ensureQueryData( + ensureQueryData: ( input: SchemaInput, options?: SetOptional< EnsureQueryDataOptions>, 'queryFn' | 'queryKey' >, - ): Promise> - ensureInfiniteQueryData( + ) => Promise> + ensureInfiniteQueryData: ( options: PartialOnUndefinedDeep< SetOptional< EnsureInfiniteQueryDataOptions< @@ -109,63 +108,61 @@ export interface ProcedureUtils< input: SchemaInputForInfiniteQuery } >, - ): Promise< + ) => Promise< InfiniteData< SchemaOutput, SchemaInput['cursor'] > > - getQueryState( + getQueryState: ( input: SchemaInput, - ): QueryState> | undefined - getInfiniteQueryState( + ) => QueryState> | undefined + getInfiniteQueryState: ( input: SchemaInputForInfiniteQuery, - ): - | QueryState< - InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > - > - | undefined + ) => | QueryState< + InfiniteData< + SchemaOutput, + SchemaInput['cursor'] + > + > + | undefined - setQueryData( + setQueryData: ( input: SchemaInput, updater: Updater< SchemaOutput | undefined, SchemaOutput | undefined >, options?: SetDataOptions, - ): SchemaOutput | undefined - setInfiniteQueryData( + ) => SchemaOutput | undefined + setInfiniteQueryData: ( input: SchemaInputForInfiniteQuery, updater: Updater< | InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > + SchemaOutput, + SchemaInput['cursor'] + > | undefined, | InfiniteData< - SchemaOutput, - SchemaInput['cursor'] - > - | undefined - >, - options?: SetDataOptions, - ): - | InfiniteData< SchemaOutput, SchemaInput['cursor'] > - | undefined + | undefined + >, + options?: SetDataOptions, + ) => | InfiniteData< + SchemaOutput, + SchemaInput['cursor'] + > + | undefined } export interface CreateProcedureUtilsOptions< TInputSchema extends Schema = undefined, TOutputSchema extends Schema = undefined, THandlerOutput extends - SchemaOutput = SchemaOutput, + SchemaOutput = SchemaOutput, > { client: ProcedureClient queryClient: QueryClient diff --git a/packages/react/src/react-hooks.ts b/packages/react/src/react-hooks.ts index dd7d7d25..a0ec2d60 100644 --- a/packages/react/src/react-hooks.ts +++ b/packages/react/src/react-hooks.ts @@ -4,33 +4,25 @@ import type { SchemaOutput, } from '@orpc/contract' import type { Procedure, Router } from '@orpc/server' -import { type GeneralHooks, createGeneralHooks } from './general-hooks' -import { orpcPathSymbol } from './orpc-path' -import { type ProcedureHooks, createProcedureHooks } from './procedure-hooks' import type { ORPCContext } from './react-context' +import { createGeneralHooks, type GeneralHooks } from './general-hooks' +import { orpcPathSymbol } from './orpc-path' +import { createProcedureHooks, type ProcedureHooks } from './procedure-hooks' export type ORPCHooksWithContractRouter = { [K in keyof TRouter]: TRouter[K] extends ContractProcedure< infer UInputSchema, infer UOutputSchema > - ? ProcedureHooks> & - GeneralHooks> + ? ProcedureHooks> & GeneralHooks> : TRouter[K] extends ContractRouter ? ORPCHooksWithContractRouter : never } & GeneralHooks export type ORPCHooksWithRouter> = { - [K in keyof TRouter]: TRouter[K] extends Procedure< - any, - any, - infer UInputSchema, - infer UOutputSchema, - infer UHandlerOutput - > - ? ProcedureHooks & - GeneralHooks + [K in keyof TRouter]: TRouter[K] extends Procedure + ? ProcedureHooks & GeneralHooks : TRouter[K] extends Router ? ORPCHooksWithRouter : never @@ -52,19 +44,19 @@ export interface CreateORPCHooksOptions< export function createORPCHooks>( options: CreateORPCHooksOptions, ): TRouter extends Router - ? ORPCHooksWithRouter - : TRouter extends ContractRouter - ? ORPCHooksWithContractRouter - : never { + ? ORPCHooksWithRouter + : TRouter extends ContractRouter + ? ORPCHooksWithContractRouter + : never { const path = options.path ?? [] const generalHooks = createGeneralHooks({ context: options.context, path }) // for sure root is not procedure, so do not it procedure hooks on root const procedureHooks = path.length ? createProcedureHooks({ - context: options.context, - path, - }) + context: options.context, + path, + }) : {} return new Proxy( diff --git a/packages/react/src/react-utils.ts b/packages/react/src/react-utils.ts index 8c630ef9..008ccc1e 100644 --- a/packages/react/src/react-utils.ts +++ b/packages/react/src/react-utils.ts @@ -5,32 +5,21 @@ import type { SchemaOutput, } from '@orpc/contract' import type { Procedure, Router } from '@orpc/server' -import { type GeneralUtils, createGeneralUtils } from './general-utils' -import { type ProcedureUtils, createProcedureUtils } from './procedure-utils' import type { ORPCContextValue } from './react-context' +import { createGeneralUtils, type GeneralUtils } from './general-utils' +import { createProcedureUtils, type ProcedureUtils } from './procedure-utils' export type ORPCUtilsWithContractRouter = { - [K in keyof TRouter]: TRouter[K] extends ContractProcedure< - infer UInputSchema, - infer UOutputSchema - > - ? ProcedureUtils> & - GeneralUtils> + [K in keyof TRouter]: TRouter[K] extends ContractProcedure + ? ProcedureUtils> & GeneralUtils> : TRouter[K] extends ContractRouter ? ORPCUtilsWithContractRouter : never } & GeneralUtils export type ORPCUtilsWithRouter> = { - [K in keyof TRouter]: TRouter[K] extends Procedure< - any, - any, - infer UInputSchema, - infer UOutputSchema, - infer UHandlerOutput - > - ? ProcedureUtils & - GeneralUtils + [K in keyof TRouter]: TRouter[K] extends Procedure + ? ProcedureUtils & GeneralUtils : TRouter[K] extends Router ? ORPCUtilsWithRouter : never @@ -52,10 +41,10 @@ export interface CreateORPCUtilsOptions< export function createORPCUtils>( options: CreateORPCUtilsOptions, ): TRouter extends Router - ? ORPCUtilsWithRouter - : TRouter extends ContractRouter - ? ORPCUtilsWithContractRouter - : never { + ? ORPCUtilsWithRouter + : TRouter extends ContractRouter + ? ORPCUtilsWithContractRouter + : never { const path = options.path ?? [] const client = options.contextValue.client as any @@ -67,10 +56,10 @@ export function createORPCUtils>( // for sure root is not procedure, so do not it procedure utils on root const procedureUtils = path.length ? createProcedureUtils({ - client, - queryClient: options.contextValue.queryClient, - path, - }) + client, + queryClient: options.contextValue.queryClient, + path, + }) : {} return new Proxy( diff --git a/packages/react/src/react.test-d.ts b/packages/react/src/react.test-d.ts index ec78f192..efbc95d5 100644 --- a/packages/react/src/react.test-d.ts +++ b/packages/react/src/react.test-d.ts @@ -1,16 +1,16 @@ import type { SchemaOutput } from '@orpc/contract' import type { QueryClient } from '@tanstack/react-query' +import type { GeneralHooks } from './general-hooks' +import type { GeneralUtils } from './general-utils' +import type { ProcedureHooks } from './procedure-hooks' +import type { ProcedureUtils } from './procedure-utils' import { + orpc, + orpcClient, ORPCContext, type UserFindInputSchema, type UserSchema, - orpc, - orpcClient, } from '../tests/orpc' -import type { GeneralHooks } from './general-hooks' -import type { GeneralUtils } from './general-utils' -import type { ProcedureHooks } from './procedure-hooks' -import type { ProcedureUtils } from './procedure-utils' import { useQueriesFactory } from './use-queries/hook' describe('useUtils', () => { diff --git a/packages/react/src/react.test.tsx b/packages/react/src/react.test.tsx index 99999abe..7fcc04c2 100644 --- a/packages/react/src/react.test.tsx +++ b/packages/react/src/react.test.tsx @@ -6,7 +6,7 @@ beforeEach(() => { }) it('useUtils', async () => { - const { result } = renderHook(() => orpc.useUtils(), { wrapper: wrapper }) + const { result } = renderHook(() => orpc.useUtils(), { wrapper }) const promise = result.current.user.find.ensureQueryData({ id: '1' }) expect(result.current.user.isFetching()).toBe(1) @@ -26,7 +26,7 @@ it('useUtils', async () => { }) it('useContext', async () => { - const { result } = renderHook(() => orpc.useContext(), { wrapper: wrapper }) + const { result } = renderHook(() => orpc.useContext(), { wrapper }) expect(result.current.client).toBe(orpcClient) expect(result.current.queryClient).toBe(queryClient) @@ -38,8 +38,8 @@ it('useContext', async () => { it('useQueries', async () => { const queries = renderHook( - () => orpc.useQueries((o) => [o.user.find({ id: '123' })]), - { wrapper: wrapper }, + () => orpc.useQueries(o => [o.user.find({ id: '123' })]), + { wrapper }, ) await waitFor(() => @@ -52,20 +52,20 @@ it('useQueries', async () => { it('hooks', async () => { const isFetching = renderHook(() => orpc.user.useIsFetching(), { - wrapper: wrapper, + wrapper, }) const isMutating = renderHook(() => orpc.user.useIsMutating(), { - wrapper: wrapper, + wrapper, }) expect(isFetching.result.current).toBe(0) expect(isMutating.result.current).toBe(0) const query = renderHook(() => orpc.user.find.useQuery({ id: '1' }), { - wrapper: wrapper, + wrapper, }) const mutation = renderHook(() => orpc.user.create.useMutation(), { - wrapper: wrapper, + wrapper, }) await waitFor(() => expect(query.result.current.status).toEqual('pending')) diff --git a/packages/react/src/react.tsx b/packages/react/src/react.tsx index ae4df093..b865de4f 100644 --- a/packages/react/src/react.tsx +++ b/packages/react/src/react.tsx @@ -1,25 +1,25 @@ import type { ContractRouter } from '@orpc/contract' import type { Router } from '@orpc/server' import { + createORPCContext, type ORPCContext, type ORPCContextValue, - createORPCContext, useORPCContext, } from './react-context' import { + createORPCHooks, type ORPCHooksWithContractRouter, type ORPCHooksWithRouter, - createORPCHooks, } from './react-hooks' import { + createORPCUtils, type ORPCUtilsWithContractRouter, type ORPCUtilsWithRouter, - createORPCUtils, } from './react-utils' import { + useQueriesFactory, type UseQueriesWithContractRouter, type UseQueriesWithRouter, - useQueriesFactory, } from './use-queries/hook' export type ORPCReactWithContractRouter = @@ -49,6 +49,7 @@ export function createORPCReact< const Context = createORPCContext() const useContext = () => useORPCContext(Context) const useUtils = () => createORPCUtils({ contextValue: useContext() }) + // eslint-disable-next-line react-hooks/rules-of-hooks const useQueries = useQueriesFactory({ context: Context }) const hooks = createORPCHooks({ context: Context }) diff --git a/packages/react/src/tanstack-key.ts b/packages/react/src/tanstack-key.ts index bf42a2c0..723b49d8 100644 --- a/packages/react/src/tanstack-key.ts +++ b/packages/react/src/tanstack-key.ts @@ -1,12 +1,12 @@ import type { SchemaInput } from '@orpc/contract' import type { PartialDeep } from '@orpc/shared' import type { MutationKey, QueryKey } from '@tanstack/react-query' -import { getORPCPath } from './orpc-path' import type { ProcedureHooks } from './procedure-hooks' import type { ORPCHooksWithContractRouter, ORPCHooksWithRouter, } from './react-hooks' +import { getORPCPath } from './orpc-path' export type QueryType = 'query' | 'infinite' | undefined @@ -17,9 +17,9 @@ export interface GetQueryKeyOptions { export function getQueryKey< T extends - | ORPCHooksWithContractRouter - | ORPCHooksWithRouter - | ProcedureHooks, + | ORPCHooksWithContractRouter + | ORPCHooksWithRouter + | ProcedureHooks, >( orpc: T, options?: GetQueryKeyOptions< @@ -36,8 +36,8 @@ export function getQueryKeyFromPath( path: string[], options?: GetQueryKeyOptions, ): QueryKey { - const withInput = - options?.input !== undefined ? { input: options?.input } : {} + const withInput + = options?.input !== undefined ? { input: options?.input } : {} const withType = options?.type !== undefined ? { type: options?.type } : {} return [ @@ -51,9 +51,9 @@ export function getQueryKeyFromPath( export function getMutationKey< T extends - | ORPCHooksWithContractRouter - | ORPCHooksWithRouter - | ProcedureHooks, + | ORPCHooksWithContractRouter + | ORPCHooksWithRouter + | ProcedureHooks, >(orpc: T): MutationKey { const path = getORPCPath(orpc) return getMutationKeyFromPath(path) diff --git a/packages/react/src/tanstack-query.ts b/packages/react/src/tanstack-query.ts index 47614583..4fb3da05 100644 --- a/packages/react/src/tanstack-query.ts +++ b/packages/react/src/tanstack-query.ts @@ -20,8 +20,8 @@ export interface ORPCAdditionalQueryFilters { export interface ORPCQueryFilters extends SetOptional, - ORPCAdditionalQueryFilters {} + ORPCAdditionalQueryFilters {} export interface ORPCInvalidateQueryFilters extends SetOptional, - ORPCAdditionalQueryFilters {} + ORPCAdditionalQueryFilters {} diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index bb4cfc4c..e1972266 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -4,4 +4,4 @@ export type SchemaInputForInfiniteQuery = Omit< SchemaInput, 'cursor' > & - (Record & { cursor?: never }) +(Record & { cursor?: never }) diff --git a/packages/react/src/use-queries/builder.ts b/packages/react/src/use-queries/builder.ts index a9c3b847..85221241 100644 --- a/packages/react/src/use-queries/builder.ts +++ b/packages/react/src/use-queries/builder.ts @@ -54,7 +54,7 @@ export function createUseQueriesBuilder< TInputSchema extends Schema = undefined, TOutputSchema extends Schema = undefined, THandlerOutput extends - SchemaOutput = SchemaOutput, + SchemaOutput = SchemaOutput, >( options: CreateUseQueriesBuilderOptions< TInputSchema, diff --git a/packages/react/src/use-queries/builders.ts b/packages/react/src/use-queries/builders.ts index bb9a7306..2d736d39 100644 --- a/packages/react/src/use-queries/builders.ts +++ b/packages/react/src/use-queries/builders.ts @@ -9,7 +9,7 @@ import type { } from '@orpc/contract' import type { Procedure, Router } from '@orpc/server' import type {} from '@tanstack/react-query' -import { type UseQueriesBuilder, createUseQueriesBuilder } from './builder' +import { createUseQueriesBuilder, type UseQueriesBuilder } from './builder' export type UseQueriesBuildersWithContractRouter< TRouter extends ContractRouter, @@ -19,10 +19,10 @@ export type UseQueriesBuildersWithContractRouter< infer UOutputSchema > ? UseQueriesBuilder< - UInputSchema, - UOutputSchema, - SchemaOutput - > + UInputSchema, + UOutputSchema, + SchemaOutput + > : TRouter[K] extends ContractRouter ? UseQueriesBuildersWithContractRouter : never @@ -62,10 +62,10 @@ export function createUseQueriesBuilders< >( options: CreateUseQueriesBuildersOptions, ): TRouter extends Router - ? UseQueriesBuildersWithRouter - : TRouter extends ContractRouter - ? UseQueriesBuildersWithContractRouter - : never { + ? UseQueriesBuildersWithRouter + : TRouter extends ContractRouter + ? UseQueriesBuildersWithContractRouter + : never { const path = options.path ?? [] const client = options.client as any diff --git a/packages/react/src/use-queries/hook.test-d.ts b/packages/react/src/use-queries/hook.test-d.ts index 3e8856ea..75a87a83 100644 --- a/packages/react/src/use-queries/hook.test-d.ts +++ b/packages/react/src/use-queries/hook.test-d.ts @@ -1,9 +1,9 @@ import type { SchemaOutput } from '@orpc/contract' import { + orpcClient, ORPCContext, type UserListOutputSchema, type UserSchema, - orpcClient, } from '../../tests/orpc' import { createUseQueriesBuilders } from './builders' import { useQueriesFactory } from './hook' diff --git a/packages/react/src/use-queries/hook.test.tsx b/packages/react/src/use-queries/hook.test.tsx index c711b7d2..703a5910 100644 --- a/packages/react/src/use-queries/hook.test.tsx +++ b/packages/react/src/use-queries/hook.test.tsx @@ -1,5 +1,5 @@ import { renderHook, waitFor } from '@testing-library/react' -import { ORPCContext, orpcClient, wrapper } from '../../tests/orpc' +import { orpcClient, ORPCContext, wrapper } from '../../tests/orpc' import { createUseQueriesBuilders } from './builders' import { useQueriesFactory } from './hook' @@ -18,7 +18,7 @@ describe('useQueriesFactory', () => { return [o.user.find({ id: '123' }), o.user.list({})] }), - { wrapper: wrapper }, + { wrapper }, ) await waitFor(() => diff --git a/packages/react/src/use-queries/hook.ts b/packages/react/src/use-queries/hook.ts index 6c4e8f91..3ebb1b50 100644 --- a/packages/react/src/use-queries/hook.ts +++ b/packages/react/src/use-queries/hook.ts @@ -7,9 +7,9 @@ import { } from '@tanstack/react-query' import { type ORPCContext, useORPCContext } from '../react-context' import { + createUseQueriesBuilders, type UseQueriesBuildersWithContractRouter, type UseQueriesBuildersWithRouter, - createUseQueriesBuilders, } from './builders' export interface UseQueriesWithContractRouter { @@ -39,19 +39,19 @@ export interface UseQueriesFactoryOptions< export function useQueriesFactory | ContractRouter>( options: UseQueriesFactoryOptions, ): TRouter extends Router - ? UseQueriesWithRouter - : TRouter extends ContractRouter - ? UseQueriesWithContractRouter - : never { - const hook = (build: any, combine?: any) => { + ? UseQueriesWithRouter + : TRouter extends ContractRouter + ? UseQueriesWithContractRouter + : never { + const Hook = (build: any, combine?: any): any => { const orpc = useORPCContext(options.context) const builders = createUseQueriesBuilders({ client: orpc.client as any }) return useQueries({ queries: build(builders), - combine: combine, + combine, }) } - return hook as any + return Hook as any } diff --git a/packages/react/tests/orpc.tsx b/packages/react/tests/orpc.tsx index 1361c933..ea850b70 100644 --- a/packages/react/tests/orpc.tsx +++ b/packages/react/tests/orpc.tsx @@ -12,10 +12,10 @@ export const ping = orpcServer.handler(() => 'pong') export const UserSchema = z .object({ data: z.object({ id: z.string(), name: z.string() }) }) - .transform((data) => data.data) + .transform(data => data.data) export const UserFindInputSchema = z .object({ id: z.string() }) - .transform((data) => ({ data })) + .transform(data => ({ data })) export const userFind = orpcServer .input(UserFindInputSchema) @@ -34,7 +34,7 @@ export const UserListInputSchema = z keyword: z.string().optional(), cursor: z.number().default(0), }) - .transform((data) => ({ data })) + .transform(data => ({ data })) export const UserListOutputSchema = z .object({ data: z.object({ @@ -42,7 +42,7 @@ export const UserListOutputSchema = z users: z.array(UserSchema), }), }) - .transform((data) => data.data) + .transform(data => data.data) export const userList = orpcServer .input(UserListInputSchema) .output(UserListOutputSchema) @@ -70,7 +70,7 @@ export const userList = orpcServer export const UserCreateInputSchema = z .object({ name: z.string() }) - .transform((data) => ({ data })) + .transform(data => ({ data })) export const userCreate = orpcServer .input(UserCreateInputSchema) .output(UserSchema) @@ -98,7 +98,7 @@ export const orpcClient = createORPCClient({ baseURL: 'http://localhost:3000', async fetch(...args) { - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise(resolve => setTimeout(resolve, 100)) const request = new Request(...args) return appHandler({ @@ -120,17 +120,12 @@ export const queryClient = new QueryClient({ export class ErrorBoundary extends React.Component< { children: React.ReactNode }, - { hasError?: boolean; error: unknown } + { hasError?: boolean, error: unknown } > { static getDerivedStateFromError(error: any) { return { hasError: true, error } } - override componentDidCatch(error: unknown, errorInfo: unknown) { - // You can use your own error logging service here - // console.log({ error, errorInfo }) - } - override render() { if (this.state?.hasError) { return ( @@ -145,7 +140,7 @@ export class ErrorBoundary extends React.Component< } } -export const wrapper = (props: { children: React.ReactNode }) => { +export function wrapper(props: { children: React.ReactNode }) { return ( diff --git a/packages/server/package.json b/packages/server/package.json index 7be8679d..a346d52e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -46,15 +46,15 @@ "build:watch": "pnpm run build --watch", "type:check": "tsc -b" }, + "peerDependencies": { + "@orpc/zod": "workspace:*", + "zod": "^3.23.8" + }, "dependencies": { "@orpc/contract": "workspace:*", "@orpc/shared": "workspace:*", "@orpc/transformer": "workspace:*" }, - "peerDependencies": { - "@orpc/zod": "workspace:*", - "zod": "^3.23.8" - }, "devDependencies": { "hono": "^4.6.3" } diff --git a/packages/server/src/adapters/fetch.test.ts b/packages/server/src/adapters/fetch.test.ts index 091e4da3..4081de0d 100644 --- a/packages/server/src/adapters/fetch.test.ts +++ b/packages/server/src/adapters/fetch.test.ts @@ -2,7 +2,7 @@ import { ORPC_HEADER, ORPC_HEADER_VALUE } from '@orpc/contract' import { oz } from '@orpc/zod' import { describe, expect, it } from 'vitest' import { z } from 'zod' -import { os, ORPCError } from '..' +import { ORPCError, os } from '..' import { createFetchHandler } from './fetch' const router = os.router({ @@ -252,7 +252,7 @@ describe('procedure throw error', () => { }) describe('hooks', () => { - test('on success', async () => { + it('on success', async () => { const onSuccess = vi.fn() const onError = vi.fn() const onFinish = vi.fn() @@ -282,7 +282,7 @@ describe('hooks', () => { expect(onFinish.mock.calls[0]?.[1]).toBe(undefined) }) - test('on failed', async () => { + it('on failed', async () => { const onSuccess = vi.fn() const onError = vi.fn() const onFinish = vi.fn() @@ -510,7 +510,7 @@ describe('dynamic params', () => { file: oz.file(), }), ) - .handler((input) => input), + .handler(input => input), find: os .route({ @@ -522,7 +522,7 @@ describe('dynamic params', () => { id: z.number(), }), ) - .handler((input) => input), + .handler(input => input), }) const handlers = [ @@ -576,7 +576,7 @@ describe('can control method on POST request', () => { file: oz.file(), }), ) - .handler((input) => input), + .handler(input => input), }) const handlers = [ diff --git a/packages/server/src/adapters/fetch.ts b/packages/server/src/adapters/fetch.ts index 6317cca9..5c2349b3 100644 --- a/packages/server/src/adapters/fetch.ts +++ b/packages/server/src/adapters/fetch.ts @@ -1,5 +1,7 @@ /// +import type { Router } from '../router' +import type { Meta, Promisable } from '../types' import { type HTTPPath, ORPC_HEADER, @@ -7,26 +9,24 @@ import { standardizeHTTPPath, } from '@orpc/contract' import { - type PartialOnUndefinedDeep, get, isPlainObject, mapValues, + type PartialOnUndefinedDeep, trim, } from '@orpc/shared' import { ORPCError } from '@orpc/shared/error' import { - ORPCDeserializer, - ORPCSerializer, OpenAPIDeserializer, OpenAPISerializer, + ORPCDeserializer, + ORPCSerializer, zodCoerce, } from '@orpc/transformer' import { LinearRouter } from 'hono/router/linear-router' import { RegExpRouter } from 'hono/router/reg-exp-router' -import { type WELL_DEFINED_PROCEDURE, isProcedure } from '../procedure' +import { isProcedure, type WELL_DEFINED_PROCEDURE } from '../procedure' import { createProcedureCaller } from '../procedure-caller' -import type { Router } from '../router' -import type { Meta, Promisable } from '../types' import { hook } from '../utils' export interface CreateFetchHandlerOptions> { @@ -64,7 +64,8 @@ export function createFetchHandler>( routing.add(method, path, [currentPath, item]) } - } else { + } + else { addRouteRecursively(item, currentPath) } } @@ -73,8 +74,8 @@ export function createFetchHandler>( addRouteRecursively(options.router, []) return async (requestOptions) => { - const isORPCTransformer = - requestOptions.request.headers.get(ORPC_HEADER) === ORPC_HEADER_VALUE + const isORPCTransformer + = requestOptions.request.headers.get(ORPC_HEADER) === ORPC_HEADER_VALUE const accept = requestOptions.request.headers.get('Accept') || undefined const serializer = isORPCTransformer @@ -97,9 +98,10 @@ export function createFetchHandler>( if (isProcedure(val)) { procedure = val } - } else { - const customMethod = - requestOptions.request.method === 'POST' + } + else { + const customMethod + = requestOptions.request.method === 'POST' ? url.searchParams.get('method')?.toUpperCase() : undefined const method = customMethod || requestOptions.request.method @@ -117,9 +119,10 @@ export function createFetchHandler>( if (params_) { params = mapValues( (match as any)[1]!, - (v) => params_[v as number]!, + v => params_[v as number]!, ) - } else { + } + else { params = match[1] as Record } } @@ -142,7 +145,7 @@ export function createFetchHandler>( const meta: Meta = { ...hooks, procedure, - path: path, + path, internal: false, } @@ -151,13 +154,14 @@ export function createFetchHandler>( const deserializer = isORPCTransformer ? new ORPCDeserializer() : new OpenAPIDeserializer({ - schema: procedure.zz$p.contract.zz$cp.InputSchema, - }) + schema: procedure.zz$p.contract.zz$cp.InputSchema, + }) const input_ = await (async () => { try { return await deserializer.deserialize(requestOptions.request) - } catch (e) { + } + catch (e) { throw new ORPCError({ code: 'BAD_REQUEST', message: @@ -209,7 +213,8 @@ export function createFetchHandler>( headers, }) }) - } catch (e) { + } + catch (e) { const error = toORPCError(e) try { @@ -217,9 +222,10 @@ export function createFetchHandler>( return new Response(body, { status: error.status, - headers: headers, + headers, }) - } catch (e) { + } + catch (e) { const error = toORPCError(e) // fallback to OpenAPI serializer (without accept) when expected serializer has failed @@ -229,7 +235,7 @@ export function createFetchHandler>( return new Response(body, { status: error.status, - headers: headers, + headers, }) } } @@ -268,8 +274,8 @@ function toORPCError(e: unknown): ORPCError { return e instanceof ORPCError ? e : new ORPCError({ - code: 'INTERNAL_SERVER_ERROR', - message: 'Internal server error', - cause: e, - }) + code: 'INTERNAL_SERVER_ERROR', + message: 'Internal server error', + cause: e, + }) } diff --git a/packages/server/src/builder.test.ts b/packages/server/src/builder.test.ts index ee9f3722..fc4e2d84 100644 --- a/packages/server/src/builder.test.ts +++ b/packages/server/src/builder.test.ts @@ -1,19 +1,19 @@ import { oc } from '@orpc/contract' import { z } from 'zod' import { - os, type Builder, type DecoratedMiddleware, type DecoratedProcedure, + isProcedure, type Meta, + os, ProcedureBuilder, ProcedureImplementer, RouterImplementer, - isProcedure, } from '.' import { RouterBuilder } from './router-builder' -test('context method', () => { +it('context method', () => { expectTypeOf< typeof os extends Builder ? TContext : never >().toEqualTypeOf>() @@ -104,7 +104,7 @@ describe('create middleware', () => { }) }) -test('router method', () => { +it('router method', () => { const pingContract = oc.input(z.string()).output(z.string()) const userFindContract = oc .input(z.object({ id: z.string() })) @@ -149,7 +149,7 @@ describe('define procedure builder', () => { const schema2 = z.object({ a: z.string() }) const example2 = { a: '' } - test('input method', () => { + it('input method', () => { const builder = osw.input(schema1, example1) expectTypeOf(builder).toEqualTypeOf< @@ -168,7 +168,7 @@ describe('define procedure builder', () => { }) }) - test('output method', () => { + it('output method', () => { const builder = osw.output(schema2, example2) expectTypeOf(builder).toEqualTypeOf< @@ -187,7 +187,7 @@ describe('define procedure builder', () => { }) }) - test('route method', () => { + it('route method', () => { const builder = osw.route({ method: 'GET', path: '/test', @@ -217,7 +217,7 @@ describe('define procedure builder', () => { }) }) - test('with middlewares', () => { + it('with middlewares', () => { const mid = os.middleware(() => { return { context: { @@ -329,7 +329,7 @@ describe('handler method', () => { }) }) -test('prefix', () => { +it('prefix', () => { const builder = os .context<{ auth: boolean }>() .use(() => { @@ -345,7 +345,7 @@ test('prefix', () => { expect(builder.zz$rb.prefix).toEqual('/api') }) -test('tags', () => { +it('tags', () => { const builder = os .context<{ auth: boolean }>() .use(() => { diff --git a/packages/server/src/builder.ts b/packages/server/src/builder.ts index 9f7e0ee4..96ee6b90 100644 --- a/packages/server/src/builder.ts +++ b/packages/server/src/builder.ts @@ -1,34 +1,34 @@ +import type { IsEqual } from '@orpc/shared' +import type { HandledRouter, Router } from './router' +import type { Context, MergeContext } from './types' import { ContractProcedure, type ContractRouter, type HTTPPath, + isContractProcedure, type RouteOptions, type Schema, type SchemaInput, type SchemaOutput, - isContractProcedure, } from '@orpc/contract' -import type { IsEqual } from '@orpc/shared' import { type DecoratedMiddleware, + decorateMiddleware, type MapInputMiddleware, type Middleware, - decorateMiddleware, } from './middleware' import { type DecoratedProcedure, - type ProcedureHandler, decorateProcedure, + type ProcedureHandler, } from './procedure' import { ProcedureBuilder } from './procedure-builder' import { ProcedureImplementer } from './procedure-implementer' -import type { HandledRouter, Router } from './router' import { RouterBuilder } from './router-builder' import { type ChainedRouterImplementer, chainRouterImplementer, } from './router-implementer' -import type { Context, MergeContext } from './types' export class Builder { constructor( @@ -49,8 +49,8 @@ export class Builder { use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, >( middleware: Middleware< MergeContext, @@ -62,8 +62,8 @@ export class Builder { use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, UMappedInput = unknown, >( middleware: Middleware< @@ -146,12 +146,12 @@ export class Builder { UHandlerOutput >, ): DecoratedProcedure< - TContext, - TExtraContext, - undefined, - undefined, - UHandlerOutput - > { + TContext, + TExtraContext, + undefined, + undefined, + UHandlerOutput + > { return decorateProcedure({ zz$p: { middlewares: this.zz$b.middlewares, @@ -171,13 +171,13 @@ export class Builder { contract | ContractRouter>( contract: UContract, ): UContract extends ContractProcedure< - infer UInputSchema, - infer UOutputSchema - > - ? ProcedureImplementer - : UContract extends ContractRouter - ? ChainedRouterImplementer - : never { + infer UInputSchema, + infer UOutputSchema + > + ? ProcedureImplementer + : UContract extends ContractRouter + ? ChainedRouterImplementer + : never { if (isContractProcedure(contract)) { return new ProcedureImplementer({ contract, @@ -203,11 +203,11 @@ export class Builder { unknown >, ): DecoratedMiddleware< - MergeContext, - UExtraContext, - TInput, - unknown - > { + MergeContext, + UExtraContext, + TInput, + unknown + > { return decorateMiddleware(middleware) } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index e9bb57fe..a3345abf 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,16 +1,16 @@ import { Builder } from './builder' export * from './builder' -export * from '@orpc/shared/error' export * from './middleware' export * from './procedure' -export * from './procedure-caller' export * from './procedure-builder' +export * from './procedure-caller' export * from './procedure-implementer' export * from './router' export * from './router-caller' export * from './router-implementer' export * from './types' export * from './utils' +export * from '@orpc/shared/error' export const os = new Builder, undefined>() diff --git a/packages/server/src/middleware.test.ts b/packages/server/src/middleware.test.ts index dd373283..b504544f 100644 --- a/packages/server/src/middleware.test.ts +++ b/packages/server/src/middleware.test.ts @@ -1,9 +1,9 @@ +import type { Meta } from './types' import { type DecoratedMiddleware, - type Middleware, decorateMiddleware, + type Middleware, } from './middleware' -import type { Meta } from './types' describe('middleware', () => { it('just a function', () => { @@ -195,7 +195,7 @@ describe('decorateMiddleware', () => { DecoratedMiddleware< { auth: boolean }, undefined, - { id: never; status: string }, + { id: never, status: string }, unknown > >() @@ -219,7 +219,7 @@ describe('decorateMiddleware', () => { (input: { postId: number }) => { return { context: { a: 'a' } } }, - (input) => ({ postId: 12455 }), + input => ({ postId: 12455 }), ) // mid2 input is unknown, because it's map input does not expect anything diff --git a/packages/server/src/middleware.ts b/packages/server/src/middleware.ts index c58bfe7d..cbaece5b 100644 --- a/packages/server/src/middleware.ts +++ b/packages/server/src/middleware.ts @@ -26,21 +26,19 @@ export interface DecoratedMiddleware< TInput, TOutput, > extends Middleware { - concat( + concat: (( middleware: Middleware< MergeContext, UExtraContext, UInput & TInput, TOutput >, - ): DecoratedMiddleware< + ) => DecoratedMiddleware< TContext, MergeContext, TInput & UInput, TOutput - > - - concat< + >) & (< UExtraContext extends Context = undefined, UInput = TInput, UMappedInput = unknown, @@ -52,16 +50,16 @@ export interface DecoratedMiddleware< TOutput >, mapInput: MapInputMiddleware, - ): DecoratedMiddleware< + ) => DecoratedMiddleware< TContext, MergeContext, TInput & UInput, TOutput - > + >) - mapInput( + mapInput: ( map: MapInputMiddleware, - ): DecoratedMiddleware + ) => DecoratedMiddleware } const decoratedMiddlewareSymbol = Symbol('🔒decoratedMiddleware') @@ -81,7 +79,7 @@ export function decorateMiddleware< const concat = ( concatMiddleware: Middleware, mapInput?: MapInputMiddleware, - ) => { + ): Middleware => { const concatMiddleware_ = mapInput ? decorateMiddleware(concatMiddleware).mapInput(mapInput) : concatMiddleware diff --git a/packages/server/src/procedure-builder.test.ts b/packages/server/src/procedure-builder.test.ts index 9ec8c519..a622bb4d 100644 --- a/packages/server/src/procedure-builder.test.ts +++ b/packages/server/src/procedure-builder.test.ts @@ -1,9 +1,9 @@ +import type { ProcedureImplementer } from './procedure-implementer' +import type { Meta } from './types' import { ContractProcedure } from '@orpc/contract' import { z } from 'zod' import { type DecoratedProcedure, isProcedure } from './procedure' import { ProcedureBuilder } from './procedure-builder' -import type { ProcedureImplementer } from './procedure-implementer' -import type { Meta } from './types' const schema1 = z.object({ id: z.string() }) const example1 = { id: '1' } @@ -127,12 +127,12 @@ describe('use middleware', () => { return { context: { a: 'a' } } }, // @ts-expect-error mismatch input - (input) => ({ postId: 12455 }), + input => ({ postId: 12455 }), ) builder.use( (input: { postId: string }) => {}, - (input) => ({ postId: '12455' }), + input => ({ postId: '12455' }), ) const implementer = builder.input(schema1).use( @@ -143,7 +143,7 @@ describe('use middleware', () => { }, } }, - (input) => ({ id: Number.parseInt(input.id) }), + input => ({ id: Number.parseInt(input.id) }), ) expectTypeOf(implementer).toEqualTypeOf< diff --git a/packages/server/src/procedure-builder.ts b/packages/server/src/procedure-builder.ts index a4a4f2da..c9612be4 100644 --- a/packages/server/src/procedure-builder.ts +++ b/packages/server/src/procedure-builder.ts @@ -1,3 +1,5 @@ +import type { MapInputMiddleware, Middleware } from './middleware' +import type { Context, MergeContext } from './types' import { type ContractProcedure, DecoratedContractProcedure, @@ -6,14 +8,12 @@ import { type SchemaInput, type SchemaOutput, } from '@orpc/contract' -import type { MapInputMiddleware, Middleware } from './middleware' import { type DecoratedProcedure, - type ProcedureHandler, decorateProcedure, + type ProcedureHandler, } from './procedure' import { ProcedureImplementer } from './procedure-implementer' -import type { Context, MergeContext } from './types' export class ProcedureBuilder< TContext extends Context, @@ -75,8 +75,8 @@ export class ProcedureBuilder< use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, >( middleware: Middleware< MergeContext, @@ -93,8 +93,8 @@ export class ProcedureBuilder< use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, UMappedInput = unknown, >( middleware: Middleware< @@ -141,12 +141,12 @@ export class ProcedureBuilder< UHandlerOutput >, ): DecoratedProcedure< - TContext, - TExtraContext, - TInputSchema, - TOutputSchema, - UHandlerOutput - > { + TContext, + TExtraContext, + TInputSchema, + TOutputSchema, + UHandlerOutput + > { return decorateProcedure({ zz$p: { middlewares: this.zz$pb.middlewares, diff --git a/packages/server/src/procedure-caller.test.ts b/packages/server/src/procedure-caller.test.ts index 4d96f044..a10e4557 100644 --- a/packages/server/src/procedure-caller.test.ts +++ b/packages/server/src/procedure-caller.test.ts @@ -1,5 +1,5 @@ import { z } from 'zod' -import { os, createProcedureCaller } from '.' +import { createProcedureCaller, os } from '.' describe('createProcedureCaller', () => { let internal = false @@ -8,8 +8,8 @@ describe('createProcedureCaller', () => { const osw = os.context<{ auth?: boolean }>() const procedure = osw - .input(z.object({ value: z.string().transform((v) => Number(v)) })) - .output(z.object({ value: z.number().transform((v) => v.toString()) })) + .input(z.object({ value: z.string().transform(v => Number(v)) })) + .output(z.object({ value: z.number().transform(v => v.toString()) })) .handler((input, context, meta) => { expect(context).toEqual(context) expect(meta.internal).toBe(internal) @@ -47,7 +47,7 @@ describe('createProcedureCaller', () => { expect(await caller({ value: '123' })).toEqual({ value: '123' }) - // @ts-expect-error + // @ts-expect-error - invalid input expect(caller({ value: {} })).rejects.toThrowError( 'Validation input failed', ) @@ -177,10 +177,10 @@ describe('createProcedureCaller', () => { const ref = { value: 0 } const caller = createProcedureCaller({ - procedure: procedure, - context: context, - internal: internal, - path: path, + procedure, + context, + internal, + path, hooks: (context, meta) => { expect(context).toEqual(context) expect(meta.internal).toBe(internal) diff --git a/packages/server/src/procedure-caller.ts b/packages/server/src/procedure-caller.ts index 72c3d235..7e26ebf9 100644 --- a/packages/server/src/procedure-caller.ts +++ b/packages/server/src/procedure-caller.ts @@ -1,8 +1,8 @@ import type { SchemaInput, SchemaOutput } from '@orpc/contract' -import { ORPCError } from '@orpc/shared/error' import type { Middleware } from './middleware' import type { Procedure } from './procedure' import type { Context, Hooks, Meta, Promisable } from './types' +import { ORPCError } from '@orpc/shared/error' import { hook, mergeContext } from './utils' export interface CreateProcedureCallerOptions< @@ -80,7 +80,7 @@ export function createProcedureCaller< const procedure = options.procedure const validate = options.validate ?? true - const caller = async (input: unknown) => { + const caller = async (input: unknown): Promise => { const handler = async ( input: unknown, context: Context, @@ -111,16 +111,19 @@ export function createProcedureCaller< }) const validOutput = await (async () => { - if (!validate) return output + if (!validate) + return output const schema = procedure.zz$p.contract.zz$cp.OutputSchema - if (!schema) return output + if (!schema) + return output const result = await schema.safeParseAsync(output) - if (result.error) + if (result.error) { throw new ORPCError({ message: 'Validation output failed', code: 'INTERNAL_SERVER_ERROR', cause: result.error, }) + } return result.data })() @@ -137,13 +140,16 @@ export function createProcedureCaller< }) const validInput = (() => { - if (!validate) return input + if (!validate) + return input const schema = procedure.zz$p.contract.zz$cp.InputSchema - if (!schema) return input + if (!schema) + return input try { return schema.parse(input) - } catch (e) { + } + catch (e) { throw new ORPCError({ message: 'Validation input failed', code: 'BAD_REQUEST', diff --git a/packages/server/src/procedure-implementer.test.ts b/packages/server/src/procedure-implementer.test.ts index 571f5a0d..06abfb59 100644 --- a/packages/server/src/procedure-implementer.test.ts +++ b/packages/server/src/procedure-implementer.test.ts @@ -1,6 +1,6 @@ import { DecoratedContractProcedure } from '@orpc/contract' import { z } from 'zod' -import { os, type DecoratedProcedure, type Meta, isProcedure } from '.' +import { type DecoratedProcedure, isProcedure, type Meta, os } from '.' import { ProcedureImplementer } from './procedure-implementer' const p1 = new DecoratedContractProcedure({ @@ -76,12 +76,12 @@ describe('use middleware', () => { return { context: { a: 'a' } } }, // @ts-expect-error mismatch input - (input) => ({ postId: 12455 }), + input => ({ postId: 12455 }), ) implementer2.use( (input: { postId: string }) => {}, - (input) => ({ postId: '12455' }), + input => ({ postId: '12455' }), ) const i = implementer2.use( @@ -92,7 +92,7 @@ describe('use middleware', () => { }, } }, - (input) => ({ id: Number.parseInt(input.id) }), + input => ({ id: Number.parseInt(input.id) }), ) expectTypeOf(i).toEqualTypeOf< diff --git a/packages/server/src/procedure-implementer.ts b/packages/server/src/procedure-implementer.ts index ee90c89d..8c9feaa7 100644 --- a/packages/server/src/procedure-implementer.ts +++ b/packages/server/src/procedure-implementer.ts @@ -1,16 +1,15 @@ -import type { ContractProcedure, SchemaOutput } from '@orpc/contract' -import type { Schema } from '@orpc/contract' +import type { ContractProcedure, Schema, SchemaOutput } from '@orpc/contract' +import type { Context, MergeContext } from './types' import { + decorateMiddleware, type MapInputMiddleware, type Middleware, - decorateMiddleware, } from './middleware' import { type DecoratedProcedure, - type ProcedureHandler, decorateProcedure, + type ProcedureHandler, } from './procedure' -import type { Context, MergeContext } from './types' export class ProcedureImplementer< TContext extends Context, @@ -27,8 +26,8 @@ export class ProcedureImplementer< use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, >( middleware: Middleware< MergeContext, @@ -45,8 +44,8 @@ export class ProcedureImplementer< use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, UMappedInput = unknown, >( middleware: Middleware< @@ -86,12 +85,12 @@ export class ProcedureImplementer< UHandlerOutput >, ): DecoratedProcedure< - TContext, - TExtraContext, - TInputSchema, - TOutputSchema, - UHandlerOutput - > { + TContext, + TExtraContext, + TInputSchema, + TOutputSchema, + UHandlerOutput + > { return decorateProcedure({ zz$p: { middlewares: this.zz$pi.middlewares, diff --git a/packages/server/src/procedure.test.ts b/packages/server/src/procedure.test.ts index 154d165c..f24d11d0 100644 --- a/packages/server/src/procedure.test.ts +++ b/packages/server/src/procedure.test.ts @@ -1,11 +1,11 @@ import { ContractProcedure, DecoratedContractProcedure } from '@orpc/contract' import { z } from 'zod' -import { os, type Meta } from '.' +import { type Meta, os } from '.' import { type DecoratedProcedure, - Procedure, decorateProcedure, isProcedure, + Procedure, } from './procedure' it('isProcedure', () => { @@ -155,7 +155,7 @@ describe('route method', () => { }) }) -test('prefix method', () => { +it('prefix method', () => { const p = os.context<{ auth: boolean }>().handler(() => { return 'unnoq' }) @@ -298,7 +298,7 @@ describe('server action', () => { it('can deserialize form data', async () => { const p = os .input(z.object({ id: z.number(), nested: z.object({ date: z.date() }) })) - .handler(async (input) => input) + .handler(async input => input) const form = new FormData() form.append('id', '123') diff --git a/packages/server/src/procedure.ts b/packages/server/src/procedure.ts index bbfd38a8..3d36d4b4 100644 --- a/packages/server/src/procedure.ts +++ b/packages/server/src/procedure.ts @@ -1,20 +1,21 @@ +import type { Context, MergeContext, Meta, Promisable } from './types' import { type ContractProcedure, DecoratedContractProcedure, type HTTPPath, + isContractProcedure, + type RouteOptions, + type Schema, type SchemaInput, type SchemaOutput, - isContractProcedure, } from '@orpc/contract' -import type { RouteOptions, Schema } from '@orpc/contract' import { OpenAPIDeserializer } from '@orpc/transformer' import { + decorateMiddleware, type MapInputMiddleware, type Middleware, - decorateMiddleware, } from './middleware' import { createProcedureCaller } from './procedure-caller' -import type { Context, MergeContext, Meta, Promisable } from './types' export class Procedure< TContext extends Context, @@ -51,9 +52,9 @@ export type DecoratedProcedure< TOutputSchema, THandlerOutput > & { - prefix( + prefix: ( prefix: HTTPPath, - ): DecoratedProcedure< + ) => DecoratedProcedure< TContext, TExtraContext, TInputSchema, @@ -61,9 +62,9 @@ export type DecoratedProcedure< THandlerOutput > - route( + route: ( opts: RouteOptions, - ): DecoratedProcedure< + ) => DecoratedProcedure< TContext, TExtraContext, TInputSchema, @@ -71,10 +72,10 @@ export type DecoratedProcedure< THandlerOutput > - use< + use: (< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, >( middleware: Middleware< MergeContext, @@ -82,18 +83,16 @@ export type DecoratedProcedure< SchemaOutput, SchemaOutput >, - ): DecoratedProcedure< + ) => DecoratedProcedure< TContext, MergeContext, TInputSchema, TOutputSchema, THandlerOutput - > - - use< + >) & (< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, UMappedInput = unknown, >( middleware: Middleware< @@ -106,19 +105,18 @@ export type DecoratedProcedure< SchemaOutput, UMappedInput >, - ): DecoratedProcedure< + ) => DecoratedProcedure< TContext, MergeContext, TInputSchema, TOutputSchema, THandlerOutput - > + >) } & (undefined extends TContext - ? ( - input: SchemaInput | FormData, - ) => Promise> - : // biome-ignore lint/complexity/noBannedTypes: {} seem has no side-effect on this case - {}) + ? ( + input: SchemaInput | FormData, + ) => Promise> + : unknown) export interface ProcedureHandler< TContext extends Context, @@ -151,19 +149,20 @@ export function decorateProcedure< THandlerOutput >, ): DecoratedProcedure< - TContext, - TExtraContext, - TInputSchema, - TOutputSchema, - THandlerOutput -> { + TContext, + TExtraContext, + TInputSchema, + TOutputSchema, + THandlerOutput + > { if (DECORATED_PROCEDURE_SYMBOL in procedure) { return procedure as any } - const serverAction = async (input: unknown) => { + const serverAction = async (input: unknown): Promise> => { const input_ = (() => { - if (!(input instanceof FormData)) return input + if (!(input instanceof FormData)) + return input const transformer = new OpenAPIDeserializer({ schema: procedure.zz$p.contract.zz$cp.InputSchema, @@ -235,17 +234,18 @@ export type WELL_DEFINED_PROCEDURE = Procedure< > export function isProcedure(item: unknown): item is WELL_DEFINED_PROCEDURE { - if (item instanceof Procedure) return true + if (item instanceof Procedure) + return true return ( - (typeof item === 'object' || typeof item === 'function') && - item !== null && - 'zz$p' in item && - typeof item.zz$p === 'object' && - item.zz$p !== null && - 'contract' in item.zz$p && - isContractProcedure(item.zz$p.contract) && - 'handler' in item.zz$p && - typeof item.zz$p.handler === 'function' + (typeof item === 'object' || typeof item === 'function') + && item !== null + && 'zz$p' in item + && typeof item.zz$p === 'object' + && item.zz$p !== null + && 'contract' in item.zz$p + && isContractProcedure(item.zz$p.contract) + && 'handler' in item.zz$p + && typeof item.zz$p.handler === 'function' ) } diff --git a/packages/server/src/router-builder.test.ts b/packages/server/src/router-builder.test.ts index 26c5fda0..8af48666 100644 --- a/packages/server/src/router-builder.test.ts +++ b/packages/server/src/router-builder.test.ts @@ -1,6 +1,6 @@ import { ContractProcedure } from '@orpc/contract' import { z } from 'zod' -import { os, Procedure, decorateProcedure, isProcedure } from '.' +import { decorateProcedure, isProcedure, os, Procedure } from '.' import { RouterBuilder } from './router-builder' const builder = new RouterBuilder({}) @@ -22,7 +22,7 @@ describe('prefix', () => { const router = builder .prefix('/api') .prefix('/users') - .router({ ping: ping, pong }) + .router({ ping, pong }) expect(router.ping.zz$p.contract.zz$cp.path).toEqual('/api/users/ping') expect(router.pong.zz$p.contract.zz$cp.path).toEqual(undefined) @@ -43,7 +43,7 @@ describe('tags', () => { const router = builder .tags('api') .tags('users') - .router({ ping: ping, pong }) + .router({ ping, pong }) expect(router.ping.zz$p.contract.zz$cp.tags).toEqual([ 'ping', @@ -68,7 +68,7 @@ describe('middleware', () => { }) it('router', () => { - const router = builder.use(mid1).use(mid2).router({ ping: ping, pong }) + const router = builder.use(mid1).use(mid2).router({ ping, pong }) expect(router.ping.zz$p.middlewares).toEqual([mid1, mid2]) expect(router.pong.zz$p.middlewares).toEqual([mid1, mid2]) diff --git a/packages/server/src/router-builder.ts b/packages/server/src/router-builder.ts index 5dfcca14..856e4173 100644 --- a/packages/server/src/router-builder.ts +++ b/packages/server/src/router-builder.ts @@ -1,12 +1,12 @@ +import type { HandledRouter, Router } from './router' +import type { Context, MergeContext } from './types' import { DecoratedContractProcedure, type HTTPPath } from '@orpc/contract' import { + decorateMiddleware, type MapInputMiddleware, type Middleware, - decorateMiddleware, } from './middleware' import { decorateProcedure, isProcedure } from './procedure' -import type { HandledRouter, Router } from './router' -import type { Context, MergeContext } from './types' export class RouterBuilder< TContext extends Context, @@ -28,7 +28,8 @@ export class RouterBuilder< } tags(...tags: string[]): RouterBuilder { - if (!tags.length) return this + if (!tags.length) + return this return new RouterBuilder({ ...this.zz$rb, @@ -38,8 +39,8 @@ export class RouterBuilder< use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, >( middleware: Middleware< MergeContext, @@ -51,8 +52,8 @@ export class RouterBuilder< use< UExtraContext extends - | Partial>> - | undefined = undefined, + | Partial>> + | undefined = undefined, UMappedInput = unknown, >( middleware: Middleware< @@ -93,7 +94,7 @@ export class RouterBuilder< const middlewares = [ ...builderMiddlewares, ...itemMiddlewares.filter( - (item) => !builderMiddlewares.includes(item), + item => !builderMiddlewares.includes(item), ), ] @@ -110,7 +111,8 @@ export class RouterBuilder< middlewares, }, }) - } else { + } + else { handled[key] = this.router(item as any) } } diff --git a/packages/server/src/router-caller.test.ts b/packages/server/src/router-caller.test.ts index 751f0958..36ec23aa 100644 --- a/packages/server/src/router-caller.test.ts +++ b/packages/server/src/router-caller.test.ts @@ -1,5 +1,5 @@ import { z } from 'zod' -import { os, createRouterCaller } from '.' +import { createRouterCaller, os } from '.' describe('createRouterCaller', () => { let internal = false @@ -8,8 +8,8 @@ describe('createRouterCaller', () => { const osw = os.context<{ auth?: boolean }>() const ping = osw - .input(z.object({ value: z.string().transform((v) => Number(v)) })) - .output(z.object({ value: z.number().transform((v) => v.toString()) })) + .input(z.object({ value: z.string().transform(v => Number(v)) })) + .output(z.object({ value: z.number().transform(v => v.toString()) })) .handler((input, context, meta) => { expect(context).toEqual(context) expect(meta.internal).toEqual(internal) @@ -87,12 +87,12 @@ describe('createRouterCaller', () => { value: true, }) - // @ts-expect-error + // @ts-expect-error - invalid input expect(caller.ping({ value: new Date('2023-01-01') })).rejects.toThrowError( 'Validation input failed', ) - // @ts-expect-error + // @ts-expect-error - invalid input expect(caller.nested.ping({ value: true })).rejects.toThrowError( 'Validation input failed', ) @@ -158,7 +158,7 @@ describe('createRouterCaller', () => { }) const caller = createRouterCaller({ - router: router, + router, context, }) diff --git a/packages/server/src/router-caller.ts b/packages/server/src/router-caller.ts index d03d1bac..82a8728c 100644 --- a/packages/server/src/router-caller.ts +++ b/packages/server/src/router-caller.ts @@ -1,8 +1,8 @@ import type {} from '@orpc/contract' -import { type Procedure, isProcedure } from './procedure' -import { type ProcedureCaller, createProcedureCaller } from './procedure-caller' import type { Router } from './router' import type { Meta, Promisable } from './types' +import { isProcedure, type Procedure } from './procedure' +import { createProcedureCaller, type ProcedureCaller } from './procedure-caller' import {} from './utils' export interface CreateRouterCallerOptions< @@ -75,11 +75,12 @@ export function createRouterCaller< procedure: item, context: options.context as any, hooks: options.hooks as any, - path: path, + path, internal, validate, }) - } else { + } + else { caller[key] = createRouterCaller({ router: item as any, context: options.context, diff --git a/packages/server/src/router-implementer.test.ts b/packages/server/src/router-implementer.test.ts index 56e648ef..3113d4bf 100644 --- a/packages/server/src/router-implementer.test.ts +++ b/packages/server/src/router-implementer.test.ts @@ -35,12 +35,12 @@ it('required all procedure match', () => { }) implementer.router({ - p1: p1, + p1, nested: { p2: os.contract(cp2).handler(() => ''), }, nested2: { - p3: p3, + p3, }, }) @@ -49,10 +49,10 @@ it('required all procedure match', () => { // @ts-expect-error p1 is mismatch p1: os.handler(() => {}), nested: { - p2: p2, + p2, }, nested2: { - p3: p3, + p3, }, }) }).toThrowError('Mismatch implementation for procedure at [p1]') @@ -62,10 +62,10 @@ it('required all procedure match', () => { // @ts-expect-error p1 is mismatch p1: osw, nested: { - p2: p2, + p2, }, nested2: { - p3: p3, + p3, }, }) }).toThrowError('Mismatch implementation for procedure at [p1]') @@ -78,10 +78,10 @@ it('required all procedure match', () => { .output(z.string()) .handler(() => 'unnoq'), nested: { - p2: p2, + p2, }, nested2: { - p3: p3, + p3, }, }) }).toThrowError('Mismatch implementation for procedure at [p1]') @@ -93,9 +93,9 @@ it('required all procedure match', () => { expect(() => { implementer.router({ - p1: p1, + p1, nested: { - p2: p2, + p2, }, // @ts-expect-error missing p3 nested2: {}, @@ -104,9 +104,9 @@ it('required all procedure match', () => { expect(() => { implementer.router({ - p1: p1, + p1, nested: { - p2: p2, + p2, }, nested2: { p3: p3.prefix('/test'), diff --git a/packages/server/src/router-implementer.ts b/packages/server/src/router-implementer.ts index 1aff1b03..da3ada15 100644 --- a/packages/server/src/router-implementer.ts +++ b/packages/server/src/router-implementer.ts @@ -1,13 +1,13 @@ +import type { Middleware } from './middleware' +import type { RouterWithContract } from './router' +import type { Context } from './types' import { type ContractProcedure, type ContractRouter, isContractProcedure, } from '@orpc/contract' -import type { Middleware } from './middleware' import { isProcedure } from './procedure' import { ProcedureImplementer } from './procedure-implementer' -import type { RouterWithContract } from './router' -import type { Context } from './types' export class RouterImplementer< TContext extends Context, @@ -61,7 +61,8 @@ export function chainRouterImplementer< contract: item, middlewares, }) - } else { + } + else { result[key] = chainRouterImplementer(item as ContractRouter, middlewares) } } @@ -94,12 +95,14 @@ export function assertRouterImplementation( `Mismatch implementation for procedure at [${currentPath.join('.')}]`, ) } - } else { + } + else { throw new Error( `Mismatch implementation for procedure at [${currentPath.join('.')}]`, ) } - } else { + } + else { assertRouterImplementation( contractItem as ContractRouter, routerItem as any, diff --git a/packages/server/src/router.test.ts b/packages/server/src/router.test.ts index bd24cec1..35b747cc 100644 --- a/packages/server/src/router.test.ts +++ b/packages/server/src/router.test.ts @@ -3,7 +3,7 @@ import { z } from 'zod' import { os, type RouterWithContract, toContractRouter } from '.' it('require procedure match context', () => { - const osw = os.context<{ auth: boolean; userId: string }>() + const osw = os.context<{ auth: boolean, userId: string }>() osw.router({ ping: osw.context<{ auth: boolean }>().handler(() => { @@ -106,14 +106,14 @@ it('toContractRouter', () => { const p3 = oc.route({ method: 'GET', path: '/test' }) const contract = oc.router({ - p1: p1, + p1, nested: oc.router({ - p2: p2, + p2, }), nested2: { - p3: p3, + p3, }, }) diff --git a/packages/server/src/router.ts b/packages/server/src/router.ts index 3cadea3c..15fd9b29 100644 --- a/packages/server/src/router.ts +++ b/packages/server/src/router.ts @@ -1,3 +1,4 @@ +import type { Context } from './types' import { type ContractProcedure, type ContractRouter, @@ -5,10 +6,9 @@ import { } from '@orpc/contract' import { type DecoratedProcedure, - type Procedure, isProcedure, + type Procedure, } from './procedure' -import type { Context } from './types' export interface Router { [k: string]: Procedure | Router @@ -23,12 +23,12 @@ export type HandledRouter> = { infer UHandlerOutput > ? DecoratedProcedure< - UContext, - UExtraContext, - UInputSchema, - UOutputSchema, - UHandlerOutput - > + UContext, + UExtraContext, + UInputSchema, + UOutputSchema, + UHandlerOutput + > : TRouter[K] extends Router ? HandledRouter : never @@ -58,9 +58,11 @@ export function toContractRouter( if (isContractProcedure(item)) { contract[key] = item - } else if (isProcedure(item)) { + } + else if (isProcedure(item)) { contract[key] = item.zz$p.contract - } else { + } + else { contract[key] = toContractRouter(item as any) } } diff --git a/packages/server/src/types.test.ts b/packages/server/src/types.test.ts index 6f05110b..d14c1d0e 100644 --- a/packages/server/src/types.test.ts +++ b/packages/server/src/types.test.ts @@ -1,6 +1,6 @@ import type { MergeContext } from './types' -test('MergeContext', () => { +it('mergeContext', () => { expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf<{ foo: string diff --git a/packages/server/src/utils.test.ts b/packages/server/src/utils.test.ts index e4c6eb3d..703cad35 100644 --- a/packages/server/src/utils.test.ts +++ b/packages/server/src/utils.test.ts @@ -1,6 +1,6 @@ import { hook, mergeContext } from './utils' -test('mergeContext', () => { +it('mergeContext', () => { expect(mergeContext(undefined, undefined)).toBe(undefined) expect(mergeContext(undefined, { foo: 'bar' })).toEqual({ foo: 'bar' }) expect(mergeContext({ foo: 'bar' }, undefined)).toEqual({ foo: 'bar' }) @@ -48,7 +48,8 @@ describe('hook', async () => { throw error }) - } catch (e) { + } + catch (e) { expect(e).toBe(error) } @@ -68,7 +69,8 @@ describe('hook', async () => { throw error }) }) - } catch (e) { + } + catch (e) { expect(e).toBe(error) } @@ -78,7 +80,8 @@ describe('hook', async () => { throw error1 }) }) - } catch (e) { + } + catch (e) { expect(e).toBe(error1) } @@ -88,7 +91,8 @@ describe('hook', async () => { throw error2 }) }) - } catch (e) { + } + catch (e) { expect(e).toBe(error2) } @@ -105,12 +109,13 @@ describe('hook', async () => { throw error }) - } catch (e) { + } + catch (e) { expect(e).toBe(error2) } }) - it('Fist In Last Out', async () => { + it('fist In Last Out', async () => { const ref = { value: 0 } const hooked = hook(async (hooks) => { hooks.onSuccess(() => { @@ -144,7 +149,7 @@ describe('hook', async () => { expect(ref).toEqual({ value: 6 }) }) - it('Fist In Last Out - onError', async () => { + it('fist In Last Out - onError', async () => { const ref = { value: 0 } const hooked = hook(async (hooks) => { hooks.onError(() => { @@ -203,7 +208,8 @@ describe('hook', async () => { throw error2 }) - } catch (e) { + } + catch (e) { expect(e).toBe(error1) } diff --git a/packages/server/src/utils.ts b/packages/server/src/utils.ts index f7da8def..cfbfb813 100644 --- a/packages/server/src/utils.ts +++ b/packages/server/src/utils.ts @@ -4,8 +4,10 @@ export function mergeContext( a: A, b: B, ): MergeContext { - if (!a) return b as any - if (!b) return a as any + if (!a) + return b as any + if (!b) + return a as any return { ...a, @@ -29,7 +31,8 @@ export async function hook( return () => { const index = onSuccessFns.indexOf(fn) - if (index !== -1) onSuccessFns.splice(index, 1) + if (index !== -1) + onSuccessFns.splice(index, 1) } }, @@ -38,7 +41,8 @@ export async function hook( return () => { const index = onErrorFns.indexOf(fn) - if (index !== -1) onErrorFns.splice(index, 1) + if (index !== -1) + onErrorFns.splice(index, 1) } }, @@ -47,13 +51,14 @@ export async function hook( return () => { const index = onFinishFns.indexOf(fn) - if (index !== -1) onFinishFns.splice(index, 1) + if (index !== -1) + onFinishFns.splice(index, 1) } }, } - let error: unknown = undefined - let output: T | undefined = undefined + let error: unknown + let output: T | undefined try { output = await fn(hooks) @@ -63,32 +68,36 @@ export async function hook( } return output - } catch (e) { + } + catch (e) { error = e for (const onErrorFn of onErrorFns) { try { await onErrorFn(error) - } catch (e) { + } + catch (e) { error = e } } throw error - } finally { + } + finally { let hasNewError = false for (const onFinishFn of onFinishFns) { try { await onFinishFn(output, error) - } catch (e) { + } + catch (e) { error = e hasNewError = true } } if (hasNewError) { - // biome-ignore lint/correctness/noUnsafeFinally: this behavior is expected + // eslint-disable-next-line no-unsafe-finally throw error } } diff --git a/packages/shared/src/error.test.ts b/packages/shared/src/error.test.ts index e410ae60..6bbf61b0 100644 --- a/packages/shared/src/error.test.ts +++ b/packages/shared/src/error.test.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { ORPCError } from './error' -test('issues', () => { +it('issues', () => { const zodError = z.object({ a: z.string() }).safeParse({ a: 1 }).error expect( diff --git a/packages/shared/src/error.ts b/packages/shared/src/error.ts index cefa308f..09665ee6 100644 --- a/packages/shared/src/error.ts +++ b/packages/shared/src/error.ts @@ -79,18 +79,18 @@ export class ORPCError extends Error { static fromJSON(json: unknown): ORPCError | undefined { if ( - typeof json !== 'object' || - json === null || - !('code' in json) || - !Object.keys(ORPC_ERROR_CODE_STATUSES).find((key) => json.code === key) || - !('status' in json) || - typeof json.status !== 'number' || - ('message' in json && - json.message !== undefined && - typeof json.message !== 'string') || - ('issues' in json && - json.issues !== undefined && - !Array.isArray(json.issues)) + typeof json !== 'object' + || json === null + || !('code' in json) + || !Object.keys(ORPC_ERROR_CODE_STATUSES).find(key => json.code === key) + || !('status' in json) + || typeof json.status !== 'number' + || ('message' in json + && json.message !== undefined + && typeof json.message !== 'string') + || ('issues' in json + && json.issues !== undefined + && !Array.isArray(json.issues)) ) { return undefined } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 8e8f48cb..e70b715b 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,7 +1,7 @@ -export type * from 'type-fest' - export * from './json' -export * from './object' +export * from './object' export { isPlainObject } from 'is-what' -export { guard, trim, mapEntries, omit, mapValues } from 'radash' + +export { guard, mapEntries, mapValues, omit, trim } from 'radash' +export type * from 'type-fest' diff --git a/packages/shared/src/json.ts b/packages/shared/src/json.ts index 5aad0469..9b58803c 100644 --- a/packages/shared/src/json.ts +++ b/packages/shared/src/json.ts @@ -1,9 +1,11 @@ export function parseJSONSafely(text: string): unknown { - if (text === '') return undefined + if (text === '') + return undefined try { return JSON.parse(text) - } catch (e) { + } + catch { return text } } diff --git a/packages/shared/src/object.ts b/packages/shared/src/object.ts index 4013d607..0ae455f8 100644 --- a/packages/shared/src/object.ts +++ b/packages/shared/src/object.ts @@ -33,8 +33,8 @@ export function get( for (const segment of segments) { if ( - (typeof currentRef !== 'object' && typeof currentRef !== 'function') || - currentRef === null + (typeof currentRef !== 'object' && typeof currentRef !== 'function') + || currentRef === null ) { return undefined } @@ -44,8 +44,8 @@ export function get( } if ( - (typeof currentRef !== 'object' && typeof currentRef !== 'function') || - currentRef === null + (typeof currentRef !== 'object' && typeof currentRef !== 'function') + || currentRef === null ) { return undefined } @@ -59,15 +59,17 @@ export function findDeepMatches( segments: Segment[] = [], maps: Segment[][] = [], values: unknown[] = [], -): { maps: Segment[][]; values: unknown[] } { +): { maps: Segment[][], values: unknown[] } { if (check(payload)) { maps.push(segments) values.push(payload) - } else if (Array.isArray(payload)) { + } + else if (Array.isArray(payload)) { payload.forEach((v, i) => { findDeepMatches(check, v, [...segments, i], maps, values) }) - } else if (isPlainObject(payload)) { + } + else if (isPlainObject(payload)) { for (const key in payload) { findDeepMatches(check, payload[key], [...segments, key], maps, values) } diff --git a/packages/transformer/README.md b/packages/transformer/README.md index 2249fc37..75f8f272 100644 --- a/packages/transformer/README.md +++ b/packages/transformer/README.md @@ -5,11 +5,12 @@ A library for transforming oRPC payloads in different client-server scenarios. ## Overview `@orpc/transformer` provides two distinct sets of serializers and deserializers for different use cases: + 1. `ORPC` serializer/deserializer designed for internal oRPC clients and servers to communicate with each other. 2. `OpenAPI` serializer/deserializer designed for external clients consuming the API through OpenAPI specifications. ```ts -import { ORPCSerializer, ORPCDeserializer } from '@orpc/transformer' +import { ORPCDeserializer, ORPCSerializer } from '@orpc/transformer' const serializer = new ORPCSerializer() // or OpenAPISerializer const deserializer = new ORPCDeserializer() // or OpenAPIDeserializer @@ -74,9 +75,9 @@ const payload = { friends: ['Alice', 'Bob'], } -// with bracket notation +// with bracket notation -{ +const equivalent = { 'user[name]': 'John Doe', 'user[age]': 30, 'avatar': new File([], 'avatar.png'), diff --git a/packages/transformer/src/bracket-notation.test.ts b/packages/transformer/src/bracket-notation.test.ts index 9c5a0e0b..09057cbc 100644 --- a/packages/transformer/src/bracket-notation.test.ts +++ b/packages/transformer/src/bracket-notation.test.ts @@ -13,10 +13,8 @@ describe('stringifyPath', () => { }) it('should handle empty path segments', () => { - ;() => { - // @ts-expect-error: empty segments are not allowed - expect(stringifyPath([])).toBe('') - } + // @ts-expect-error: empty segments are not allowed + expect(() => stringifyPath([])).toThrow() expect(stringifyPath(['', 'test'])).toBe('[test]') }) @@ -274,7 +272,7 @@ describe('deserialize', () => { ] as const const expected = { 'key[with]bracket.s': 'value', - key: { + 'key': { va: { 'lu.e]': 'value', }, @@ -319,7 +317,7 @@ describe('deserialize', () => { const expected = { names: { '': 'empty', - age: 'string', + 'age': 'string', }, } expect(deserialize(input)).toEqual(expected) @@ -341,7 +339,7 @@ describe('deserialize', () => { const expected = { names: { '': 'John3', - index: 'Jane', + 'index': 'Jane', }, } expect(deserialize(input)).toEqual(expected) @@ -356,7 +354,7 @@ describe('deserialize', () => { expect(deserialize(input)).toEqual(expected) }) - it('should deserialize a root array', () => { + it('should deserialize a root array 2', () => { const input = [ ['[]', '1'], ['[]', '2'], diff --git a/packages/transformer/src/bracket-notation.ts b/packages/transformer/src/bracket-notation.ts index 225a72d0..db587414 100644 --- a/packages/transformer/src/bracket-notation.ts +++ b/packages/transformer/src/bracket-notation.ts @@ -26,7 +26,8 @@ export function serialize( payload: unknown, parentKey = '', ): [string, unknown][] { - if (!Array.isArray(payload) && !isPlainObject(payload)) return [['', payload]] + if (!Array.isArray(payload) && !isPlainObject(payload)) + return [['', payload]] const result: [string, unknown][] = [] @@ -35,11 +36,13 @@ export function serialize( value.forEach((item, index) => { helper(item, [...path, String(index)]) }) - } else if (isPlainObject(value)) { + } + else if (isPlainObject(value)) { for (const [key, val] of Object.entries(value)) { helper(val, [...path, key]) } - } else { + } + else { result.push([stringifyPath(path as [string, ...string[]]), value]) } } @@ -93,7 +96,8 @@ export function deserialize( if (last === '') { arrayPushPaths.add(base) - } else { + } + else { arrayPushPaths.delete(base) } } @@ -180,7 +184,7 @@ export function deserialize( * ``` */ export function escapeSegment(segment: string): string { - return segment.replace(/\\|\[|\]/g, (match) => { + return segment.replace(/[\\[\]]/g, (match) => { switch (match) { case '\\': return '\\\\' @@ -237,7 +241,8 @@ export function stringifyPath(path: readonly [string, ...string[]]): string { * ``` */ export function parsePath(path: string): [string, ...string[]] { - if (path === '') return [''] + if (path === '') + return [''] const result: string[] = [] let currentSegment = '' @@ -264,14 +269,17 @@ export function parsePath(path: string): [string, ...string[]] { if (backslashCount % 2 === 1) { if (inBracket) { bracketContent += literalBackslashes + char - } else { + } + else { currentSegment += literalBackslashes + char } - } else { + } + else { // Even number means the bracket is not escaped if (inBracket) { bracketContent += literalBackslashes - } else { + } + else { currentSegment += literalBackslashes } @@ -282,24 +290,29 @@ export function parsePath(path: string): [string, ...string[]] { inBracket = true bracketContent = '' currentSegment = '' - } else if (char === ']' && inBracket) { + } + else if (char === ']' && inBracket) { result.push(bracketContent) inBracket = false bracketContent = '' - } else { + } + else { if (inBracket) { bracketContent += char - } else { + } + else { currentSegment += char } } } - } else { + } + else { // For non-bracket characters, just add all backslashes as literals const allBackslashes = '\\'.repeat(backslashCount) if (inBracket) { bracketContent += allBackslashes + char - } else { + } + else { currentSegment += allBackslashes + char } } @@ -328,7 +341,8 @@ export function parsePath(path: string): [string, ...string[]] { // Add normal characters if (inBracket) { bracketContent += char - } else { + } + else { currentSegment += char } } @@ -338,7 +352,8 @@ export function parsePath(path: string): [string, ...string[]] { const remainingBackslashes = '\\'.repeat(backslashCount) if (inBracket) { bracketContent += remainingBackslashes - } else { + } + else { currentSegment += remainingBackslashes } } @@ -349,7 +364,8 @@ export function parsePath(path: string): [string, ...string[]] { result.push(currentSegment) } result.push(`[${bracketContent}`) - } else if (currentSegment !== '' || result.length === 0) { + } + else if (currentSegment !== '' || result.length === 0) { result.push(currentSegment) } diff --git a/packages/transformer/src/index.ts b/packages/transformer/src/index.ts index ebad8723..1c9628d7 100644 --- a/packages/transformer/src/index.ts +++ b/packages/transformer/src/index.ts @@ -1,7 +1,7 @@ -export * from './types' export * from './openapi/deserializer' export * from './openapi/serializer' +export { zodCoerce } from './openapi/zod-coerce' export * from './orpc/deserializer' export * from './orpc/serializer' -export { zodCoerce } from './openapi/zod-coerce' +export * from './types' diff --git a/packages/transformer/src/openapi/deserializer.ts b/packages/transformer/src/openapi/deserializer.ts index ba706922..6850c00d 100644 --- a/packages/transformer/src/openapi/deserializer.ts +++ b/packages/transformer/src/openapi/deserializer.ts @@ -1,13 +1,12 @@ /// /// -import type { Deserializer } from '../types' +import type { ZodType } from 'zod' +import type { Deserializer } from '../types' import { parseJSONSafely } from '@orpc/shared' import cd from 'content-disposition' -import type { ZodType } from 'zod' import * as BracketNotation from '../bracket-notation' -import type {} from '../types' import { zodCoerce } from './zod-coerce' export class OpenAPIDeserializer implements Deserializer { @@ -33,11 +32,11 @@ export class OpenAPIDeserializer implements Deserializer { } if ( - ('method' in re && re.method === 'GET') || - contentType?.startsWith('application/x-www-form-urlencoded') + ('method' in re && re.method === 'GET') + || contentType?.startsWith('application/x-www-form-urlencoded') ) { - const params = - 'method' in re && re.method === 'GET' + const params + = 'method' in re && re.method === 'GET' ? new URLSearchParams(re.url.split('?')[1]) : new URLSearchParams(await re.text()) diff --git a/packages/transformer/src/openapi/serializer.ts b/packages/transformer/src/openapi/serializer.ts index 8f50ab12..5bdb3029 100644 --- a/packages/transformer/src/openapi/serializer.ts +++ b/packages/transformer/src/openapi/serializer.ts @@ -1,3 +1,4 @@ +import type { Serialized, Serializer } from '../types' import { findDeepMatches } from '@orpc/shared' import { ORPCError } from '@orpc/shared/error' import cd from 'content-disposition' @@ -5,7 +6,6 @@ import { safeParse } from 'fast-content-type-parse' import { isPlainObject } from 'is-what' import wcmatch from 'wildcard-match' import * as BracketNotation from '../bracket-notation' -import type { Serialized, Serializer } from '../types' export class OpenAPISerializer implements Serializer { constructor( @@ -22,16 +22,16 @@ export class OpenAPISerializer implements Serializer { if (payload instanceof Blob) { const contentType = this.getBlobContentType(payload) - if (typeMatchers.some((isMatch) => isMatch(contentType))) { + if (typeMatchers.some(isMatch => isMatch(contentType))) { return this.serializeAsBlob(payload) } } const payload_ = preSerialize(payload) - const hasBlobs = - findDeepMatches((v) => v instanceof Blob, payload_).values.length > 0 + const hasBlobs + = findDeepMatches(v => v instanceof Blob, payload_).values.length > 0 - const isExpectedMultipartFormData = typeMatchers.some((isMatch) => + const isExpectedMultipartFormData = typeMatchers.some(isMatch => isMatch('multipart/form-data'), ) @@ -39,12 +39,12 @@ export class OpenAPISerializer implements Serializer { return this.serializeAsMultipartFormData(payload_) } - if (typeMatchers.some((isMatch) => isMatch('application/json'))) { + if (typeMatchers.some(isMatch => isMatch('application/json'))) { return this.serializeAsJSON(payload_) } if ( - typeMatchers.some((isMatch) => + typeMatchers.some(isMatch => isMatch('application/x-www-form-urlencoded'), ) ) { @@ -75,19 +75,22 @@ export class OpenAPISerializer implements Serializer { for (const [path, value] of BracketNotation.serialize(payload)) { if ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' + typeof value === 'string' + || typeof value === 'number' + || typeof value === 'boolean' ) { form.append(path, value.toString()) - } else if (value === null) { + } + else if (value === null) { form.append(path, 'null') - } else if (value instanceof Date && !Number.isNaN(value.valueOf())) { + } + else if (value instanceof Date && !Number.isNaN(value.valueOf())) { form.append( path, Number.isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString(), ) - } else if (value instanceof Blob) { + } + else if (value instanceof Blob) { form.append(path, value) } } @@ -103,14 +106,16 @@ export class OpenAPISerializer implements Serializer { for (const [path, value] of BracketNotation.serialize(payload)) { if ( - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' + typeof value === 'string' + || typeof value === 'number' + || typeof value === 'boolean' ) { params.append(path, value.toString()) - } else if (value === null) { + } + else if (value === null) { params.append(path, 'null') - } else if (value instanceof Date && !Number.isNaN(value.valueOf())) { + } + else if (value instanceof Date && !Number.isNaN(value.valueOf())) { params.append( path, Number.isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString(), @@ -145,19 +150,26 @@ export class OpenAPISerializer implements Serializer { } export function preSerialize(payload: unknown): unknown { - if (payload instanceof Set) return preSerialize([...payload]) - if (payload instanceof Map) return preSerialize([...payload.entries()]) + if (payload instanceof Set) + return preSerialize([...payload]) + if (payload instanceof Map) + return preSerialize([...payload.entries()]) if (Array.isArray(payload)) { - return payload.map((v) => (v === undefined ? 'undefined' : preSerialize(v))) + return payload.map(v => (v === undefined ? 'undefined' : preSerialize(v))) } - if (Number.isNaN(payload)) return 'NaN' - if (typeof payload === 'bigint') return payload.toString() + if (Number.isNaN(payload)) + return 'NaN' + if (typeof payload === 'bigint') + return payload.toString() if (payload instanceof Date && Number.isNaN(payload.getTime())) { return 'Invalid Date' } - if (payload instanceof RegExp) return payload.toString() - if (payload instanceof URL) return payload.toString() - if (!isPlainObject(payload)) return payload + if (payload instanceof RegExp) + return payload.toString() + if (payload instanceof URL) + return payload.toString() + if (!isPlainObject(payload)) + return payload return Object.keys(payload).reduce( (carry, key) => { const val = payload[key] diff --git a/packages/transformer/src/openapi/zod-coerce.ts b/packages/transformer/src/openapi/zod-coerce.ts index 138b56ef..33a4150e 100644 --- a/packages/transformer/src/openapi/zod-coerce.ts +++ b/packages/transformer/src/openapi/zod-coerce.ts @@ -37,7 +37,7 @@ export interface ZodCoerceOptions { /** * Some fix rules only apply in the root * - * @internal use it with your own risk + * @internal * @default true */ isRoot?: boolean @@ -52,10 +52,10 @@ export function zodCoerce( const options_ = { ...options, isRoot: false } if ( - isRoot && - options?.bracketNotation && - Array.isArray(value) && - value.length === 1 + isRoot + && options?.bracketNotation + && Array.isArray(value) + && value.length === 1 ) { const newValue = zodCoerce(schema, value[0], options_) if (schema.safeParse(newValue).success) { @@ -68,12 +68,13 @@ export function zodCoerce( if (customType === 'Invalid Date') { if ( - typeof value === 'string' && - value.toLocaleLowerCase() === 'invalid date' + typeof value === 'string' + && value.toLocaleLowerCase() === 'invalid date' ) { return new Date('Invalid Date') } - } else if (customType === 'RegExp') { + } + else if (customType === 'RegExp') { if (typeof value === 'string' && value.startsWith('/')) { const match = value.match(/^\/(.*)\/([a-z]*)$/) @@ -82,7 +83,8 @@ export function zodCoerce( return new RegExp(pattern!, flags) } } - } else if (customType === 'URL') { + } + else if (customType === 'URL') { if (typeof value === 'string') { const url = guard(() => new URL(value)) if (url !== undefined) { @@ -131,9 +133,9 @@ export function zodCoerce( // else if (typeName === ZodFirstPartyTypeKind.ZodNull) { if ( - options_?.bracketNotation && - typeof value === 'string' && - value.toLowerCase() === 'null' + options_?.bracketNotation + && typeof value === 'string' + && value.toLowerCase() === 'null' ) { return null } @@ -141,8 +143,8 @@ export function zodCoerce( // else if ( - typeName === ZodFirstPartyTypeKind.ZodUndefined || - typeName === ZodFirstPartyTypeKind.ZodVoid + typeName === ZodFirstPartyTypeKind.ZodUndefined + || typeName === ZodFirstPartyTypeKind.ZodVoid ) { if (typeof value === 'string' && value.toLowerCase() === 'undefined') { return undefined @@ -152,10 +154,10 @@ export function zodCoerce( // else if (typeName === ZodFirstPartyTypeKind.ZodDate) { if ( - typeof value === 'string' && - (value.includes('-') || - value.includes(':') || - value.toLocaleLowerCase() === 'invalid date') + typeof value === 'string' + && (value.includes('-') + || value.includes(':') + || value.toLocaleLowerCase() === 'invalid date') ) { return new Date(value) } @@ -173,13 +175,13 @@ export function zodCoerce( // else if ( - typeName === ZodFirstPartyTypeKind.ZodArray || - typeName === ZodFirstPartyTypeKind.ZodTuple + typeName === ZodFirstPartyTypeKind.ZodArray + || typeName === ZodFirstPartyTypeKind.ZodTuple ) { const schema_ = schema as ZodArray if (Array.isArray(value)) { - return value.map((v) => zodCoerce(schema_._def.type, v, options_)) + return value.map(v => zodCoerce(schema_._def.type, v, options_)) } if (options_?.bracketNotation) { @@ -188,14 +190,14 @@ export function zodCoerce( } if ( - isPlainObject(value) && - Object.keys(value).every((k) => /^[1-9][0-9]*$/.test(k) || k === '0') + isPlainObject(value) + && Object.keys(value).every(k => /^[1-9]\d*$/.test(k) || k === '0') ) { const indexes = Object.keys(value) - .map((k) => Number(k)) + .map(k => Number(k)) .sort((a, b) => a - b) - const arr = new Array((indexes.at(-1) ?? -1) + 1) + const arr = Array.from({ length: (indexes.at(-1) ?? -1) + 1 }) for (const i of indexes) { arr[i] = zodCoerce(schema_._def.type, value[i], options_) @@ -219,7 +221,8 @@ export function zodCoerce( ]) for (const k of keys) { - if (!(k in value)) continue + if (!(k in value)) + continue const v = value[k] newObj[k] = zodCoerce( @@ -250,7 +253,7 @@ export function zodCoerce( if (Array.isArray(value)) { return new Set( - value.map((v) => zodCoerce(schema_._def.valueType, v, options_)), + value.map(v => zodCoerce(schema_._def.valueType, v, options_)), ) } @@ -260,14 +263,14 @@ export function zodCoerce( } if ( - isPlainObject(value) && - Object.keys(value).every((k) => /^[1-9][0-9]*$/.test(k) || k === '0') + isPlainObject(value) + && Object.keys(value).every(k => /^[1-9]\d*$/.test(k) || k === '0') ) { const indexes = Object.keys(value) - .map((k) => Number(k)) + .map(k => Number(k)) .sort((a, b) => a - b) - const arr = new Array((indexes.at(-1) ?? -1) + 1) + const arr = Array.from({ length: (indexes.at(-1) ?? -1) + 1 }) for (const i of indexes) { arr[i] = zodCoerce(schema_._def.valueType, value[i], options_) @@ -283,8 +286,8 @@ export function zodCoerce( const schema_ = schema as ZodMap if ( - Array.isArray(value) && - value.every((i) => Array.isArray(i) && i.length === 2) + Array.isArray(value) + && value.every(i => Array.isArray(i) && i.length === 2) ) { return new Map( value.map(([k, v]) => [ @@ -300,18 +303,18 @@ export function zodCoerce( } if (isPlainObject(value)) { - const arr = new Array(Object.keys(value).length) + const arr = Array.from({ length: Object.keys(value).length }) .fill(undefined) .map((_, i) => - isPlainObject(value[i]) && - Object.keys(value[i]).length === 2 && - '0' in value[i] && - '1' in value[i] + isPlainObject(value[i]) + && Object.keys(value[i]).length === 2 + && '0' in value[i] + && '1' in value[i] ? ([value[i]['0'], value[i]['1']] as const) : undefined, ) - if (arr.every((v) => !!v)) { + if (arr.every(v => !!v)) { return new Map( arr.map(([k, v]) => [ zodCoerce(schema_._def.keyType, k, options_), @@ -342,8 +345,8 @@ export function zodCoerce( // else if ( - typeName === ZodFirstPartyTypeKind.ZodUnion || - typeName === ZodFirstPartyTypeKind.ZodDiscriminatedUnion + typeName === ZodFirstPartyTypeKind.ZodUnion + || typeName === ZodFirstPartyTypeKind.ZodDiscriminatedUnion ) { const schema_ = schema as | ZodUnion<[ZodTypeAny]> @@ -357,7 +360,8 @@ export function zodCoerce( for (const s of schema_._def.options) { const newValue = zodCoerce(s, value, { ...options_, isRoot }) - if (newValue === value) continue + if (newValue === value) + continue const result = schema_.safeParse(newValue) @@ -493,17 +497,20 @@ export function zodCoerce( if (num !== undefined) { return num } - } else if (expectedValue === undefined) { + } + else if (expectedValue === undefined) { if (value.toLocaleLowerCase() === 'undefined') { return undefined } - } else if (options?.bracketNotation) { + } + else if (options?.bracketNotation) { if (typeof expectedValue === 'number') { const num = Number(value) if (!Number.isNaN(num)) { return num } - } else if (typeof expectedValue === 'boolean') { + } + else if (typeof expectedValue === 'boolean') { const lower = value.toLowerCase() if (lower === 'false' || lower === 'off' || lower === 'f') { @@ -513,7 +520,8 @@ export function zodCoerce( if (lower === 'true' || lower === 'on' || lower === 't') { return true } - } else if (expectedValue === null) { + } + else if (expectedValue === null) { if (value.toLocaleLowerCase() === 'null') { return null } diff --git a/packages/transformer/src/orpc/deserializer.ts b/packages/transformer/src/orpc/deserializer.ts index 5b2d2355..7e1720a1 100644 --- a/packages/transformer/src/orpc/deserializer.ts +++ b/packages/transformer/src/orpc/deserializer.ts @@ -1,5 +1,5 @@ -import { set } from '@orpc/shared' import type { Deserializer } from '../types' +import { set } from '@orpc/shared' import * as SuperJSON from './super-json' export class ORPCDeserializer implements Deserializer { diff --git a/packages/transformer/src/orpc/serializer.ts b/packages/transformer/src/orpc/serializer.ts index 5668cdf5..3acdd3bd 100644 --- a/packages/transformer/src/orpc/serializer.ts +++ b/packages/transformer/src/orpc/serializer.ts @@ -1,11 +1,11 @@ -import { findDeepMatches } from '@orpc/shared' import type { Body, Serializer } from '../types' +import { findDeepMatches } from '@orpc/shared' import * as SuperJSON from './super-json' export class ORPCSerializer implements Serializer { - serialize(payload: unknown): { body: Body; headers: Headers } { + serialize(payload: unknown): { body: Body, headers: Headers } { const { data, meta } = SuperJSON.serialize(payload) - const { maps, values } = findDeepMatches((v) => v instanceof Blob, data) + const { maps, values } = findDeepMatches(v => v instanceof Blob, data) if (values.length > 0) { const form = new FormData() diff --git a/packages/transformer/src/orpc/super-json.ts b/packages/transformer/src/orpc/super-json.ts index 0e929cc2..f060a973 100644 --- a/packages/transformer/src/orpc/super-json.ts +++ b/packages/transformer/src/orpc/super-json.ts @@ -18,9 +18,9 @@ export function serialize( segments: Segment[] = [], meta: JSONMeta = [], ): { - data: unknown - meta: JSONMeta -} { + data: unknown + meta: JSONMeta + } { if (typeof value === 'bigint') { meta.push(['bigint', segments]) return { data: value.toString(), meta } @@ -128,7 +128,8 @@ export function deserialize({ if (match) { const [, pattern, flags] = match currentRef[preSegment] = new RegExp(pattern!, flags) - } else { + } + else { currentRef[preSegment] = new RegExp(currentRef[preSegment]) } diff --git a/packages/transformer/src/types.ts b/packages/transformer/src/types.ts index 1a8613fa..1d2995fe 100644 --- a/packages/transformer/src/types.ts +++ b/packages/transformer/src/types.ts @@ -8,11 +8,11 @@ export interface Serialized { } export interface Serializer { - serialize(payload: unknown): Serialized + serialize: (payload: unknown) => Serialized } export type Deserialized = Promise export interface Deserializer { - deserialize(re: Request | Response): Deserialized + deserialize: (re: Request | Response) => Deserialized } diff --git a/packages/transformer/tests/openapi-human-readable.test.ts b/packages/transformer/tests/openapi-human-readable.test.ts index 779b3a38..38a6c665 100644 --- a/packages/transformer/tests/openapi-human-readable.test.ts +++ b/packages/transformer/tests/openapi-human-readable.test.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { OpenAPIDeserializer, OpenAPISerializer } from '../src' -describe('OpenAPI transformer human readable', () => { +describe('openAPI transformer human readable', () => { it('should deserialize object', async () => { const form = new FormData() form.append('number', '123') @@ -78,14 +78,15 @@ describe('OpenAPI transformer human readable', () => { const { body } = new OpenAPISerializer().serialize({ name: 'test', object: { a: 1, b: 2 }, - file: file, + file, nested: { - file: file, + 'file': file, '[]file\\]': file, }, }) - if (!(body instanceof FormData)) throw new Error('body must be FormData') + if (!(body instanceof FormData)) + throw new Error('body must be FormData') expect(body.get('file')).toBeInstanceOf(Blob) expect(body.get('nested[file]')).toBeInstanceOf(Blob) diff --git a/packages/transformer/tests/superjson.bench.ts b/packages/transformer/tests/superjson.bench.ts index ac3ce6f1..e99cc53c 100644 --- a/packages/transformer/tests/superjson.bench.ts +++ b/packages/transformer/tests/superjson.bench.ts @@ -1,6 +1,5 @@ -import { describe } from 'node:test' import SuperJSON from 'superjson' -import { bench } from 'vitest' +import { bench, describe } from 'vitest' import * as SuperJSON2 from '../src/orpc/super-json' describe('with simple data', () => { @@ -26,11 +25,11 @@ describe('with simple data', () => { regexp: /uic/gi, } - bench('SuperJSON', () => { + bench('superJSON', () => { SuperJSON.deserialize(SuperJSON.serialize(data)) }) - bench('SuperJSON2', () => { + bench('superJSON2', () => { SuperJSON2.deserialize(SuperJSON2.serialize(data)) }) }) @@ -64,11 +63,11 @@ describe('with deep data', () => { }, } - bench('SuperJSON', () => { + bench('superJSON', () => { SuperJSON.deserialize(SuperJSON.serialize(data)) }) - bench('SuperJSON2', () => { + bench('superJSON2', () => { SuperJSON2.deserialize(SuperJSON2.serialize(data)) }) }) diff --git a/packages/transformer/tests/transformer.bench.ts b/packages/transformer/tests/transformer.bench.ts index a5c7fea0..6c5b8872 100644 --- a/packages/transformer/tests/transformer.bench.ts +++ b/packages/transformer/tests/transformer.bench.ts @@ -1,10 +1,10 @@ import { bench } from 'vitest' import { z } from 'zod' import { - ORPCDeserializer, - ORPCSerializer, OpenAPIDeserializer, OpenAPISerializer, + ORPCDeserializer, + ORPCSerializer, } from '../src' describe('simple data', () => { @@ -13,7 +13,7 @@ describe('simple data', () => { number: 1234, boolean: true, null: null, - undefined: undefined, + undefined, bigint: BigInt(1234), date: new Date('2023-01-01'), map: new Map([ @@ -52,7 +52,7 @@ describe('simple data', () => { const orpcDeserializer = new ORPCDeserializer() const openapiDeserializer = new OpenAPIDeserializer({ schema }) - bench('ORPCTransformer', () => { + bench('oRPCTransformer', () => { orpcDeserializer.deserialize( new Request('http://localhost', { method: 'POST', @@ -61,7 +61,7 @@ describe('simple data', () => { ) }) - bench('OpenAPITransformer', () => { + bench('openAPITransformer', () => { openapiDeserializer.deserialize( new Request('http://localhost', { method: 'POST', @@ -77,7 +77,7 @@ describe('with file', () => { number: 1234, boolean: true, null: null, - undefined: undefined, + undefined, bigint: BigInt(1234), date: new Date('2023-01-01'), map: new Map([ @@ -120,7 +120,7 @@ describe('with file', () => { const orpcDeserializer = new ORPCDeserializer() const openapiDeserializer = new OpenAPIDeserializer({ schema }) - bench('ORPCTransformer', () => { + bench('oRPCTransformer', () => { orpcDeserializer.deserialize( new Request('http://localhost', { method: 'POST', @@ -129,7 +129,7 @@ describe('with file', () => { ) }) - bench('OpenAPITransformer', () => { + bench('openAPITransformer', () => { openapiDeserializer.deserialize( new Request('http://localhost', { method: 'POST', @@ -175,7 +175,7 @@ describe('with unions', () => { const orpcDeserializer = new ORPCDeserializer() const openapiDeserializer = new OpenAPIDeserializer({ schema }) - bench('ORPCTransformer', () => { + bench('oRPCTransformer', () => { orpcDeserializer.deserialize( new Request('http://localhost', { method: 'POST', @@ -184,7 +184,7 @@ describe('with unions', () => { ) }) - bench('OpenAPITransformer', () => { + bench('openAPITransformer', () => { openapiDeserializer.deserialize( new Request('http://localhost', { method: 'POST', diff --git a/packages/transformer/tests/types-support.test.ts b/packages/transformer/tests/types-support.test.ts index cc1c3b8c..8ec555df 100644 --- a/packages/transformer/tests/types-support.test.ts +++ b/packages/transformer/tests/types-support.test.ts @@ -1,11 +1,11 @@ import { oz } from '@orpc/zod' -import { type ZodType, object, z } from 'zod' +import { object, z, type ZodType } from 'zod' import { type Deserializer, - ORPCDeserializer, - ORPCSerializer, OpenAPIDeserializer, OpenAPISerializer, + ORPCDeserializer, + ORPCSerializer, type Serializer, } from '../src' @@ -24,28 +24,28 @@ const transformers: { { name: 'OpenAPITransformer auto', createSerializer: () => new OpenAPISerializer(), - createDeserializer: (schema) => new OpenAPIDeserializer({ schema }), + createDeserializer: schema => new OpenAPIDeserializer({ schema }), isFileSupport: true, }, { name: 'OpenAPITransformer multipart/form-data', createSerializer: () => new OpenAPISerializer({ accept: 'multipart/form-data' }), - createDeserializer: (schema) => new OpenAPIDeserializer({ schema }), + createDeserializer: schema => new OpenAPIDeserializer({ schema }), isFileSupport: true, }, { name: 'OpenAPITransformer application/json', createSerializer: () => new OpenAPISerializer({ accept: 'application/json' }), - createDeserializer: (schema) => new OpenAPIDeserializer({ schema }), + createDeserializer: schema => new OpenAPIDeserializer({ schema }), isFileSupport: false, }, { name: 'OpenAPITransformer application/x-www-form-urlencoded', createSerializer: () => new OpenAPISerializer({ accept: 'application/x-www-form-urlencoded' }), - createDeserializer: (schema) => new OpenAPIDeserializer({ schema }), + createDeserializer: schema => new OpenAPIDeserializer({ schema }), isFileSupport: false, }, ] @@ -187,16 +187,16 @@ describe.each(transformers)( const object = { '[]data\\]': origin, - list: [origin], - map: new Map([[origin, origin]]), - set: new Set([origin]), + 'list': [origin], + 'map': new Map([[origin, origin]]), + 'set': new Set([origin]), } const objectSchema = z.object({ '[]data\\]': schema, - list: z.array(schema), - map: z.map(schema, schema), - set: z.set(schema), + 'list': z.array(schema), + 'map': z.map(schema, schema), + 'set': z.set(schema), }) const serializer = createSerializer() diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index 2984da39..8cfbe053 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -4,14 +4,14 @@ import type { JSONSchema } from 'json-schema-typed/draft-2020-12' import wcmatch from 'wildcard-match' import { + custom, type CustomErrorParams, + type input, + type output, type ZodEffects, type ZodType, type ZodTypeAny, type ZodTypeDef, - custom, - type input, - type output, } from 'zod' export type CustomZodType = 'File' | 'Blob' | 'Invalid Date' | 'RegExp' | 'URL' @@ -64,8 +64,8 @@ function composeParams(options: { defaultMessage?: string | ((input: T) => string) }): (input: T) => CustomParams { return (val) => { - const defaultMessage = - typeof options.defaultMessage === 'function' + const defaultMessage + = typeof options.defaultMessage === 'function' ? options.defaultMessage(val) : options.defaultMessage @@ -98,17 +98,17 @@ function composeParams(options: { export function file( params?: string | CustomParams | ((input: unknown) => CustomParams), ): ZodType, ZodTypeDef, InstanceType> & { - type( + type: ( mimeType: string, params?: string | CustomParams | ((input: unknown) => CustomParams), - ): ZodEffects< + ) => ZodEffects< ZodType, ZodTypeDef, InstanceType>, InstanceType, InstanceType > } { const schema = custom>( - (val) => val instanceof File, + val => val instanceof File, composeParams({ params, defaultMessage: 'Input is not a file' }), ) @@ -119,15 +119,15 @@ export function file( return Object.assign(schema, { type: ( mimeType: string, - params: string | CustomParams | ((input: unknown) => CustomParams), + params?: string | CustomParams | ((input: unknown) => CustomParams), ) => { const isMatch = wcmatch(mimeType) const refinedSchema = schema.refine( - (val) => isMatch(val.type.split(';')[0]!), + val => isMatch(val.type.split(';')[0]!), composeParams>({ params, - defaultMessage: (val) => + defaultMessage: val => `Expected a file of type ${mimeType} but got a file of type ${val.type || 'unknown'}`, }), ) @@ -146,7 +146,7 @@ export function blob( params?: string | CustomParams | ((input: unknown) => CustomParams), ): ZodType, ZodTypeDef, InstanceType> { const schema = custom>( - (val) => val instanceof Blob, + val => val instanceof Blob, composeParams({ params, defaultMessage: 'Input is not a blob' }), ) @@ -161,7 +161,7 @@ export function invalidDate( params?: string | CustomParams | ((input: unknown) => CustomParams), ): ZodType { const schema = custom( - (val) => val instanceof Date && Number.isNaN(val.getTime()), + val => val instanceof Date && Number.isNaN(val.getTime()), composeParams({ params, defaultMessage: 'Input is not an invalid date' }), ) @@ -176,7 +176,7 @@ export function regexp( options?: CustomParams, ): ZodType { const schema = custom( - (val) => val instanceof RegExp, + val => val instanceof RegExp, composeParams({ params: options, defaultMessage: 'Input is not a regexp' }), ) @@ -189,7 +189,7 @@ export function regexp( export function url(options?: CustomParams): ZodType { const schema = custom( - (val) => val instanceof URL, + val => val instanceof URL, composeParams({ params: options, defaultMessage: 'Input is not a URL' }), ) @@ -219,8 +219,8 @@ export function openapi< ): ReturnType { const newSchema = schema.refine(() => true) as ReturnType - const SYMBOL = - options?.mode === 'input' + const SYMBOL + = options?.mode === 'input' ? CUSTOM_JSON_SCHEMA_INPUT_SYMBOL : options?.mode === 'output' ? CUSTOM_JSON_SCHEMA_OUTPUT_SYMBOL diff --git a/packages/zod/tests/custom.test.ts b/packages/zod/tests/custom.test.ts index 58bb6aa3..01f16f61 100644 --- a/packages/zod/tests/custom.test.ts +++ b/packages/zod/tests/custom.test.ts @@ -95,7 +95,7 @@ const cases = [ ], ] as const -describe('Custom Zod Types', () => { +describe('custom Zod Types', () => { it.each(cases)( 'should validate %s', (name, schema, value, mimeType, errorMessage) => { @@ -107,7 +107,8 @@ describe('Custom Zod Types', () => { success: true, data: value, }) - } else { + } + else { expect(schema.safeParse(value).error?.issues[0]?.message).toEqual( errorMessage, ) diff --git a/packages/zod/tests/openapi.test-d.ts b/packages/zod/tests/openapi.test-d.ts index eb5e149a..580169be 100644 --- a/packages/zod/tests/openapi.test-d.ts +++ b/packages/zod/tests/openapi.test-d.ts @@ -12,19 +12,19 @@ describe('openapi function', () => { }) oz.openapi(schema, { - // @ts-expect-error + // @ts-expect-error - invalid example examples: [{ a: '23' }], }) oz.openapi(schema, { - // @ts-expect-error + // @ts-expect-error - invalid example examples: [12343], }) }) it('strict on input & output', () => { const schema = z.object({ - name: z.string().transform((val) => val.length), + name: z.string().transform(val => val.length), }) oz.openapi(schema, { diff --git a/packages/zod/tests/openapi.test.ts b/packages/zod/tests/openapi.test.ts index f1b998cd..4393a675 100644 --- a/packages/zod/tests/openapi.test.ts +++ b/packages/zod/tests/openapi.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { type ZodEffects, z } from 'zod' +import { z, type ZodEffects } from 'zod' import { getCustomJSONSchema, openapi } from '../src' describe('openapi function', () => { diff --git a/playgrounds/contract-openapi/.vscode/settings.json b/playgrounds/contract-openapi/.vscode/settings.json index 17293413..ad92582b 100644 --- a/playgrounds/contract-openapi/.vscode/settings.json +++ b/playgrounds/contract-openapi/.vscode/settings.json @@ -1,3 +1,3 @@ { - "editor.formatOnSave": true, -} \ No newline at end of file + "editor.formatOnSave": true +} diff --git a/playgrounds/contract-openapi/package.json b/playgrounds/contract-openapi/package.json index 1472b6c7..418d26b0 100644 --- a/playgrounds/contract-openapi/package.json +++ b/playgrounds/contract-openapi/package.json @@ -1,20 +1,15 @@ { "name": "orpc-contract-openapi-playground", + "type": "module", "version": "0.0.0", "description": "oRPC Contract OpenAPI Playground", - "type": "module", + "author": "", + "license": "ISC", + "keywords": ["orpc", "contract", "openapi", "playground", "unnoq"], "scripts": { "dev": "tsx --watch src/main.ts", "start": "tsx src/main.ts" }, - "keywords": ["orpc", "contract", "openapi", "playground", "unnoq"], - "author": "", - "license": "ISC", - "devDependencies": { - "@types/node": "^22.9.0", - "tsx": "^4.19.2", - "typescript": "^5.6.3" - }, "dependencies": { "@orpc/client": "latest", "@orpc/contract": "latest", @@ -24,5 +19,10 @@ "@orpc/zod": "latest", "@whatwg-node/server": "^0.9.55", "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^22.9.0", + "tsx": "^4.19.2", + "typescript": "^5.6.3" } } diff --git a/playgrounds/contract-openapi/src/main.ts b/playgrounds/contract-openapi/src/main.ts index 0f53f6c5..aaf1cddc 100644 --- a/playgrounds/contract-openapi/src/main.ts +++ b/playgrounds/contract-openapi/src/main.ts @@ -8,7 +8,7 @@ import { router } from './router' const orpcHandler = createFetchHandler({ router, hooks(context, meta) { - meta.onError((e) => console.error(e)) + meta.onError(e => console.error(e)) }, }) @@ -107,6 +107,6 @@ The example OpenAPI Playground for ORPC. ) server.listen(2026, () => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log('Playground is available at http://localhost:2026') }) diff --git a/playgrounds/contract-openapi/src/orpc.ts b/playgrounds/contract-openapi/src/orpc.ts index 8838bc5a..3f856727 100644 --- a/playgrounds/contract-openapi/src/orpc.ts +++ b/playgrounds/contract-openapi/src/orpc.ts @@ -1,15 +1,15 @@ -import { os, ORPCError } from '@orpc/server' import type { z } from 'zod' -import { contract } from './contract' import type { UserSchema } from './schemas/user' +import { ORPCError, os } from '@orpc/server' +import { contract } from './contract' -export type ORPCContext = { user?: z.infer; db: any } +export type ORPCContext = { user?: z.infer, db: any } const base = os.context().use((input, context, meta) => { const start = Date.now() meta.onFinish(() => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log(`[${meta.path.join('/')}] ${Date.now() - start}ms`) }) }) diff --git a/playgrounds/contract-openapi/src/playground-client.ts b/playgrounds/contract-openapi/src/playground-client.ts index 33ad36e3..ca434cfc 100644 --- a/playgrounds/contract-openapi/src/playground-client.ts +++ b/playgrounds/contract-openapi/src/playground-client.ts @@ -2,8 +2,8 @@ * This file is where you can play with type of oRPC Client. */ -import { createORPCClient } from '@orpc/client' import type { contract } from './contract' +import { createORPCClient } from '@orpc/client' const orpc = createORPCClient({ baseURL: 'http://localhost:2026/api', diff --git a/playgrounds/contract-openapi/src/playground-react.ts b/playgrounds/contract-openapi/src/playground-react.ts index 8a8460c3..28f7ee0d 100644 --- a/playgrounds/contract-openapi/src/playground-react.ts +++ b/playgrounds/contract-openapi/src/playground-react.ts @@ -2,21 +2,21 @@ * This file is where you can play with type of oRPC React. */ -import { createORPCReact } from '@orpc/react' import type { contract } from './contract' +import { createORPCReact } from '@orpc/react' const { orpc } = createORPCReact() const listQuery = orpc.planet.list.useInfiniteQuery({ input: {}, - getNextPageParam: (lastPage) => (lastPage.at(-1)?.id ?? -1) + 1, + getNextPageParam: lastPage => (lastPage.at(-1)?.id ?? -1) + 1, }) const utils = orpc.useUtils() utils.planet.invalidate() -const queries = orpc.useQueries((o) => [ +const queries = orpc.useQueries(o => [ o.planet.find({ id: 1 }), o.planet.list({}), ]) diff --git a/playgrounds/contract-openapi/src/router/planet.ts b/playgrounds/contract-openapi/src/router/planet.ts index dccee119..4c3cea9b 100644 --- a/playgrounds/contract-openapi/src/router/planet.ts +++ b/playgrounds/contract-openapi/src/router/planet.ts @@ -28,7 +28,7 @@ export const createPlanet = authed.planet.create.handler( export const findPlanet = pub.planet.find.handler( async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -43,7 +43,7 @@ export const findPlanet = pub.planet.find.handler( export const updatePlanet = authed.planet.update.handler( async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -62,7 +62,7 @@ export const updatePlanet = authed.planet.update.handler( export const updatePlanetImage = authed.planet.updateImage.handler( async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -79,7 +79,7 @@ export const updatePlanetImage = authed.planet.updateImage.handler( export const deletePlanet = authed.planet.delete.handler( async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ diff --git a/playgrounds/contract-openapi/tsconfig.json b/playgrounds/contract-openapi/tsconfig.json index 06f9e8a9..4a4a647a 100644 --- a/playgrounds/contract-openapi/tsconfig.json +++ b/playgrounds/contract-openapi/tsconfig.json @@ -2,22 +2,22 @@ "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "target": "ES2022", + "jsx": "react-jsx", "lib": ["ES2022"], - "useDefineForClassFields": true, "moduleDetection": "force", + "useDefineForClassFields": true, "module": "ES2022", - "types": ["node"], "moduleResolution": "bundler", "resolveJsonModule": true, - "jsx": "react-jsx", + "types": ["node"], - "noEmit": true, "strict": true, - "outDir": "${configDir}/dist", + "noImplicitOverride": true, "noUncheckedIndexedAccess": true, + "noEmit": true, + "outDir": "${configDir}/dist", "noUncheckedSideEffectImports": true, - "noImplicitOverride": true, "esModuleInterop": true, "isolatedModules": true, "skipLibCheck": true diff --git a/playgrounds/expressjs/.vscode/settings.json b/playgrounds/expressjs/.vscode/settings.json index 17293413..ad92582b 100644 --- a/playgrounds/expressjs/.vscode/settings.json +++ b/playgrounds/expressjs/.vscode/settings.json @@ -1,3 +1,3 @@ { - "editor.formatOnSave": true, -} \ No newline at end of file + "editor.formatOnSave": true +} diff --git a/playgrounds/expressjs/package.json b/playgrounds/expressjs/package.json index b222e4d9..4906771c 100644 --- a/playgrounds/expressjs/package.json +++ b/playgrounds/expressjs/package.json @@ -1,21 +1,15 @@ { "name": "orpc-expresjs-playground", + "type": "module", "version": "0.0.0", "description": "oRPC ExpressJS Playground", - "type": "module", + "author": "", + "license": "ISC", + "keywords": ["orpc", "expressjs", "playground", "unnoq"], "scripts": { "dev": "tsx --watch src/main.ts", "start": "tsx src/main.ts" }, - "keywords": ["orpc", "expressjs", "playground", "unnoq"], - "author": "", - "license": "ISC", - "devDependencies": { - "@types/express": "^5.0.0", - "@types/node": "^22.9.0", - "tsx": "^4.19.2", - "typescript": "^5.6.3" - }, "dependencies": { "@orpc/client": "latest", "@orpc/openapi": "latest", @@ -25,5 +19,11 @@ "@whatwg-node/server": "^0.9.55", "express": "^4.21.1", "zod": "^3.23.8" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/node": "^22.9.0", + "tsx": "^4.19.2", + "typescript": "^5.6.3" } } diff --git a/playgrounds/expressjs/src/main.ts b/playgrounds/expressjs/src/main.ts index 1d6cbea2..40dc77d8 100644 --- a/playgrounds/expressjs/src/main.ts +++ b/playgrounds/expressjs/src/main.ts @@ -9,7 +9,7 @@ const app = express() const orpcHandler = createFetchHandler({ router, hooks(context, meta) { - meta.onError((e) => console.error(e)) + meta.onError(e => console.error(e)) }, }) @@ -97,6 +97,6 @@ app.get('/', (req, res) => { }) app.listen(2026, () => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log('Playground is available at http://localhost:2026') }) diff --git a/playgrounds/expressjs/src/orpc.ts b/playgrounds/expressjs/src/orpc.ts index 6f5c1ab5..b9aafbd3 100644 --- a/playgrounds/expressjs/src/orpc.ts +++ b/playgrounds/expressjs/src/orpc.ts @@ -1,14 +1,14 @@ -import { os, ORPCError } from '@orpc/server' import type { z } from 'zod' import type { UserSchema } from './schemas/user' +import { ORPCError, os } from '@orpc/server' -export type ORPCContext = { user?: z.infer; db: any } +export type ORPCContext = { user?: z.infer, db: any } export const pub = os.context().use((input, context, meta) => { const start = Date.now() meta.onFinish(() => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log(`[${meta.path.join('/')}] ${Date.now() - start}ms`) }) }) diff --git a/playgrounds/expressjs/src/playground-client.ts b/playgrounds/expressjs/src/playground-client.ts index 82ad64c9..91bf9b3e 100644 --- a/playgrounds/expressjs/src/playground-client.ts +++ b/playgrounds/expressjs/src/playground-client.ts @@ -2,8 +2,8 @@ * This file is where you can play with type of oRPC Client. */ -import { createORPCClient } from '@orpc/client' import type { router } from './router' +import { createORPCClient } from '@orpc/client' const orpc = createORPCClient({ baseURL: 'http://localhost:2026/api', diff --git a/playgrounds/expressjs/src/playground-react.ts b/playgrounds/expressjs/src/playground-react.ts index 45ac6baa..33e0cb7b 100644 --- a/playgrounds/expressjs/src/playground-react.ts +++ b/playgrounds/expressjs/src/playground-react.ts @@ -2,21 +2,21 @@ * This file is where you can play with type of oRPC React. */ -import { createORPCReact } from '@orpc/react' import type { router } from './router' +import { createORPCReact } from '@orpc/react' const { orpc } = createORPCReact() const listQuery = orpc.planet.list.useInfiniteQuery({ input: {}, - getNextPageParam: (lastPage) => (lastPage.at(-1)?.id ?? -1) + 1, + getNextPageParam: lastPage => (lastPage.at(-1)?.id ?? -1) + 1, }) const utils = orpc.useUtils() utils.planet.invalidate() -const queries = orpc.useQueries((o) => [ +const queries = orpc.useQueries(o => [ o.planet.find({ id: 1 }), o.planet.list({}), ]) diff --git a/playgrounds/expressjs/src/router/planet.ts b/playgrounds/expressjs/src/router/planet.ts index a9d7d270..c3a9679f 100644 --- a/playgrounds/expressjs/src/router/planet.ts +++ b/playgrounds/expressjs/src/router/planet.ts @@ -63,7 +63,7 @@ export const findPlanet = pub ) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -84,7 +84,7 @@ export const updatePlanet = authed .input(UpdatePlanetSchema) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -114,7 +114,7 @@ export const updatePlanetImage = authed ) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -141,7 +141,7 @@ export const deletePlanet = authed }), ) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ diff --git a/playgrounds/expressjs/tsconfig.json b/playgrounds/expressjs/tsconfig.json index 06f9e8a9..4a4a647a 100644 --- a/playgrounds/expressjs/tsconfig.json +++ b/playgrounds/expressjs/tsconfig.json @@ -2,22 +2,22 @@ "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "target": "ES2022", + "jsx": "react-jsx", "lib": ["ES2022"], - "useDefineForClassFields": true, "moduleDetection": "force", + "useDefineForClassFields": true, "module": "ES2022", - "types": ["node"], "moduleResolution": "bundler", "resolveJsonModule": true, - "jsx": "react-jsx", + "types": ["node"], - "noEmit": true, "strict": true, - "outDir": "${configDir}/dist", + "noImplicitOverride": true, "noUncheckedIndexedAccess": true, + "noEmit": true, + "outDir": "${configDir}/dist", "noUncheckedSideEffectImports": true, - "noImplicitOverride": true, "esModuleInterop": true, "isolatedModules": true, "skipLibCheck": true diff --git a/playgrounds/nextjs/.vscode/settings.json b/playgrounds/nextjs/.vscode/settings.json index 17293413..ad92582b 100644 --- a/playgrounds/nextjs/.vscode/settings.json +++ b/playgrounds/nextjs/.vscode/settings.json @@ -1,3 +1,3 @@ { - "editor.formatOnSave": true, -} \ No newline at end of file + "editor.formatOnSave": true +} diff --git a/playgrounds/nextjs/package.json b/playgrounds/nextjs/package.json index 43466c35..ef531106 100644 --- a/playgrounds/nextjs/package.json +++ b/playgrounds/nextjs/package.json @@ -1,8 +1,8 @@ { "name": "orpc-nextjs-playground", "version": "0.1.0", - "description": "oRPC NextJS Playground", "private": true, + "description": "oRPC NextJS Playground", "keywords": ["orpc", "nextjs", "playground", "unnoq"], "scripts": { "dev": "next dev --port 2026", diff --git a/playgrounds/nextjs/src/app/error.tsx b/playgrounds/nextjs/src/app/error.tsx index b2b429b0..098bc38a 100644 --- a/playgrounds/nextjs/src/app/error.tsx +++ b/playgrounds/nextjs/src/app/error.tsx @@ -3,7 +3,10 @@ export default function ErrorPage({ error }: { error: Error }) { return (

-

Error: {error.message}

+

+ Error: + {error.message} +

) } diff --git a/playgrounds/nextjs/src/app/page.tsx b/playgrounds/nextjs/src/app/page.tsx index 79073cb4..2401f9b0 100644 --- a/playgrounds/nextjs/src/app/page.tsx +++ b/playgrounds/nextjs/src/app/page.tsx @@ -8,7 +8,11 @@ export default function Home() {

ORPC Playground

- You can visit the Scalar API Reference page. + You can visit the + {' '} + Scalar API Reference + {' '} + page.

@@ -21,10 +25,10 @@ export default function Home() { } function SSRListPlanets() { - const { data, refetch, fetchNextPage, hasNextPage } = - orpc.planet.list.useSuspenseInfiniteQuery({ + const { data, refetch, fetchNextPage, hasNextPage } + = orpc.planet.list.useSuspenseInfiniteQuery({ input: {}, - getNextPageParam: (lastPage) => (lastPage.at(-1)?.id ?? -1) + 1, + getNextPageParam: lastPage => (lastPage.at(-1)?.id ?? -1) + 1, }) return ( @@ -39,7 +43,8 @@ function SSRListPlanets() { {data.pages.flatMap((page, i) => - page.map((planet) => ( + page.map(planet => ( + // eslint-disable-next-line react/no-array-index-key {planet.id} {planet.name} @@ -90,8 +95,8 @@ function AddPlanet() { const form = new FormData(e.target as HTMLFormElement) const name = form.get('name') as string - const description = - (form.get('description') as string | null) ?? undefined + const description + = (form.get('description') as string | null) ?? undefined const image = form.get('image') as File mutate({ @@ -121,7 +126,8 @@ function AddPlanet() { function ServerActionsTest() { return (
- Server Actions Test:{' '} + Server Actions Test: + {' '} diff --git a/playgrounds/nextjs/src/app/providers.tsx b/playgrounds/nextjs/src/app/providers.tsx index c2ad16ff..dd79d637 100644 --- a/playgrounds/nextjs/src/app/providers.tsx +++ b/playgrounds/nextjs/src/app/providers.tsx @@ -1,6 +1,6 @@ 'use client' -import { ORPCContext, orpcClient } from '@/lib/orpc' +import { orpcClient, ORPCContext } from '@/lib/orpc' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { useState } from 'react' @@ -9,7 +9,7 @@ export function Providers(props: { children: React.ReactNode }) { return ( - + ({ client: orpcClient, queryClient }), [queryClient])}> {props.children} diff --git a/playgrounds/nextjs/src/app/scalar/route.ts b/playgrounds/nextjs/src/app/scalar/route.ts index 63eb7a43..38e067cc 100644 --- a/playgrounds/nextjs/src/app/scalar/route.ts +++ b/playgrounds/nextjs/src/app/scalar/route.ts @@ -1,4 +1,4 @@ -export const GET = () => { +export function GET() { return new Response( ` diff --git a/playgrounds/nextjs/src/app/spec/route.ts b/playgrounds/nextjs/src/app/spec/route.ts index 408677bf..097f7201 100644 --- a/playgrounds/nextjs/src/app/spec/route.ts +++ b/playgrounds/nextjs/src/app/spec/route.ts @@ -1,7 +1,7 @@ import { router } from '@/router' import { generateOpenAPI } from '@orpc/openapi' -export const GET = (request: Request) => { +export function GET(request: Request) { const spec = generateOpenAPI({ router, info: { diff --git a/playgrounds/nextjs/src/lib/orpc.ts b/playgrounds/nextjs/src/lib/orpc.ts index 77c85ef1..9207e182 100644 --- a/playgrounds/nextjs/src/lib/orpc.ts +++ b/playgrounds/nextjs/src/lib/orpc.ts @@ -1,7 +1,7 @@ +import type { router } from '@/router' import { createORPCClient } from '@orpc/client' -import { createORPCReact } from '@orpc/react' -import type { router } from '@/router' +import { createORPCReact } from '@orpc/react' export const orpcClient = createORPCClient({ baseURL: 'http://localhost:3000/api', diff --git a/playgrounds/nextjs/src/orpc.ts b/playgrounds/nextjs/src/orpc.ts index 6f5c1ab5..b9aafbd3 100644 --- a/playgrounds/nextjs/src/orpc.ts +++ b/playgrounds/nextjs/src/orpc.ts @@ -1,14 +1,14 @@ -import { os, ORPCError } from '@orpc/server' import type { z } from 'zod' import type { UserSchema } from './schemas/user' +import { ORPCError, os } from '@orpc/server' -export type ORPCContext = { user?: z.infer; db: any } +export type ORPCContext = { user?: z.infer, db: any } export const pub = os.context().use((input, context, meta) => { const start = Date.now() meta.onFinish(() => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log(`[${meta.path.join('/')}] ${Date.now() - start}ms`) }) }) diff --git a/playgrounds/nextjs/src/playground-react.ts b/playgrounds/nextjs/src/playground-react.ts index f5231bf1..51b0c7a8 100644 --- a/playgrounds/nextjs/src/playground-react.ts +++ b/playgrounds/nextjs/src/playground-react.ts @@ -6,14 +6,14 @@ import { orpc } from '@/lib/orpc' const listQuery = orpc.planet.list.useInfiniteQuery({ input: {}, - getNextPageParam: (lastPage) => (lastPage.at(-1)?.id ?? -1) + 1, + getNextPageParam: lastPage => (lastPage.at(-1)?.id ?? -1) + 1, }) const utils = orpc.useUtils() utils.planet.invalidate() -const queries = orpc.useQueries((o) => [ +const queries = orpc.useQueries(o => [ o.planet.find({ id: 1 }), o.planet.list({}), ]) diff --git a/playgrounds/nextjs/src/router/planet.ts b/playgrounds/nextjs/src/router/planet.ts index a9d7d270..c3a9679f 100644 --- a/playgrounds/nextjs/src/router/planet.ts +++ b/playgrounds/nextjs/src/router/planet.ts @@ -63,7 +63,7 @@ export const findPlanet = pub ) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -84,7 +84,7 @@ export const updatePlanet = authed .input(UpdatePlanetSchema) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -114,7 +114,7 @@ export const updatePlanetImage = authed ) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -141,7 +141,7 @@ export const deletePlanet = authed }), ) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ diff --git a/playgrounds/nextjs/tsconfig.json b/playgrounds/nextjs/tsconfig.json index c1334095..afe446ba 100644 --- a/playgrounds/nextjs/tsconfig.json +++ b/playgrounds/nextjs/tsconfig.json @@ -1,26 +1,26 @@ { "compilerOptions": { + "incremental": true, "target": "ES2017", + "jsx": "preserve", "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "moduleResolution": "bundler", + "paths": { + "@/*": ["./src/*"] + }, + "resolveJsonModule": true, "allowJs": true, - "skipLibCheck": true, "strict": true, "noEmit": true, "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", - "incremental": true, + "skipLibCheck": true, "plugins": [ { "name": "next" } - ], - "paths": { - "@/*": ["./src/*"] - } + ] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] diff --git a/playgrounds/openapi/.vscode/settings.json b/playgrounds/openapi/.vscode/settings.json index 17293413..ad92582b 100644 --- a/playgrounds/openapi/.vscode/settings.json +++ b/playgrounds/openapi/.vscode/settings.json @@ -1,3 +1,3 @@ { - "editor.formatOnSave": true, -} \ No newline at end of file + "editor.formatOnSave": true +} diff --git a/playgrounds/openapi/package.json b/playgrounds/openapi/package.json index ad1c228e..11cff35b 100644 --- a/playgrounds/openapi/package.json +++ b/playgrounds/openapi/package.json @@ -1,20 +1,15 @@ { "name": "orpc-openapi-playground", + "type": "module", "version": "0.0.0", "description": "oRPC OpenAPI Playground", - "type": "module", + "author": "", + "license": "ISC", + "keywords": ["orpc", "openapi", "playground", "unnoq"], "scripts": { "dev": "tsx --watch src/main.ts", "start": "tsx src/main.ts" }, - "keywords": ["orpc", "openapi", "playground", "unnoq"], - "author": "", - "license": "ISC", - "devDependencies": { - "@types/node": "^22.9.0", - "tsx": "^4.19.2", - "typescript": "^5.6.3" - }, "dependencies": { "@orpc/client": "latest", "@orpc/openapi": "latest", @@ -23,5 +18,10 @@ "@orpc/zod": "latest", "@whatwg-node/server": "^0.9.55", "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^22.9.0", + "tsx": "^4.19.2", + "typescript": "^5.6.3" } } diff --git a/playgrounds/openapi/src/main.ts b/playgrounds/openapi/src/main.ts index 0218caee..7878fc19 100644 --- a/playgrounds/openapi/src/main.ts +++ b/playgrounds/openapi/src/main.ts @@ -7,7 +7,7 @@ import { router } from './router' const orpcHandler = createFetchHandler({ router, hooks(context, meta) { - meta.onError((e) => console.error(e)) + meta.onError(e => console.error(e)) }, }) @@ -106,6 +106,6 @@ The example OpenAPI Playground for ORPC. ) server.listen(2026, () => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log('Playground is available at http://localhost:2026') }) diff --git a/playgrounds/openapi/src/orpc.ts b/playgrounds/openapi/src/orpc.ts index 6f5c1ab5..b9aafbd3 100644 --- a/playgrounds/openapi/src/orpc.ts +++ b/playgrounds/openapi/src/orpc.ts @@ -1,14 +1,14 @@ -import { os, ORPCError } from '@orpc/server' import type { z } from 'zod' import type { UserSchema } from './schemas/user' +import { ORPCError, os } from '@orpc/server' -export type ORPCContext = { user?: z.infer; db: any } +export type ORPCContext = { user?: z.infer, db: any } export const pub = os.context().use((input, context, meta) => { const start = Date.now() meta.onFinish(() => { - // biome-ignore lint/suspicious/noConsole: + // eslint-disable-next-line no-console console.log(`[${meta.path.join('/')}] ${Date.now() - start}ms`) }) }) diff --git a/playgrounds/openapi/src/playground-client.ts b/playgrounds/openapi/src/playground-client.ts index 82ad64c9..91bf9b3e 100644 --- a/playgrounds/openapi/src/playground-client.ts +++ b/playgrounds/openapi/src/playground-client.ts @@ -2,8 +2,8 @@ * This file is where you can play with type of oRPC Client. */ -import { createORPCClient } from '@orpc/client' import type { router } from './router' +import { createORPCClient } from '@orpc/client' const orpc = createORPCClient({ baseURL: 'http://localhost:2026/api', diff --git a/playgrounds/openapi/src/playground-react.ts b/playgrounds/openapi/src/playground-react.ts index 45ac6baa..33e0cb7b 100644 --- a/playgrounds/openapi/src/playground-react.ts +++ b/playgrounds/openapi/src/playground-react.ts @@ -2,21 +2,21 @@ * This file is where you can play with type of oRPC React. */ -import { createORPCReact } from '@orpc/react' import type { router } from './router' +import { createORPCReact } from '@orpc/react' const { orpc } = createORPCReact() const listQuery = orpc.planet.list.useInfiniteQuery({ input: {}, - getNextPageParam: (lastPage) => (lastPage.at(-1)?.id ?? -1) + 1, + getNextPageParam: lastPage => (lastPage.at(-1)?.id ?? -1) + 1, }) const utils = orpc.useUtils() utils.planet.invalidate() -const queries = orpc.useQueries((o) => [ +const queries = orpc.useQueries(o => [ o.planet.find({ id: 1 }), o.planet.list({}), ]) diff --git a/playgrounds/openapi/src/router/planet.ts b/playgrounds/openapi/src/router/planet.ts index a9d7d270..c3a9679f 100644 --- a/playgrounds/openapi/src/router/planet.ts +++ b/playgrounds/openapi/src/router/planet.ts @@ -63,7 +63,7 @@ export const findPlanet = pub ) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -84,7 +84,7 @@ export const updatePlanet = authed .input(UpdatePlanetSchema) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -114,7 +114,7 @@ export const updatePlanetImage = authed ) .output(PlanetSchema) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ @@ -141,7 +141,7 @@ export const deletePlanet = authed }), ) .handler(async (input, context, meta) => { - const planet = planets.find((planet) => planet.id === input.id) + const planet = planets.find(planet => planet.id === input.id) if (!planet) { throw new ORPCError({ diff --git a/playgrounds/openapi/tsconfig.json b/playgrounds/openapi/tsconfig.json index 06f9e8a9..4a4a647a 100644 --- a/playgrounds/openapi/tsconfig.json +++ b/playgrounds/openapi/tsconfig.json @@ -2,22 +2,22 @@ "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "target": "ES2022", + "jsx": "react-jsx", "lib": ["ES2022"], - "useDefineForClassFields": true, "moduleDetection": "force", + "useDefineForClassFields": true, "module": "ES2022", - "types": ["node"], "moduleResolution": "bundler", "resolveJsonModule": true, - "jsx": "react-jsx", + "types": ["node"], - "noEmit": true, "strict": true, - "outDir": "${configDir}/dist", + "noImplicitOverride": true, "noUncheckedIndexedAccess": true, + "noEmit": true, + "outDir": "${configDir}/dist", "noUncheckedSideEffectImports": true, - "noImplicitOverride": true, "esModuleInterop": true, "isolatedModules": true, "skipLibCheck": true diff --git a/tsconfig.base.json b/tsconfig.base.json index 8c30065d..091f7f66 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -2,21 +2,21 @@ "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "target": "ES2022", + "jsx": "react-jsx", "lib": ["ES2022"], - "useDefineForClassFields": true, "moduleDetection": "force", + "useDefineForClassFields": true, "module": "ES2022", "moduleResolution": "bundler", "resolveJsonModule": true, - "jsx": "react-jsx", - "noEmit": true, "strict": true, - "outDir": "${configDir}/dist", + "noImplicitOverride": true, "noUncheckedIndexedAccess": true, + "noEmit": true, + "outDir": "${configDir}/dist", "noUncheckedSideEffectImports": true, - "noImplicitOverride": true, "esModuleInterop": true, "isolatedModules": true, "skipLibCheck": true diff --git a/tsconfig.lib.json b/tsconfig.lib.json index 2052bdf3..c0f79ec0 100644 --- a/tsconfig.lib.json +++ b/tsconfig.lib.json @@ -3,9 +3,9 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "composite": true, - "noEmit": false, "declaration": true, "declarationMap": true, - "emitDeclarationOnly": true + "emitDeclarationOnly": true, + "noEmit": false } }