@lowerdeck/rpc-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +11 -0
- package/.turbo/turbo-test.log +35 -0
- package/dist/anonymizeIp.d.ts +10 -0
- package/dist/anonymizeIp.d.ts.map +1 -0
- package/dist/anonymizeIp.test.d.ts +2 -0
- package/dist/anonymizeIp.test.d.ts.map +1 -0
- package/dist/controller.d.ts +82 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.test.d.ts +2 -0
- package/dist/controller.test.d.ts.map +1 -0
- package/dist/extractIp.d.ts +2 -0
- package/dist/extractIp.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.modern.js +2 -0
- package/dist/index.modern.js.map +1 -0
- package/dist/index.module.js +2 -0
- package/dist/index.module.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/rpcMux.d.ts +30 -0
- package/dist/rpcMux.d.ts.map +1 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.d.ts.map +1 -0
- package/package.json +42 -0
- package/src/controller.test.ts +251 -0
- package/src/controller.ts +203 -0
- package/src/extractIp.ts +9 -0
- package/src/index.ts +3 -0
- package/src/rpcMux.ts +303 -0
- package/src/server.ts +204 -0
- package/tsconfig.json +13 -0
- package/tsup.config.js +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/controller.ts","../src/rpcMux.ts","../src/server.ts","../src/extractIp.ts"],"sourcesContent":["import { ServiceError, validationError } from '@lowerdeck/error';\nimport { ValidationType } from '@lowerdeck/validation';\nimport * as Cookie from 'cookie';\n\nexport type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\nexport interface ServiceRequest {\n query: URLSearchParams;\n headers: Headers;\n url: string;\n ip?: string;\n body: any;\n rawBody: any;\n requestId: string;\n\n getCookies: () => Record<string, string | undefined>;\n getCookie: (name: string) => string | undefined;\n setCookie: (name: string, value: string, opts?: Cookie.SerializeOptions) => void;\n\n sharedMiddlewareMemo: Map<string, Promise<any>>;\n beforeSend: (handler: () => Promise<any>) => void;\n appendHeaders: (headers: Record<string, string | string[]>) => void;\n}\n\nexport type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};\nexport type ExtendContext<C extends object, E> = E extends object ? Simplify<C & E> : C;\n\nexport class Group<Context extends { [key: string]: any } = {}> {\n constructor(\n private _middleware: Array<(ctx: Context & ServiceRequest) => Promise<any>> = []\n ) {}\n\n use<T extends { [key: string]: any } | undefined | void>(\n handler: (ctx: Context & ServiceRequest) => Promise<T>,\n opts?: {\n getSharedMemoKey?: (ctx: Context & ServiceRequest) => string;\n }\n ) {\n let middleware = async (ctx: Parameters<typeof handler>[0]): Promise<T> => {\n let key = opts?.getSharedMemoKey?.(ctx);\n if (key && ctx.sharedMiddlewareMemo.has(key)) {\n return await ctx.sharedMiddlewareMemo.get(key)!;\n }\n\n let res = handler(ctx);\n if (key) ctx.sharedMiddlewareMemo.set(key, res);\n\n return await res;\n };\n\n return new Group<Simplify<Context & T>>([...this._middleware, middleware]);\n }\n\n createMiddleware<T extends { [key: string]: any }, P = void>(\n handler: (ctx: Context & ServiceRequest, input: P) => Promise<T>,\n opts?: {\n getSharedMemoKey?: (ctx: Context & ServiceRequest, input: P) => string;\n }\n ) {\n return (input: P) =>\n async (ctx: Context & ServiceRequest): Promise<T> => {\n let key = opts?.getSharedMemoKey?.(ctx, input);\n if (key && ctx.sharedMiddlewareMemo.has(key)) {\n return await ctx.sharedMiddlewareMemo.get(key)!;\n }\n\n let res = handler(ctx, input);\n if (key) ctx.sharedMiddlewareMemo.set(key, res);\n\n return await res;\n };\n }\n\n handler() {\n return new Handler([...this._middleware]);\n }\n\n controller<\n HandlersAndSubControllers extends {\n [key: string]: Handler<any, any, any> | Controller<any>;\n }\n >(handlers: HandlersAndSubControllers): Controller<HandlersAndSubControllers> {\n return handlers;\n }\n}\n\nexport type Controller<\n HandlersAndSubControllers extends { [key: string]: Handler<any, any, any> | Controller<any> }\n> = HandlersAndSubControllers;\n\nexport type InferControllerType<T> = T extends Controller<infer U> ? U : never;\n\nexport type InferClient<\n HandlersAndSubControllers extends { [key: string]: Handler<any, any, any> | Controller<any> }\n> = {\n [K in keyof HandlersAndSubControllers]: HandlersAndSubControllers[K] extends Handler<\n infer I,\n infer O,\n infer C\n >\n ? ((\n input: I,\n opts?: { headers?: Record<string, string>; query?: Record<string, string> }\n ) => Promise<O>) & {\n getFull: (\n input: I,\n opts?: { headers?: Record<string, string>; query?: Record<string, string> }\n ) => Promise<{\n data: O;\n status: number;\n headers: Record<string, string>;\n }>;\n }\n : HandlersAndSubControllers[K] extends Controller<infer U>\n ? InferClient<U>\n : never;\n};\n\nexport class Handler<Input, Output, Context extends { [key: string]: any } = {}> {\n private _handler!: (\n ctx: Context & Omit<ServiceRequest, 'body'> & { input: Input }\n ) => Promise<Output>;\n private _validation: ValidationType<Input> | undefined;\n\n constructor(\n private _middleware: Array<(ctx: Context & ServiceRequest) => Promise<any>> = []\n ) {}\n\n do<HandlerOutput>(\n handler: (\n ctx: Context & Omit<ServiceRequest, 'body'> & { input: Input }\n ) => Promise<HandlerOutput>\n ) {\n if (this._handler != undefined) throw new Error('Handler already defined');\n\n // @ts-ignore\n this._handler = handler;\n\n return this as any as Handler<Input, HandlerOutput, Context>;\n }\n\n use<T extends { [key: string]: any } = {}>(\n handler: (ctx: Context & ServiceRequest) => Promise<T | undefined | void>\n ) {\n this._middleware.push(handler);\n return this as any as Handler<Input, Output, ExtendContext<Context, T>>;\n }\n\n input<HandlerInput>(validation: ValidationType<HandlerInput>) {\n if (this._validation != undefined) throw new Error('Input validation already defined');\n\n // @ts-ignore\n this._validation = validation;\n\n return this as any as Handler<HandlerInput, Output, Context>;\n }\n\n async run(\n req: ServiceRequest,\n initialContext: any\n ): Promise<{\n response: Output;\n }> {\n if (!this._handler) throw new Error('Handler not defined');\n\n let input = req.body as Input;\n\n if (this._validation) {\n let valRes = this._validation.validate(req.body);\n\n if (!valRes.success) {\n throw new ServiceError(\n validationError({ errors: valRes.errors, entity: 'call_data' })\n );\n }\n\n input = valRes.value;\n }\n\n let ctx = {\n ...initialContext,\n ...req,\n\n // Always use the sanitized input\n body: input\n };\n\n for (let mw of this._middleware) {\n let res = await mw(ctx);\n if (res) ctx = { ...ctx, ...res };\n }\n\n let res = await this._handler({\n ...ctx,\n input,\n body: undefined\n });\n\n return {\n response: res\n };\n }\n}\n","import {\n internalServerError,\n notAcceptableError,\n notFoundError,\n validationError\n} from '@lowerdeck/error';\nimport { createExecutionContext, provideExecutionContext } from '@lowerdeck/execution-context';\nimport { generateCustomId } from '@lowerdeck/id';\nimport { memo } from '@lowerdeck/memo';\nimport { getSentry } from '@lowerdeck/sentry';\nimport { serialize } from '@lowerdeck/serialize';\nimport { v } from '@lowerdeck/validation';\nimport * as Cookie from 'cookie';\nimport { ServiceRequest } from './controller';\nimport { parseForwardedFor } from './extractIp';\n\nlet Sentry = getSentry();\n\nlet validation = v.object({\n calls: v.array(\n v.object({\n id: v.string(),\n name: v.string(),\n payload: v.any()\n })\n )\n});\n\nexport let rpcMux = (\n opts: {\n path: string;\n cors?: {\n headers?: string[];\n } & ({ domains: string[] } | { check: (origin: string) => boolean });\n },\n rpcs: {\n handlerNames: string[];\n runMany: (\n req: ServiceRequest,\n body: {\n requestId: string;\n calls: {\n id: string;\n name: string;\n payload: any;\n }[];\n }\n ) => Promise<{\n status: number;\n body: {\n calls: any[];\n };\n }>;\n }[]\n) => {\n let handlerNameToRpcMap = new Map<string, number>(\n rpcs.flatMap((rpc, i) => rpc.handlerNames.map(name => [name, i]))\n );\n\n return {\n path: opts.path,\n\n fetch: async (req: any): Promise<any> => {\n let origin = req.headers.get('origin') ?? '';\n let corsOk = false;\n\n if (opts.cors && 'domains' in opts.cors) {\n try {\n let url = new URL(origin);\n let rootDomain = url.hostname.split('.').slice(-2).join('.');\n corsOk = opts.cors.domains.includes(rootDomain);\n } catch (e) {\n // Ignore -> no cors\n }\n } else if (opts.cors && 'check' in opts.cors) {\n corsOk = opts.cors.check(origin);\n }\n\n let url = new URL(req.url);\n\n let additionalCorsHeaders = opts.cors?.headers?.join(', ');\n if (additionalCorsHeaders) additionalCorsHeaders = `, ${additionalCorsHeaders}`.trim();\n\n let corsHeaders: Record<string, string> = corsOk\n ? {\n 'access-control-allow-origin': origin,\n 'access-control-allow-methods': 'POST, OPTIONS',\n 'access-control-allow-headers': `Content-Type, Authorization, Baggage, Sentry-Trace${\n additionalCorsHeaders ?? ''\n }`,\n 'access-control-max-age': '604800',\n 'access-control-allow-credentials': 'true'\n }\n : {};\n\n if (req.method == 'OPTIONS') {\n if (corsOk) {\n return new Response(null, {\n status: 204,\n headers: corsHeaders\n });\n }\n\n return new Response(null, { status: 403 });\n }\n\n let body: any = null;\n\n try {\n body = serialize.decode(await req.text());\n } catch (e) {\n return new Response(\n JSON.stringify(notAcceptableError({ message: 'Invalid JSON' }).toResponse()),\n { status: 406 }\n );\n }\n\n let sentryTraceHeaders = req.headers.get('sentry-trace');\n let sentryTrace =\n (Array.isArray(sentryTraceHeaders)\n ? sentryTraceHeaders.join(',')\n : sentryTraceHeaders) ?? undefined;\n let baggage = req.headers.get('baggage');\n\n let ip = parseForwardedFor(\n req.headers.get('lowerdeck-connecting-ip') ??\n req.headers.get('cf-connecting-ip') ??\n req.headers.get('x-forwarded-for') ??\n req.headers.get('x-real-ip')\n );\n\n let headers = new Headers();\n\n if (corsOk) {\n for (let [key, value] of Object.entries(corsHeaders)) {\n headers.append(key, value);\n }\n }\n\n return await Sentry.withIsolationScope(\n async () =>\n await Sentry.continueTrace(\n { sentryTrace, baggage },\n async () =>\n await Sentry.startSpan(\n {\n name: 'rpc request',\n op: 'rpc.server',\n attributes: {\n ip,\n transport: 'http',\n ua: req.headers.get('user-agent') ?? '',\n origin: req.headers.get('origin') ?? ''\n }\n },\n async () => {\n try {\n let beforeSends: Array<() => Promise<any>> = [];\n let id = generateCustomId('req_');\n\n let parseCookies = memo(() =>\n Cookie.parse(req.headers.get('cookie') ?? '')\n );\n\n let request: ServiceRequest = {\n url: req.url,\n headers: req.headers,\n query: url.searchParams,\n body,\n rawBody: body,\n ip,\n requestId: id,\n\n getCookies: () => parseCookies(),\n getCookie: (name: string) => parseCookies()[name],\n setCookie: (name: string, value: string, opts?: any) => {\n let cookie = Cookie.serialize(name, value, opts);\n // @ts-ignore\n headers.append('Set-Cookie', cookie);\n },\n\n beforeSend: (handler: () => Promise<any>) => {\n beforeSends.push(handler);\n },\n\n sharedMiddlewareMemo: new Map<string, Promise<any>>(),\n\n appendHeaders: (newHeaders: Record<string, string | string[]>) => {\n for (let [key, value] of Object.entries(newHeaders)) {\n if (Array.isArray(value)) {\n for (let v of value) headers.append(key, v);\n } else {\n headers.append(key, value);\n }\n }\n }\n };\n\n Sentry.getCurrentScope().setContext('rpc.request', {\n url: req.url,\n query: Object.fromEntries(url.searchParams.entries())\n });\n\n Sentry.getCurrentScope().addAttachment({\n filename: 'rpc.request.body.json',\n data: body,\n contentType: 'application/json'\n });\n\n let valRes = validation.validate(body);\n if (!valRes.success) {\n return new Response(\n JSON.stringify(\n validationError({\n errors: valRes.errors,\n entity: 'request_data'\n }).toResponse()\n ),\n { status: 406, headers }\n );\n }\n\n return provideExecutionContext(\n createExecutionContext({\n type: 'request',\n contextId: id,\n ip: ip ?? '0.0.0.0',\n userAgent: req.headers.get('user-agent') ?? ''\n }),\n async () => {\n let callsByRpc = new Map<\n number,\n { id: string; name: string; payload: any }[]\n >();\n\n for (let call of body.calls) {\n let rpcIndex = handlerNameToRpcMap.get(call.name);\n if (rpcIndex == undefined) {\n return new Response(\n JSON.stringify(\n notFoundError({ entity: 'handler' }).toResponse()\n ),\n { status: 404, headers }\n );\n }\n\n let calls = callsByRpc.get(rpcIndex) ?? [];\n calls.push(call);\n callsByRpc.set(rpcIndex, calls);\n }\n\n // let res = await runMany(request);\n\n let resRef = {\n body: {\n __typename: 'rpc.response',\n calls: [] as any[]\n },\n status: 200\n };\n\n await Promise.all(\n Array.from(callsByRpc.entries()).map(async ([rpcIndex, calls]) => {\n let rpc = rpcs[rpcIndex];\n let res = await rpc.runMany(request, {\n requestId: id,\n calls\n });\n\n resRef.status = Math.max(resRef.status, res.status);\n resRef.body.calls.push(...res.body.calls);\n })\n );\n\n headers.append('x-req-id', id);\n headers.append('content-type', 'application/rpc+json');\n headers.append('x-powered-by', 'lowerdeck RPC');\n\n await Promise.all(beforeSends.map(s => s()));\n\n return new Response(serialize.encode(resRef.body), {\n status: resRef.status,\n headers\n });\n }\n );\n } catch (e) {\n console.error(e);\n\n Sentry.captureException(e);\n\n return new Response(JSON.stringify(internalServerError().toResponse()), {\n status: 500,\n headers\n });\n }\n }\n )\n )\n );\n }\n };\n};\n","import { internalServerError, isServiceError, notFoundError } from '@lowerdeck/error';\nimport { getSentry } from '@lowerdeck/sentry';\nimport { Controller, Handler, ServiceRequest } from './controller';\n\nlet Sentry = getSentry();\n\nexport let createServer =\n (opts: {\n onError?: (opts: {\n request: ServiceRequest;\n error: any;\n reqId: string;\n callId: string;\n callName: string;\n }) => void;\n onRequest?: (opts: {\n reqId: string;\n callId: string;\n callName: string;\n request: ServiceRequest;\n response: { status: number; body: any };\n }) => void;\n }) =>\n (controller: Controller<any>) => {\n let findHandler = (name: string): Handler<any, any, any> | null => {\n let parts = name.split(':');\n let current = controller;\n\n while (current && parts.length > 0) {\n current = current[parts.shift()!];\n if (!current) return null;\n }\n\n if (current && current instanceof Handler) return current;\n\n return null;\n };\n\n let getSupportedHandlerNames = (controller: Controller<any>): string[] =>\n Object.entries(controller).flatMap(([key, value]) => {\n if (value instanceof Handler) return [key];\n if (typeof value == 'object')\n return getSupportedHandlerNames(value).map(name => `${key}:${name}`);\n return [];\n });\n\n let handlerNames = getSupportedHandlerNames(controller);\n\n let run = async (\n req: ServiceRequest,\n call: { id: string; name: string; payload: any },\n reqId: string\n ): Promise<{\n response: any;\n status: number;\n request: ServiceRequest;\n }> => {\n let request = { ...req, body: call.payload };\n\n try {\n let handler = findHandler(call.name);\n\n if (!handler) {\n return {\n request,\n status: 404,\n response: notFoundError({ entity: 'handler' }).toResponse()\n };\n }\n\n let response = await handler.run(request, {});\n\n return {\n status: 200,\n request: req,\n response: response.response\n };\n } catch (e) {\n console.error(e);\n\n if (isServiceError(e)) {\n if (e.data.status >= 500) {\n Sentry.captureException(e, {\n tags: { reqId }\n });\n }\n\n return {\n request,\n status: e.data.status,\n response: e.toResponse()\n };\n }\n\n Sentry.captureException(e, {\n tags: { reqId }\n });\n\n opts.onError?.({\n callName: call.name,\n callId: call.id,\n request: req,\n error: e,\n reqId\n });\n\n return {\n request,\n status: 500,\n response: internalServerError().toResponse()\n };\n }\n };\n\n let runMany = async (\n req: ServiceRequest,\n body: {\n calls: {\n id: string;\n name: string;\n payload: any;\n }[];\n requestId: string;\n }\n ): Promise<{\n status: number;\n body: any;\n }> => {\n let callRes = await Promise.all(\n body.calls.map(async (call, i) => {\n let res = await run(req, call as any, body.requestId);\n\n try {\n opts.onRequest?.({\n reqId: body.requestId,\n callId: call.id,\n callName: call.name,\n request: res.request,\n response: { status: res.status, body: res.response }\n });\n } catch (e) {\n Sentry.captureException(e);\n console.error(e);\n }\n\n return {\n __typename: 'rpc.response.call',\n id: call.id,\n name: call.name,\n status: res.status,\n result: res.response\n };\n })\n );\n\n return {\n status: Math.max(...callRes.map(c => c.status)),\n body: {\n __typename: 'rpc.response',\n calls: callRes\n }\n };\n };\n\n return {\n handlerNames,\n runMany\n\n // fetch,\n\n // http: async (\n // req: IncomingMessage & {\n // body: any;\n // },\n // res: ServerResponse & { send: (body: any) => void }\n // ) => {\n // let headers = new Headers(\n // Object.fromEntries(\n // Object.entries(req.headers).map(([key, value]) => [\n // key,\n // value === undefined ? '' : String(value)\n // ])\n // )\n // );\n // let url = new URL(req.url ?? '', `http://${req.headers.host}`);\n\n // let request = new Request(url.toString(), {\n // method: req.method,\n // headers,\n // body: JSON.stringify(req.body)\n // });\n\n // let response = await fetch(request);\n\n // res.statusCode = response.status;\n\n // for (let [key, value] of response.headers.entries()) {\n // res.setHeader(key, value);\n // }\n\n // res.send(response.body);\n // }\n };\n };\n","export let parseForwardedFor = (xForwardedForHeader?: string | null | undefined) => {\n if (typeof xForwardedForHeader != 'string') return undefined;\n\n let ips = xForwardedForHeader\n .split(',')\n .map(ip => ip.trim())\n .filter(Boolean);\n return ips.length > 0 ? ips[0] : undefined;\n};\n"],"names":["_settle","pact","state","value","s","_Pact","o","bind","v","then","observer","prototype","onFulfilled","onRejected","result","this","callback","e","_this","_isSettledPact","thenable","Group","_middleware","_proto","use","handler","opts","concat","ctx","_exit","_temp2","_result","res","key","sharedMiddlewareMemo","set","Promise","resolve","getSharedMemoKey","_temp","has","get","_await$ctx$sharedMidd","reject","createMiddleware","input","_exit2","_temp4","_result2","_temp3","_await$ctx$sharedMidd2","Handler","controller","handlers","_handler","_validation","_proto2","undefined","Error","push","validation","run","req","initialContext","_temp6","_extends","body","response","valRes","validate","success","ServiceError","validationError","errors","entity","_temp5","_forOf","mw","Sentry","getSentry","object","calls","array","id","string","name","payload","any","getSupportedHandlerNames","Object","entries","flatMap","_ref","map","handlerNames","runMany","all","call","i","reqId","request","parts","split","current","length","shift","findHandler","status","notFoundError","toResponse","_catch","console","error","isServiceError","data","captureException","tags","onError","callName","callId","internalServerError","requestId","onRequest","__typename","callRes","Math","max","apply","c","rpcs","handlerNameToRpcMap","Map","rpc","path","fetch","_req$headers$get6","_opts$cors","_ref2","_ref3","_req$headers$get","sentryTraceHeaders","headers","sentryTrace","Array","isArray","join","baggage","ip","xForwardedForHeader","ips","trim","filter","Boolean","parseForwardedFor","Headers","corsOk","_i","_Object$entries","corsHeaders","_Object$entries$_i","append","withIsolationScope","continueTrace","_req$headers$get2","_req$headers$get3","startSpan","op","attributes","transport","ua","origin","_req$headers$get5","beforeSends","generateCustomId","parseCookies","memo","_req$headers$get4","Cookie","parse","url","query","searchParams","rawBody","getCookies","getCookie","setCookie","cookie","serialize","beforeSend","appendHeaders","newHeaders","_i2","_Object$entries2","_Object$entries2$_i","_iterator","_step","_createForOfIteratorHelperLoose","done","getCurrentScope","setContext","fromEntries","addAttachment","filename","contentType","provideExecutionContext","createExecutionContext","type","contextId","userAgent","_step2","callsByRpc","_iterator2","_callsByRpc$get","rpcIndex","Response","JSON","stringify","resRef","from","_ref4","_resRef$body$calls","encode","cors","rootDomain","URL","hostname","slice","domains","includes","check","additionalCorsHeaders","method","_decode","decode","text","_req$text","_Response","notAcceptableError","message"],"mappings":"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"}
|
package/dist/rpcMux.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ServiceRequest } from './controller';
|
|
2
|
+
export declare let rpcMux: (opts: {
|
|
3
|
+
path: string;
|
|
4
|
+
cors?: {
|
|
5
|
+
headers?: string[];
|
|
6
|
+
} & ({
|
|
7
|
+
domains: string[];
|
|
8
|
+
} | {
|
|
9
|
+
check: (origin: string) => boolean;
|
|
10
|
+
});
|
|
11
|
+
}, rpcs: {
|
|
12
|
+
handlerNames: string[];
|
|
13
|
+
runMany: (req: ServiceRequest, body: {
|
|
14
|
+
requestId: string;
|
|
15
|
+
calls: {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
payload: any;
|
|
19
|
+
}[];
|
|
20
|
+
}) => Promise<{
|
|
21
|
+
status: number;
|
|
22
|
+
body: {
|
|
23
|
+
calls: any[];
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
26
|
+
}[]) => {
|
|
27
|
+
path: string;
|
|
28
|
+
fetch: (req: any) => Promise<any>;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=rpcMux.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpcMux.d.ts","sourceRoot":"","sources":["../src/rpcMux.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAe9C,eAAO,IAAI,MAAM,GACf,MAAM;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,GAAG,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG;QAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;KAAE,CAAC,CAAC;CACtE,EACD,MAAM;IACJ,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,CACP,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE;YACL,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,GAAG,CAAC;SACd,EAAE,CAAC;KACL,KACE,OAAO,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE;YACJ,KAAK,EAAE,GAAG,EAAE,CAAC;SACd,CAAC;KACH,CAAC,CAAC;CACJ,EAAE;;iBASkB,GAAG,KAAG,OAAO,CAAC,GAAG,CAAC;CAgPxC,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Controller, ServiceRequest } from './controller';
|
|
2
|
+
export declare let createServer: (opts: {
|
|
3
|
+
onError?: (opts: {
|
|
4
|
+
request: ServiceRequest;
|
|
5
|
+
error: any;
|
|
6
|
+
reqId: string;
|
|
7
|
+
callId: string;
|
|
8
|
+
callName: string;
|
|
9
|
+
}) => void;
|
|
10
|
+
onRequest?: (opts: {
|
|
11
|
+
reqId: string;
|
|
12
|
+
callId: string;
|
|
13
|
+
callName: string;
|
|
14
|
+
request: ServiceRequest;
|
|
15
|
+
response: {
|
|
16
|
+
status: number;
|
|
17
|
+
body: any;
|
|
18
|
+
};
|
|
19
|
+
}) => void;
|
|
20
|
+
}) => (controller: Controller<any>) => {
|
|
21
|
+
handlerNames: string[];
|
|
22
|
+
runMany: (req: ServiceRequest, body: {
|
|
23
|
+
calls: {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
payload: any;
|
|
27
|
+
}[];
|
|
28
|
+
requestId: string;
|
|
29
|
+
}) => Promise<{
|
|
30
|
+
status: number;
|
|
31
|
+
body: any;
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAW,cAAc,EAAE,MAAM,cAAc,CAAC;AAInE,eAAO,IAAI,YAAY,GACpB,MAAM;IACL,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,cAAc,CAAC;QACxB,KAAK,EAAE,GAAG,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,IAAI,CAAC;IACX,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,cAAc,CAAC;QACxB,QAAQ,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,GAAG,CAAA;SAAE,CAAC;KACzC,KAAK,IAAI,CAAC;CACZ,MACA,YAAY,UAAU,CAAC,GAAG,CAAC;;mBA4FnB,cAAc,QACb;QACJ,KAAK,EAAE;YACL,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,GAAG,CAAC;SACd,EAAE,CAAC;QACJ,SAAS,EAAE,MAAM,CAAC;KACnB,KACA,OAAO,CAAC;QACT,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,GAAG,CAAC;KACX,CAAC;CA4EH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lowerdeck/rpc-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"author": "Tobias Herber",
|
|
8
|
+
"license": "Apache 2",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"source": "src/index.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"require": "./dist/index.cjs",
|
|
14
|
+
"import": "./dist/index.module.js",
|
|
15
|
+
"default": "./dist/index.module.js"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.module.js",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"unpkg": "./dist/index.umd.js",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "vitest run --passWithNoTests",
|
|
23
|
+
"lint": "prettier src/**/*.ts --check",
|
|
24
|
+
"build": "microbundle"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"microbundle": "^0.15.1",
|
|
28
|
+
"@lowerdeck/tsconfig": "^1.0.0",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"vitest": "^3.2.4"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@lowerdeck/error": "^1.0.5",
|
|
34
|
+
"@lowerdeck/execution-context": "^1.0.0",
|
|
35
|
+
"@lowerdeck/id": "^1.0.2",
|
|
36
|
+
"@lowerdeck/memo": "^1.0.1",
|
|
37
|
+
"@lowerdeck/sentry": "^1.0.0",
|
|
38
|
+
"@lowerdeck/serialize": "^1.0.1",
|
|
39
|
+
"@lowerdeck/validation": "^1.0.1",
|
|
40
|
+
"cookie": "^1.1.1"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { v } from '@lowerdeck/validation';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
import { Group } from './controller';
|
|
4
|
+
|
|
5
|
+
describe('controller', () => {
|
|
6
|
+
test('should create group', () => {
|
|
7
|
+
let group = new Group();
|
|
8
|
+
expect(group).toBeDefined();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('should register middleware', async () => {
|
|
12
|
+
let group = new Group();
|
|
13
|
+
|
|
14
|
+
let group1 = group.use(async ctx => {
|
|
15
|
+
expect(typeof ctx).toBe('object');
|
|
16
|
+
expect(ctx.body).toBe('body');
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
test: 'test' as const
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
let group2 = group1.use(async ctx => {
|
|
24
|
+
expect(ctx.test).toBe('test');
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
hello: 'world' as const
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let handler = group2.handler().do(async ctx => {
|
|
32
|
+
expect(ctx.hello).toBe('world');
|
|
33
|
+
expect(ctx.test).toBe('test');
|
|
34
|
+
|
|
35
|
+
return ctx;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(
|
|
39
|
+
await handler.run(
|
|
40
|
+
{
|
|
41
|
+
body: 'body',
|
|
42
|
+
query: new URLSearchParams(),
|
|
43
|
+
headers: new Headers(),
|
|
44
|
+
rawBody: '',
|
|
45
|
+
sharedMiddlewareMemo: new Map(),
|
|
46
|
+
beforeSend: () => {},
|
|
47
|
+
appendHeaders: () => {},
|
|
48
|
+
url: ''
|
|
49
|
+
} as any,
|
|
50
|
+
{}
|
|
51
|
+
)
|
|
52
|
+
).toEqual({
|
|
53
|
+
response: {
|
|
54
|
+
hello: 'world',
|
|
55
|
+
test: 'test',
|
|
56
|
+
input: 'body',
|
|
57
|
+
query: new URLSearchParams(),
|
|
58
|
+
headers: new Headers(),
|
|
59
|
+
rawBody: '',
|
|
60
|
+
sharedMiddlewareMemo: new Map(),
|
|
61
|
+
appendHeaders: expect.any(Function),
|
|
62
|
+
beforeSend: expect.any(Function),
|
|
63
|
+
body: undefined,
|
|
64
|
+
url: ''
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should register handler middleware', async () => {
|
|
70
|
+
let group = new Group().use(async ctx => {
|
|
71
|
+
expect(typeof ctx).toBe('object');
|
|
72
|
+
expect(ctx.body).toBe('body');
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
test: 'test' as const
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
let handler = group
|
|
80
|
+
.handler()
|
|
81
|
+
.use(async ctx => {
|
|
82
|
+
expect(ctx.test).toBe('test');
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
hello: 'world' as const
|
|
86
|
+
};
|
|
87
|
+
})
|
|
88
|
+
.do(async ctx => {
|
|
89
|
+
expect(ctx.hello).toBe('world');
|
|
90
|
+
expect(ctx.test).toBe('test');
|
|
91
|
+
|
|
92
|
+
return ctx;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expect(
|
|
96
|
+
await handler.run(
|
|
97
|
+
{
|
|
98
|
+
body: 'body',
|
|
99
|
+
query: new URLSearchParams(),
|
|
100
|
+
headers: new Headers(),
|
|
101
|
+
rawBody: '',
|
|
102
|
+
sharedMiddlewareMemo: new Map(),
|
|
103
|
+
beforeSend: () => {},
|
|
104
|
+
appendHeaders: () => {},
|
|
105
|
+
url: ''
|
|
106
|
+
} as any,
|
|
107
|
+
{}
|
|
108
|
+
)
|
|
109
|
+
).toMatchObject({
|
|
110
|
+
response: {
|
|
111
|
+
hello: 'world',
|
|
112
|
+
test: 'test',
|
|
113
|
+
input: 'body',
|
|
114
|
+
query: new URLSearchParams(),
|
|
115
|
+
headers: new Headers(),
|
|
116
|
+
rawBody: '',
|
|
117
|
+
sharedMiddlewareMemo: new Map(),
|
|
118
|
+
appendHeaders: expect.any(Function),
|
|
119
|
+
beforeSend: expect.any(Function),
|
|
120
|
+
body: undefined,
|
|
121
|
+
url: ''
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('should run validation', async () => {
|
|
127
|
+
let group = new Group().use(async ctx => {
|
|
128
|
+
expect(typeof ctx).toBe('object');
|
|
129
|
+
expect(ctx.body).toEqual({ hello: 'world' });
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
test: 'test' as const
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
let handler = group
|
|
137
|
+
.handler()
|
|
138
|
+
.input(
|
|
139
|
+
v.object({
|
|
140
|
+
hello: v.string()
|
|
141
|
+
})
|
|
142
|
+
)
|
|
143
|
+
.do(async ctx => {
|
|
144
|
+
expect(ctx.test).toBe('test');
|
|
145
|
+
expect(ctx.input).toEqual({ hello: 'world' });
|
|
146
|
+
|
|
147
|
+
return ctx;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(
|
|
151
|
+
await handler.run(
|
|
152
|
+
{
|
|
153
|
+
query: new URLSearchParams(),
|
|
154
|
+
headers: new Headers(),
|
|
155
|
+
rawBody: '',
|
|
156
|
+
sharedMiddlewareMemo: new Map(),
|
|
157
|
+
beforeSend: () => {},
|
|
158
|
+
appendHeaders: () => {},
|
|
159
|
+
url: '',
|
|
160
|
+
body: { hello: 'world', other: false }
|
|
161
|
+
} as any,
|
|
162
|
+
{}
|
|
163
|
+
)
|
|
164
|
+
).toEqual({
|
|
165
|
+
response: {
|
|
166
|
+
test: 'test',
|
|
167
|
+
input: { hello: 'world' },
|
|
168
|
+
query: new URLSearchParams(),
|
|
169
|
+
headers: new Headers(),
|
|
170
|
+
rawBody: '',
|
|
171
|
+
sharedMiddlewareMemo: new Map(),
|
|
172
|
+
appendHeaders: expect.any(Function),
|
|
173
|
+
beforeSend: expect.any(Function),
|
|
174
|
+
body: undefined,
|
|
175
|
+
url: ''
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
await handler.run(
|
|
181
|
+
{
|
|
182
|
+
query: new URLSearchParams(),
|
|
183
|
+
headers: new Headers(),
|
|
184
|
+
rawBody: '',
|
|
185
|
+
sharedMiddlewareMemo: new Map(),
|
|
186
|
+
beforeSend: () => {},
|
|
187
|
+
appendHeaders: () => {},
|
|
188
|
+
url: '',
|
|
189
|
+
body: { hello: 42 }
|
|
190
|
+
} as any,
|
|
191
|
+
{}
|
|
192
|
+
);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
expect(error).toBeDefined();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('should group handlers to controller', async () => {
|
|
199
|
+
let group = new Group();
|
|
200
|
+
|
|
201
|
+
let controller = group.controller({
|
|
202
|
+
first: group
|
|
203
|
+
.handler()
|
|
204
|
+
.input(
|
|
205
|
+
v.object({
|
|
206
|
+
hello: v.string()
|
|
207
|
+
})
|
|
208
|
+
)
|
|
209
|
+
.do(async ctx => {
|
|
210
|
+
return ctx;
|
|
211
|
+
}),
|
|
212
|
+
|
|
213
|
+
second: group.handler().do(async ctx => {
|
|
214
|
+
return { cool: true };
|
|
215
|
+
}),
|
|
216
|
+
|
|
217
|
+
sub: group.controller({
|
|
218
|
+
third: group
|
|
219
|
+
.handler()
|
|
220
|
+
.input(
|
|
221
|
+
v.object({
|
|
222
|
+
hello: v.string()
|
|
223
|
+
})
|
|
224
|
+
)
|
|
225
|
+
.do(async ctx => {
|
|
226
|
+
return { sub: true };
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
expect(
|
|
232
|
+
await controller.sub.third.run(
|
|
233
|
+
{
|
|
234
|
+
query: new URLSearchParams(),
|
|
235
|
+
headers: new Headers(),
|
|
236
|
+
rawBody: '',
|
|
237
|
+
sharedMiddlewareMemo: new Map(),
|
|
238
|
+
beforeSend: () => {},
|
|
239
|
+
appendHeaders: () => {},
|
|
240
|
+
url: '',
|
|
241
|
+
body: { hello: 'world' }
|
|
242
|
+
} as any,
|
|
243
|
+
{}
|
|
244
|
+
)
|
|
245
|
+
).toEqual({
|
|
246
|
+
response: {
|
|
247
|
+
sub: true
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { ServiceError, validationError } from '@lowerdeck/error';
|
|
2
|
+
import { ValidationType } from '@lowerdeck/validation';
|
|
3
|
+
import * as Cookie from 'cookie';
|
|
4
|
+
|
|
5
|
+
export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
6
|
+
|
|
7
|
+
export interface ServiceRequest {
|
|
8
|
+
query: URLSearchParams;
|
|
9
|
+
headers: Headers;
|
|
10
|
+
url: string;
|
|
11
|
+
ip?: string;
|
|
12
|
+
body: any;
|
|
13
|
+
rawBody: any;
|
|
14
|
+
requestId: string;
|
|
15
|
+
|
|
16
|
+
getCookies: () => Record<string, string | undefined>;
|
|
17
|
+
getCookie: (name: string) => string | undefined;
|
|
18
|
+
setCookie: (name: string, value: string, opts?: Cookie.SerializeOptions) => void;
|
|
19
|
+
|
|
20
|
+
sharedMiddlewareMemo: Map<string, Promise<any>>;
|
|
21
|
+
beforeSend: (handler: () => Promise<any>) => void;
|
|
22
|
+
appendHeaders: (headers: Record<string, string | string[]>) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
|
|
26
|
+
export type ExtendContext<C extends object, E> = E extends object ? Simplify<C & E> : C;
|
|
27
|
+
|
|
28
|
+
export class Group<Context extends { [key: string]: any } = {}> {
|
|
29
|
+
constructor(
|
|
30
|
+
private _middleware: Array<(ctx: Context & ServiceRequest) => Promise<any>> = []
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
use<T extends { [key: string]: any } | undefined | void>(
|
|
34
|
+
handler: (ctx: Context & ServiceRequest) => Promise<T>,
|
|
35
|
+
opts?: {
|
|
36
|
+
getSharedMemoKey?: (ctx: Context & ServiceRequest) => string;
|
|
37
|
+
}
|
|
38
|
+
) {
|
|
39
|
+
let middleware = async (ctx: Parameters<typeof handler>[0]): Promise<T> => {
|
|
40
|
+
let key = opts?.getSharedMemoKey?.(ctx);
|
|
41
|
+
if (key && ctx.sharedMiddlewareMemo.has(key)) {
|
|
42
|
+
return await ctx.sharedMiddlewareMemo.get(key)!;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let res = handler(ctx);
|
|
46
|
+
if (key) ctx.sharedMiddlewareMemo.set(key, res);
|
|
47
|
+
|
|
48
|
+
return await res;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return new Group<Simplify<Context & T>>([...this._middleware, middleware]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
createMiddleware<T extends { [key: string]: any }, P = void>(
|
|
55
|
+
handler: (ctx: Context & ServiceRequest, input: P) => Promise<T>,
|
|
56
|
+
opts?: {
|
|
57
|
+
getSharedMemoKey?: (ctx: Context & ServiceRequest, input: P) => string;
|
|
58
|
+
}
|
|
59
|
+
) {
|
|
60
|
+
return (input: P) =>
|
|
61
|
+
async (ctx: Context & ServiceRequest): Promise<T> => {
|
|
62
|
+
let key = opts?.getSharedMemoKey?.(ctx, input);
|
|
63
|
+
if (key && ctx.sharedMiddlewareMemo.has(key)) {
|
|
64
|
+
return await ctx.sharedMiddlewareMemo.get(key)!;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let res = handler(ctx, input);
|
|
68
|
+
if (key) ctx.sharedMiddlewareMemo.set(key, res);
|
|
69
|
+
|
|
70
|
+
return await res;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
handler() {
|
|
75
|
+
return new Handler([...this._middleware]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
controller<
|
|
79
|
+
HandlersAndSubControllers extends {
|
|
80
|
+
[key: string]: Handler<any, any, any> | Controller<any>;
|
|
81
|
+
}
|
|
82
|
+
>(handlers: HandlersAndSubControllers): Controller<HandlersAndSubControllers> {
|
|
83
|
+
return handlers;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type Controller<
|
|
88
|
+
HandlersAndSubControllers extends { [key: string]: Handler<any, any, any> | Controller<any> }
|
|
89
|
+
> = HandlersAndSubControllers;
|
|
90
|
+
|
|
91
|
+
export type InferControllerType<T> = T extends Controller<infer U> ? U : never;
|
|
92
|
+
|
|
93
|
+
export type InferClient<
|
|
94
|
+
HandlersAndSubControllers extends { [key: string]: Handler<any, any, any> | Controller<any> }
|
|
95
|
+
> = {
|
|
96
|
+
[K in keyof HandlersAndSubControllers]: HandlersAndSubControllers[K] extends Handler<
|
|
97
|
+
infer I,
|
|
98
|
+
infer O,
|
|
99
|
+
infer C
|
|
100
|
+
>
|
|
101
|
+
? ((
|
|
102
|
+
input: I,
|
|
103
|
+
opts?: { headers?: Record<string, string>; query?: Record<string, string> }
|
|
104
|
+
) => Promise<O>) & {
|
|
105
|
+
getFull: (
|
|
106
|
+
input: I,
|
|
107
|
+
opts?: { headers?: Record<string, string>; query?: Record<string, string> }
|
|
108
|
+
) => Promise<{
|
|
109
|
+
data: O;
|
|
110
|
+
status: number;
|
|
111
|
+
headers: Record<string, string>;
|
|
112
|
+
}>;
|
|
113
|
+
}
|
|
114
|
+
: HandlersAndSubControllers[K] extends Controller<infer U>
|
|
115
|
+
? InferClient<U>
|
|
116
|
+
: never;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export class Handler<Input, Output, Context extends { [key: string]: any } = {}> {
|
|
120
|
+
private _handler!: (
|
|
121
|
+
ctx: Context & Omit<ServiceRequest, 'body'> & { input: Input }
|
|
122
|
+
) => Promise<Output>;
|
|
123
|
+
private _validation: ValidationType<Input> | undefined;
|
|
124
|
+
|
|
125
|
+
constructor(
|
|
126
|
+
private _middleware: Array<(ctx: Context & ServiceRequest) => Promise<any>> = []
|
|
127
|
+
) {}
|
|
128
|
+
|
|
129
|
+
do<HandlerOutput>(
|
|
130
|
+
handler: (
|
|
131
|
+
ctx: Context & Omit<ServiceRequest, 'body'> & { input: Input }
|
|
132
|
+
) => Promise<HandlerOutput>
|
|
133
|
+
) {
|
|
134
|
+
if (this._handler != undefined) throw new Error('Handler already defined');
|
|
135
|
+
|
|
136
|
+
// @ts-ignore
|
|
137
|
+
this._handler = handler;
|
|
138
|
+
|
|
139
|
+
return this as any as Handler<Input, HandlerOutput, Context>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
use<T extends { [key: string]: any } = {}>(
|
|
143
|
+
handler: (ctx: Context & ServiceRequest) => Promise<T | undefined | void>
|
|
144
|
+
) {
|
|
145
|
+
this._middleware.push(handler);
|
|
146
|
+
return this as any as Handler<Input, Output, ExtendContext<Context, T>>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
input<HandlerInput>(validation: ValidationType<HandlerInput>) {
|
|
150
|
+
if (this._validation != undefined) throw new Error('Input validation already defined');
|
|
151
|
+
|
|
152
|
+
// @ts-ignore
|
|
153
|
+
this._validation = validation;
|
|
154
|
+
|
|
155
|
+
return this as any as Handler<HandlerInput, Output, Context>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async run(
|
|
159
|
+
req: ServiceRequest,
|
|
160
|
+
initialContext: any
|
|
161
|
+
): Promise<{
|
|
162
|
+
response: Output;
|
|
163
|
+
}> {
|
|
164
|
+
if (!this._handler) throw new Error('Handler not defined');
|
|
165
|
+
|
|
166
|
+
let input = req.body as Input;
|
|
167
|
+
|
|
168
|
+
if (this._validation) {
|
|
169
|
+
let valRes = this._validation.validate(req.body);
|
|
170
|
+
|
|
171
|
+
if (!valRes.success) {
|
|
172
|
+
throw new ServiceError(
|
|
173
|
+
validationError({ errors: valRes.errors, entity: 'call_data' })
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
input = valRes.value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let ctx = {
|
|
181
|
+
...initialContext,
|
|
182
|
+
...req,
|
|
183
|
+
|
|
184
|
+
// Always use the sanitized input
|
|
185
|
+
body: input
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
for (let mw of this._middleware) {
|
|
189
|
+
let res = await mw(ctx);
|
|
190
|
+
if (res) ctx = { ...ctx, ...res };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let res = await this._handler({
|
|
194
|
+
...ctx,
|
|
195
|
+
input,
|
|
196
|
+
body: undefined
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
response: res
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
package/src/extractIp.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export let parseForwardedFor = (xForwardedForHeader?: string | null | undefined) => {
|
|
2
|
+
if (typeof xForwardedForHeader != 'string') return undefined;
|
|
3
|
+
|
|
4
|
+
let ips = xForwardedForHeader
|
|
5
|
+
.split(',')
|
|
6
|
+
.map(ip => ip.trim())
|
|
7
|
+
.filter(Boolean);
|
|
8
|
+
return ips.length > 0 ? ips[0] : undefined;
|
|
9
|
+
};
|
package/src/index.ts
ADDED