Skip to content

Commit

Permalink
remove @orpc/transformer
Browse files Browse the repository at this point in the history
  • Loading branch information
unnoq committed Dec 26, 2024
1 parent 131068a commit d03672b
Show file tree
Hide file tree
Showing 37 changed files with 172 additions and 2,988 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"simple-git-hooks": "^2.11.1",
"tsup": "^8.3.0",
"typescript": "5.7.2",
"vitest": "^2.1.3"
"vitest": "^2.1.8"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
Expand Down
7 changes: 3 additions & 4 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@
"type:check": "tsc -b"
},
"peerDependencies": {
"@orpc/contract": "workspace:*",
"@orpc/server": "workspace:*"
"@orpc/contract": "workspace:*"
},
"dependencies": {
"@orpc/shared": "workspace:*",
"@orpc/transformer": "workspace:*"
"@orpc/server": "workspace:*",
"@orpc/shared": "workspace:*"
},
"devDependencies": {
"@orpc/openapi": "workspace:*",
Expand Down
23 changes: 11 additions & 12 deletions packages/client/src/procedure-fetch-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { ORPCDeserializer, ORPCSerializer } from '@orpc/transformer'
import { ORPCPayloadCodec } from '@orpc/server/fetch'
import { createProcedureFetchClient } from './procedure-fetch-client'

vi.mock('@orpc/transformer', () => ({
ORPCSerializer: vi.fn().mockReturnValue({ serialize: vi.fn() }),
ORPCDeserializer: vi.fn().mockReturnValue({ deserialize: vi.fn() }),
vi.mock('@orpc/server/fetch', () => ({
ORPCPayloadCodec: vi.fn().mockReturnValue({ encode: vi.fn(), decode: vi.fn() }),
}))

beforeEach(() => {
vi.clearAllMocks()
})

describe('procedure fetch client', () => {
const serialize = (ORPCSerializer as any)().serialize
const deserialize = (ORPCDeserializer as any)().deserialize
const encode = (ORPCPayloadCodec as any)().encode
const decode = (ORPCPayloadCodec as any)().decode
const response = new Response('output')
const headers = new Headers({ 'Content-Type': 'application/json' })

const fakeFetch = vi.fn()
fakeFetch.mockReturnValue(response)
serialize.mockReturnValue({ body: 'transformed_input', headers })
deserialize.mockReturnValue('transformed_output')
encode.mockReturnValue({ body: 'transformed_input', headers })
decode.mockReturnValue('transformed_output')

it('works', async () => {
const client = createProcedureFetchClient({
Expand All @@ -32,8 +31,8 @@ describe('procedure fetch client', () => {

expect(output).toBe('transformed_output')

expect(serialize).toBeCalledTimes(1)
expect(serialize).toBeCalledWith('input')
expect(encode).toBeCalledTimes(1)
expect(encode).toBeCalledWith('input')

expect(fakeFetch).toBeCalledTimes(1)
expect(fakeFetch).toBeCalledWith('http://localhost:3000/orpc/ping', {
Expand All @@ -42,8 +41,8 @@ describe('procedure fetch client', () => {
headers: expect.any(Headers),
})

expect(deserialize).toBeCalledTimes(1)
expect(deserialize).toBeCalledWith(response)
expect(decode).toBeCalledTimes(1)
expect(decode).toBeCalledWith(response)
})

it.each([
Expand Down
23 changes: 9 additions & 14 deletions packages/client/src/procedure-fetch-client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { ProcedureClient } from '@orpc/server'
import type { Promisable } from '@orpc/shared'
import { ORPCPayloadCodec } from '@orpc/server/fetch'
import { ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE, trim } from '@orpc/shared'
import { ORPCError } from '@orpc/shared/error'
import { ORPCDeserializer, ORPCSerializer } from '@orpc/transformer'

export interface CreateProcedureClientOptions {
/**
Expand All @@ -28,8 +28,7 @@ export interface CreateProcedureClientOptions {
path: string[]
}

const serializer = new ORPCSerializer()
const deserializer = new ORPCDeserializer()
const payloadCodec = new ORPCPayloadCodec()

export function createProcedureFetchClient<TInput, TOutput>(
options: CreateProcedureClientOptions,
Expand All @@ -38,32 +37,28 @@ export function createProcedureFetchClient<TInput, TOutput>(
const fetchClient = options.fetch ?? fetch
const url = `${trim(options.baseURL, '/')}/${options.path.map(encodeURIComponent).join('/')}`

const headers = new Headers({
[ORPC_HANDLER_HEADER]: ORPC_HANDLER_VALUE,
})
const encoded = payloadCodec.encode(input)

const headers = new Headers(encoded.headers)

headers.append(ORPC_HANDLER_HEADER, ORPC_HANDLER_VALUE)

let customHeaders = await options.headers?.(input)
customHeaders = customHeaders instanceof Headers ? customHeaders : new Headers(customHeaders)
for (const [key, value] of customHeaders.entries()) {
headers.append(key, value)
}

const serialized = serializer.serialize(input)

for (const [key, value] of serialized.headers.entries()) {
headers.append(key, value)
}

const response = await fetchClient(url, {
method: 'POST',
headers,
body: serialized.body,
body: encoded.body,
signal: callerOptions?.signal,
})

const json = await (async () => {
try {
return await deserializer.deserialize(response)
return await payloadCodec.decode(response)
}
catch (e) {
throw new ORPCError({
Expand Down
3 changes: 1 addition & 2 deletions packages/client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
{ "path": "../contract" },
{ "path": "../openapi" },
{ "path": "../server" },
{ "path": "../shared" },
{ "path": "../transformer" }
{ "path": "../shared" }
],
"include": ["src"],
"exclude": [
Expand Down
1 change: 1 addition & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
},
"dependencies": {
"@orpc/contract": "workspace:*",
"@orpc/openapi": "workspace:*",
"@orpc/shared": "workspace:*"
},
"devDependencies": {
Expand Down
13 changes: 12 additions & 1 deletion packages/next/src/action-form.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { os } from '@orpc/server'
import { ZodCoercer } from '@orpc/zod'
import { z } from 'zod'
import { createFormAction } from './action-form'

Expand All @@ -9,7 +10,14 @@ describe('createFormAction', () => {

it('should accept form data and auto-convert types', async () => {
const onSuccess = vi.fn()
const formAction = createFormAction({ procedure, path: ['name'], onSuccess })
const formAction = createFormAction({
procedure,
path: ['name'],
onSuccess,
schemaCoercers: [
new ZodCoercer(),
],
})

const form = new FormData()
form.append('big', '19992')
Expand Down Expand Up @@ -39,6 +47,9 @@ describe('createFormAction', () => {
path: ['name'],
onSuccess,
onError,
schemaCoercers: [
new ZodCoercer(),
],
})

const form = new FormData()
Expand Down
23 changes: 13 additions & 10 deletions packages/next/src/action-form.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
import type { Schema, SchemaInput } from '@orpc/contract'
import type { Context, CreateProcedureClientOptions } from '@orpc/server'
import { CompositeSchemaCoercer, OpenAPIPayloadCodec, type PublicOpenAPIPayloadCodec, type SchemaCoercer } from '@orpc/openapi/fetch'
import { createProcedureClient, ORPCError, unlazy } from '@orpc/server'
import { OpenAPIDeserializer } from '@orpc/transformer'
import { forbidden, notFound, unauthorized } from 'next/navigation'

export type FormAction = (input: FormData) => Promise<void>

export type CreateFormActionOptions = {
schemaCoercers?: SchemaCoercer[]
payloadCodec?: PublicOpenAPIPayloadCodec
}

export function createFormAction<
TContext extends Context,
TInputSchema extends Schema,
TOutputSchema extends Schema,
TFuncOutput extends SchemaInput<TOutputSchema>,
>(opt: CreateProcedureClientOptions<TContext, TInputSchema, TOutputSchema, TFuncOutput>): FormAction {
>(opt: CreateProcedureClientOptions<TContext, TInputSchema, TOutputSchema, TFuncOutput> & CreateFormActionOptions): FormAction {
const caller = createProcedureClient(opt)

const formAction = async (input: FormData): Promise<void> => {
try {
const { default: procedure } = await unlazy(opt.procedure)
const codec = opt.payloadCodec ?? new OpenAPIPayloadCodec()
const coercer = new CompositeSchemaCoercer(opt.schemaCoercers ?? [])

const inputSchema = procedure['~orpc'].contract['~orpc'].InputSchema

const deserializer = new OpenAPIDeserializer({
schema: inputSchema?.['~standard'].vendor === 'zod' ? inputSchema as any : undefined,
})
const { default: procedure } = await unlazy(opt.procedure)

const deserializedInput = deserializer.deserializeAsFormData(input)
const decodedInput = await codec.decode(input)
const coercedInput = coercer.coerce(procedure['~orpc'].contract['~orpc'].InputSchema, decodedInput)

await caller(deserializedInput as any)
await caller(coercedInput as any)
}
catch (e) {
if (e instanceof ORPCError) {
Expand Down
1 change: 1 addition & 0 deletions packages/next/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
},
"references": [
{ "path": "../contract" },
{ "path": "../openapi" },
{ "path": "../server" },
{ "path": "../shared" }
],
Expand Down
2 changes: 0 additions & 2 deletions packages/openapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@
"@orpc/contract": "workspace:*",
"@orpc/server": "workspace:*",
"@orpc/shared": "workspace:*",
"@orpc/transformer": "workspace:*",
"@orpc/zod": "workspace:*",
"@standard-schema/spec": "1.0.0-beta.4",
"content-disposition": "^0.5.4",
"escape-string-regexp": "^5.0.0",
Expand Down
7 changes: 6 additions & 1 deletion packages/openapi/src/fetch/openapi-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { describe, expect, it, vi } from 'vitest'
import { OpenAPIHandler } from './openapi-handler'

vi.mock('@orpc/server', async original => ({
...(await original()),
...await original(),
createProcedureClient: vi.fn(() => vi.fn()),
}))

Expand All @@ -19,6 +19,10 @@ const hono = [
['PatternRouter', new PatternRouter<any>()],
] as const

beforeEach(() => {
vi.clearAllMocks()
})

describe.each(hono)('openAPIHandler: %s', (_, hono) => {
const ping = new Procedure({
contract: new ContractProcedure({
Expand Down Expand Up @@ -209,6 +213,7 @@ describe.each(hono)('openAPIHandler: %s', (_, hono) => {

expect(coerce).toBeCalledTimes(1)
expect(coerce).toBeCalledWith(undefined, { value: '123' })
expect(createProcedureClient).toBeCalledTimes(1)

Check failure on line 216 in packages/openapi/src/fetch/openapi-handler.test.ts

View workflow job for this annotation

GitHub Actions / test

packages/openapi/src/fetch/openapi-handler.test.ts > openAPIHandler: LinearRouter > schema coercer

AssertionError: expected "spy" to be called 1 times, but got 0 times ❯ packages/openapi/src/fetch/openapi-handler.test.ts:216:35

Check failure on line 216 in packages/openapi/src/fetch/openapi-handler.test.ts

View workflow job for this annotation

GitHub Actions / test

packages/openapi/src/fetch/openapi-handler.test.ts > openAPIHandler: TrieRouter > schema coercer

AssertionError: expected "spy" to be called 1 times, but got 0 times ❯ packages/openapi/src/fetch/openapi-handler.test.ts:216:35

Check failure on line 216 in packages/openapi/src/fetch/openapi-handler.test.ts

View workflow job for this annotation

GitHub Actions / test

packages/openapi/src/fetch/openapi-handler.test.ts > openAPIHandler: LinearRouter > schema coercer

AssertionError: expected "spy" to be called 1 times, but got 0 times ❯ packages/openapi/src/fetch/openapi-handler.test.ts:216:35

Check failure on line 216 in packages/openapi/src/fetch/openapi-handler.test.ts

View workflow job for this annotation

GitHub Actions / test

packages/openapi/src/fetch/openapi-handler.test.ts > openAPIHandler: TrieRouter > schema coercer

AssertionError: expected "spy" to be called 1 times, but got 0 times ❯ packages/openapi/src/fetch/openapi-handler.test.ts:216:35
expect(vi.mocked(createProcedureClient).mock.results[0]?.value).toBeCalledTimes(1)
expect(vi.mocked(createProcedureClient).mock.results[0]?.value).toBeCalledWith('__mocked__', { signal: undefined })
})
Expand Down
22 changes: 13 additions & 9 deletions packages/openapi/src/fetch/openapi-payload-codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class OpenAPIPayloadCodec {
}
}

const handledPayload = this.preEncode(payload)
const handledPayload = this.serialize(payload)
const hasBlobs = findDeepMatches(v => v instanceof Blob, handledPayload).values.length > 0

const isExpectedMultipartFormData = typeMatchers.some(isMatch =>
Expand Down Expand Up @@ -135,13 +135,13 @@ export class OpenAPIPayloadCodec {
}
}

private preEncode(payload: unknown): unknown {
serialize(payload: unknown): unknown {
if (payload instanceof Set)
return this.preEncode([...payload])
return this.serialize([...payload])
if (payload instanceof Map)
return this.preEncode([...payload.entries()])
return this.serialize([...payload.entries()])
if (Array.isArray(payload)) {
return payload.map(v => (v === undefined ? 'undefined' : this.preEncode(v)))
return payload.map(v => (v === undefined ? 'undefined' : this.serialize(v)))
}
if (Number.isNaN(payload))
return 'NaN'
Expand All @@ -159,15 +159,19 @@ export class OpenAPIPayloadCodec {
return Object.keys(payload).reduce(
(carry, key) => {
const val = payload[key]
carry[key] = this.preEncode(val)
carry[key] = this.serialize(val)
return carry
},
{} as Record<string, unknown>,
)
}

async decode(re: Request | Response | Headers | URLSearchParams): Promise<unknown> {
if (re instanceof Headers || re instanceof URLSearchParams) {
async decode(re: Request | Response | Headers | URLSearchParams | FormData): Promise<unknown> {
if (
re instanceof Headers
|| re instanceof URLSearchParams
|| re instanceof FormData
) {
return BracketNotation.deserialize([...re.entries()])
}

Expand Down Expand Up @@ -204,7 +208,7 @@ export class OpenAPIPayloadCodec {

if (contentType.startsWith('multipart/form-data')) {
const form = await re.formData()
return BracketNotation.deserialize([...form.entries()])
return this.decode(form)
}

const blob = await re.blob()
Expand Down
8 changes: 6 additions & 2 deletions packages/openapi/src/generator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { JSONSchema } from 'json-schema-typed/draft-2020-12'
import type { PublicOpenAPIPayloadCodec } from './fetch'
import type { EachLeafOptions } from './utils'
import { type ContractRouter, isContractProcedure } from '@orpc/contract'
import { type ANY_ROUTER, unlazy } from '@orpc/server'
import { findDeepMatches, isPlainObject, omit } from '@orpc/shared'
import { preSerialize } from '@orpc/transformer'
import {
type MediaTypeObject,
OpenApiBuilder,
Expand All @@ -13,6 +13,7 @@ import {
type RequestBodyObject,
type ResponseObject,
} from 'openapi3-ts/oas31'
import { OpenAPIPayloadCodec } from './fetch'
import { forEachContractProcedure, standardizeHTTPPath } from './utils'
import {
extractJSONSchema,
Expand All @@ -39,6 +40,8 @@ export interface GenerateOpenAPIOptions {
* @default false
*/
ignoreUndefinedPathProcedures?: boolean

payloadCodec?: PublicOpenAPIPayloadCodec
}

export async function generateOpenAPI(
Expand All @@ -51,6 +54,7 @@ export async function generateOpenAPI(
= options?.throwOnMissingTagDefinition ?? false
const ignoreUndefinedPathProcedures
= options?.ignoreUndefinedPathProcedures ?? false
const payloadCodec = options?.payloadCodec ?? new OpenAPIPayloadCodec()

const builder = new OpenApiBuilder({
...omit(opts, ['router']),
Expand Down Expand Up @@ -353,7 +357,7 @@ export async function generateOpenAPI(
}
}

return preSerialize(builder.getSpec()) as OpenAPIObject
return payloadCodec.serialize(builder.getSpec()) as OpenAPIObject
}

function isFileSchema(schema: unknown) {
Expand Down
4 changes: 1 addition & 3 deletions packages/openapi/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
"references": [
{ "path": "../contract" },
{ "path": "../server" },
{ "path": "../shared" },
{ "path": "../transformer" },
{ "path": "../zod" }
{ "path": "../shared" }
],
"include": ["src"],
"exclude": [
Expand Down
Loading

0 comments on commit d03672b

Please sign in to comment.