Skip to content

Commit f22c7ec

Browse files
authored
feat(server): strict .callable/.actionable cannot chainable after call (#88)
1 parent 8072e6e commit f22c7ec

3 files changed

Lines changed: 15 additions & 19 deletions

File tree

packages/server/src/procedure-decorated.test-d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Client, ClientRest, ORPCError } from '@orpc/contract'
22
import type { ORPCErrorConstructorMap } from './error'
33
import type { Middleware, MiddlewareOutputFn } from './middleware'
4-
import type { ANY_PROCEDURE } from './procedure'
4+
import type { ANY_PROCEDURE, Procedure } from './procedure'
55
import type { DecoratedProcedure } from './procedure-decorated'
66
import type { WELL_CONTEXT } from './types'
77
import { z } from 'zod'
@@ -233,7 +233,7 @@ describe('self chainable', () => {
233233
})
234234

235235
expectTypeOf(callable).toEqualTypeOf<
236-
& DecoratedProcedure<{ auth: boolean }, { db: string }, typeof baseSchema, typeof baseSchema, { val: string }, typeof baseErrors>
236+
& Procedure<{ auth: boolean }, { db: string }, typeof baseSchema, typeof baseSchema, { val: string }, typeof baseErrors>
237237
& Client<'something', { val: string }, { val: number }, Error | ORPCError<'CODE', { why: string }>>
238238
>()
239239
})
@@ -244,7 +244,7 @@ describe('self chainable', () => {
244244
})
245245

246246
expectTypeOf(actionable).toEqualTypeOf<
247-
& DecoratedProcedure<{ auth: boolean }, { db: string }, typeof baseSchema, typeof baseSchema, { val: string }, typeof baseErrors>
247+
& Procedure<{ auth: boolean }, { db: string }, typeof baseSchema, typeof baseSchema, { val: string }, typeof baseErrors>
248248
& ((...rest: ClientRest<'something', { val: string }>) => Promise<{ val: number }>)
249249
>()
250250
})

packages/server/src/procedure-decorated.test.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -250,16 +250,14 @@ describe('self chainable', () => {
250250
expect(createProcedureClient).toBeCalledWith(decorated, options)
251251
})
252252

253-
it('can chain after callable', () => {
253+
it('can not chain after callable', () => {
254254
const mid2 = vi.fn()
255255

256256
const applied = decorated.callable({
257257
context: { auth: true },
258-
}).use(mid2)
258+
})
259259

260-
expect(applied).not.toBeInstanceOf(Function)
261-
expect(applied).toSatisfy(isProcedure)
262-
expect(applied['~orpc'].postMiddlewares).toEqual([mid, mid2])
260+
expect(applied).not.haveOwnPropertyDescriptor('use')
263261
})
264262
})
265263

@@ -274,16 +272,14 @@ describe('self chainable', () => {
274272
expect(createProcedureClient).toBeCalledWith(decorated, options)
275273
})
276274

277-
it('can chain after actionable', () => {
275+
it('can not chain after actionable', () => {
278276
const mid2 = vi.fn()
279277

280278
const applied = decorated.actionable({
281279
context: { auth: true },
282-
}).use(mid2)
280+
})
283281

284-
expect(applied).not.toBeInstanceOf(Function)
285-
expect(applied).toSatisfy(isProcedure)
286-
expect(applied['~orpc'].postMiddlewares).toEqual([mid, mid2])
282+
expect(applied).not.haveOwnPropertyDescriptor('use')
287283
})
288284
})
289285
})

packages/server/src/procedure-decorated.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { ANY_MIDDLEWARE, MapInputMiddleware, Middleware } from './middlewar
44
import type { CreateProcedureClientRest, ProcedureClient } from './procedure-client'
55
import type { Context, MergeContext } from './types'
66
import { DecoratedContractProcedure } from '@orpc/contract'
7-
import { createCallableObject } from '@orpc/shared'
87
import { decorateMiddleware } from './middleware-decorated'
98
import { Procedure } from './procedure'
109
import { createProcedureClient } from './procedure-client'
@@ -150,20 +149,21 @@ export class DecoratedProcedure<
150149

151150
/**
152151
* Make this procedure callable (works like a function while still being a procedure).
153-
* **Note**: this only takes effect when this method is called at the end of the chain.
154152
*/
155153
callable<TClientContext>(...rest: CreateProcedureClientRest<TContext, TOutputSchema, THandlerOutput, TClientContext>):
156-
& DecoratedProcedure<TContext, TExtraContext, TInputSchema, TOutputSchema, THandlerOutput, TErrorMap>
154+
& Procedure<TContext, TExtraContext, TInputSchema, TOutputSchema, THandlerOutput, TErrorMap>
157155
& ProcedureClient<TClientContext, TInputSchema, TOutputSchema, THandlerOutput, TErrorMap> {
158-
return createCallableObject(this, createProcedureClient(this, ...rest))
156+
return Object.assign(createProcedureClient(this, ...rest), {
157+
'~type': 'Procedure' as const,
158+
'~orpc': this['~orpc'],
159+
})
159160
}
160161

161162
/**
162163
* Make this procedure compatible with server action (the same as .callable, but the type is compatible with server action).
163-
* **Note**: this only takes effect when this method is called at the end of the chain.
164164
*/
165165
actionable<TClientContext>(...rest: CreateProcedureClientRest<TContext, TOutputSchema, THandlerOutput, TClientContext>):
166-
& DecoratedProcedure<TContext, TExtraContext, TInputSchema, TOutputSchema, THandlerOutput, TErrorMap>
166+
& Procedure<TContext, TExtraContext, TInputSchema, TOutputSchema, THandlerOutput, TErrorMap>
167167
& ((...rest: ClientRest<TClientContext, SchemaInput<TInputSchema>>) => Promise<SchemaOutput<TOutputSchema, THandlerOutput>>) {
168168
return this.callable(...rest)
169169
}

0 commit comments

Comments
 (0)