diff --git a/packages/server/src/implementer-chainable.ts b/packages/server/src/implementer-chainable.ts index b30f5fc1..643773ad 100644 --- a/packages/server/src/implementer-chainable.ts +++ b/packages/server/src/implementer-chainable.ts @@ -1,6 +1,7 @@ import type { Middleware } from './middleware' import type { Context, MergeContext, WELL_CONTEXT } from './types' import { type ContractProcedure, type ContractRouter, isContractProcedure } from '@orpc/contract' +import { createCallableObject } from '@orpc/shared' import { ProcedureImplementer } from './procedure-implementer' import { RouterImplementer } from './router-implementer' @@ -31,38 +32,28 @@ export function createChainableImplementer< return implementer as any } - const chainable: Record = {} + const chainable = {} as ChainableImplementer for (const key in contract) { - chainable[key] = createChainableImplementer(contract[key]!, middlewares) + (chainable as any)[key] = createChainableImplementer(contract[key]!, middlewares) } const routerImplementer = new RouterImplementer({ contract, middlewares }) const merged = new Proxy(chainable, { get(target, key) { - const next = Reflect.get(target, key) + const next = Reflect.get(target, key) as ChainableImplementer | undefined const method = Reflect.get(routerImplementer, key) if (typeof key !== 'string' || typeof method !== 'function') { return next } - return new Proxy(method.bind(routerImplementer), { - get(target, key) { - // TODO: create own utils for object callable proxy - if ( - typeof key !== 'string' - || !next - || (typeof next !== 'object' && typeof next !== 'function') - || !(key in next) - ) { - return Reflect.get(target, key) - } + if (!next) { + return method.bind(routerImplementer) + } - return next[key] - }, - }) + return createCallableObject(next, method.bind(routerImplementer)) }, }) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index cebe4c6d..5fd079d1 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -3,6 +3,7 @@ export * from './function' export * from './hook' export * from './json' export * from './object' +export * from './proxy' export * from './value' export { isPlainObject } from 'is-what' diff --git a/packages/shared/src/proxy.ts b/packages/shared/src/proxy.ts new file mode 100644 index 00000000..df4ea9be --- /dev/null +++ b/packages/shared/src/proxy.ts @@ -0,0 +1,30 @@ +import type { AnyFunction } from './function' + +export function createCallableObject(obj: TObject, func: TFunc): TObject & TFunc { + const proxy = new Proxy(func, { + has(target, key) { + return Reflect.has(obj, key) || Reflect.has(target, key) + }, + ownKeys(target) { + return Array.from(new Set(Reflect.ownKeys(obj).concat(...Reflect.ownKeys(target)))) + }, + get(target, key) { + if (!Reflect.has(target, key) || Reflect.has(obj, key)) { + return Reflect.get(obj, key) + } + + return Reflect.get(target, key) + }, + defineProperty(_, key, descriptor) { + return Reflect.defineProperty(obj, key, descriptor) + }, + set(_, key, value) { + return Reflect.set(obj, key, value) + }, + deleteProperty(target, key) { + return Reflect.deleteProperty(target, key) && Reflect.deleteProperty(obj, key) + }, + }) + + return proxy as any +}