@kibinrpc/server 0.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ixexel661
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ //#region src/decorator.d.ts
2
+ declare function ServerAction(): <This, Args extends unknown[], Return>(target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>) => (this: This, ...args: Args) => Return;
3
+ //#endregion
4
+ //#region src/errors.d.ts
5
+ declare class KibinError extends Error {
6
+ readonly code: string;
7
+ readonly statusCode: number;
8
+ constructor(code: string, message: string, statusCode?: number);
9
+ }
10
+ //#endregion
11
+ //#region src/fn.d.ts
12
+ declare function serverAction<T extends (...args: never[]) => unknown>(fn: T): T;
13
+ declare function defineActions<T extends Record<string, (...args: never[]) => unknown>>(actions: T): T;
14
+ //#endregion
15
+ //#region src/router.d.ts
16
+ type Services = Record<string, object>;
17
+ declare function createRouter<T extends Services>(services: T): {
18
+ services: T;
19
+ handler: (request: Request) => Promise<Response>;
20
+ };
21
+ //#endregion
22
+ //#region src/types.d.ts
23
+ interface RpcRequest {
24
+ namespace: string;
25
+ method: string;
26
+ args: unknown[];
27
+ }
28
+ interface RpcResponse<T = unknown> {
29
+ data?: T;
30
+ error?: {
31
+ code: string;
32
+ message: string;
33
+ };
34
+ }
35
+ //#endregion
36
+ export { KibinError, type RpcRequest, type RpcResponse, ServerAction, createRouter, defineActions, serverAction };
37
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/decorator.ts","../src/errors.ts","../src/fn.ts","../src/router.ts","../src/types.ts"],"mappings":";iBAEgB,YAAA,CAAA,0CAEd,MAAA,GAAS,IAAA,EAAM,IAAA,KAAS,IAAA,EAAM,IAAA,KAAS,MAAA,EACvC,OAAA,EAAS,2BAAA,CAA4B,IAAA,GAAO,IAAA,EAAM,IAAA,KAAS,IAAA,EAAM,IAAA,KAAS,MAAA,OAAO,IAAA,EADlE,IAAA,KAAI,IAAA,EAAW,IAAA,KAAS,MAAA;;;cCJ5B,UAAA,SAAmB,KAAK;EAAA,SAC3B,IAAA;EAAA,SACA,UAAA;cAEG,IAAA,UAAc,OAAA,UAAiB,UAAA;AAAA;;;iBCF5B,YAAA,eAA2B,IAAA,sBAAA,CAA2B,EAAA,EAAI,CAAA,GAAI,CAAC;AAAA,iBAK/D,aAAA,WAAwB,MAAA,aAAmB,IAAA,uBAAA,CAC1D,OAAA,EAAS,CAAA,GACP,CAAA;;;KCLE,QAAA,GAAW,MAAM;AAAA,iBASN,YAAA,WAAuB,QAAA,CAAA,CAAU,QAAA,EAAU,CAAA;;qBAC1B,OAAA,KAAU,OAAA,CAAQ,QAAA;AAAA;;;UCdlC,UAAA;EAChB,SAAA;EACA,MAAA;EACA,IAAA;AAAA;AAAA,UAGgB,WAAA;EAChB,IAAA,GAAO,CAAC;EACR,KAAA;IAAU,IAAA;IAAc,OAAA;EAAA;AAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,98 @@
1
+ //#region src/registry.ts
2
+ const SERVER_ACTIONS_KEY = Symbol("serverActions");
3
+ const FUNCTION_ACTION_KEY = Symbol("functionAction");
4
+ function getRegisteredActions(instance) {
5
+ return Reflect.get(instance, SERVER_ACTIONS_KEY) ?? /* @__PURE__ */ new Set();
6
+ }
7
+ function isBrandedAction(fn) {
8
+ return typeof fn === "function" && Reflect.get(fn, FUNCTION_ACTION_KEY) === true;
9
+ }
10
+ //#endregion
11
+ //#region src/decorator.ts
12
+ function ServerAction() {
13
+ return (target, context) => {
14
+ context.addInitializer(function() {
15
+ if (!Reflect.has(this, SERVER_ACTIONS_KEY)) Reflect.set(this, SERVER_ACTIONS_KEY, /* @__PURE__ */ new Set());
16
+ Reflect.get(this, SERVER_ACTIONS_KEY).add(String(context.name));
17
+ });
18
+ return target;
19
+ };
20
+ }
21
+ //#endregion
22
+ //#region src/errors.ts
23
+ var KibinError = class extends Error {
24
+ code;
25
+ statusCode;
26
+ constructor(code, message, statusCode = 400) {
27
+ super(message);
28
+ this.name = "KibinError";
29
+ this.code = code;
30
+ this.statusCode = statusCode;
31
+ }
32
+ };
33
+ //#endregion
34
+ //#region src/fn.ts
35
+ function serverAction(fn) {
36
+ Reflect.set(fn, FUNCTION_ACTION_KEY, true);
37
+ return fn;
38
+ }
39
+ function defineActions(actions) {
40
+ for (const key of Object.keys(actions)) serverAction(actions[key]);
41
+ return actions;
42
+ }
43
+ //#endregion
44
+ //#region src/router.ts
45
+ function jsonResponse(body, status) {
46
+ return new Response(JSON.stringify(body), {
47
+ status,
48
+ headers: { "Content-Type": "application/json" }
49
+ });
50
+ }
51
+ function createRouter(services) {
52
+ async function handler(request) {
53
+ let body;
54
+ try {
55
+ body = await request.json();
56
+ } catch {
57
+ return jsonResponse({ error: {
58
+ code: "BAD_REQUEST",
59
+ message: "Invalid JSON body"
60
+ } }, 400);
61
+ }
62
+ const { namespace, method, args } = body;
63
+ const service = services[namespace];
64
+ if (!service) return jsonResponse({ error: {
65
+ code: "NOT_FOUND",
66
+ message: `Namespace "${namespace}" not found`
67
+ } }, 404);
68
+ const fn = service[method];
69
+ if (!(getRegisteredActions(service).has(method) || isBrandedAction(fn))) return jsonResponse({ error: {
70
+ code: "METHOD_NOT_FOUND",
71
+ message: `"${method}" is not a registered server action`
72
+ } }, 404);
73
+ if (typeof fn !== "function") return jsonResponse({ error: {
74
+ code: "METHOD_NOT_FOUND",
75
+ message: `Method "${method}" not found`
76
+ } }, 404);
77
+ try {
78
+ return jsonResponse({ data: await fn.apply(service, args) }, 200);
79
+ } catch (error) {
80
+ if (error instanceof KibinError) return jsonResponse({ error: {
81
+ code: error.code,
82
+ message: error.message
83
+ } }, error.statusCode);
84
+ return jsonResponse({ error: {
85
+ code: "INTERNAL_ERROR",
86
+ message: "Internal server error"
87
+ } }, 500);
88
+ }
89
+ }
90
+ return {
91
+ services,
92
+ handler
93
+ };
94
+ }
95
+ //#endregion
96
+ export { KibinError, ServerAction, createRouter, defineActions, serverAction };
97
+
98
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/registry.ts","../src/decorator.ts","../src/errors.ts","../src/fn.ts","../src/router.ts"],"sourcesContent":["export const SERVER_ACTIONS_KEY = Symbol('serverActions');\nexport const FUNCTION_ACTION_KEY = Symbol('functionAction');\n\nexport function getRegisteredActions(instance: object): Set<string> {\n\treturn (Reflect.get(instance, SERVER_ACTIONS_KEY) as Set<string> | undefined) ?? new Set();\n}\n\nexport function isBrandedAction(fn: unknown): boolean {\n\treturn typeof fn === 'function' && Reflect.get(fn, FUNCTION_ACTION_KEY) === true;\n}\n","import { SERVER_ACTIONS_KEY } from './registry.js';\n\nexport function ServerAction() {\n\treturn <This, Args extends unknown[], Return>(\n\t\ttarget: (this: This, ...args: Args) => Return,\n\t\tcontext: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>,\n\t) => {\n\t\tcontext.addInitializer(function (this: This) {\n\t\t\tif (!Reflect.has(this as object, SERVER_ACTIONS_KEY)) {\n\t\t\t\tReflect.set(this as object, SERVER_ACTIONS_KEY, new Set<string>());\n\t\t\t}\n\t\t\t(Reflect.get(this as object, SERVER_ACTIONS_KEY) as Set<string>).add(String(context.name));\n\t\t});\n\t\treturn target;\n\t};\n}\n","export class KibinError extends Error {\n\treadonly code: string;\n\treadonly statusCode: number;\n\n\tconstructor(code: string, message: string, statusCode = 400) {\n\t\tsuper(message);\n\t\tthis.name = 'KibinError';\n\t\tthis.code = code;\n\t\tthis.statusCode = statusCode;\n\t}\n}\n","import { FUNCTION_ACTION_KEY } from './registry.js';\n\nexport function serverAction<T extends (...args: never[]) => unknown>(fn: T): T {\n\tReflect.set(fn, FUNCTION_ACTION_KEY, true);\n\treturn fn;\n}\n\nexport function defineActions<T extends Record<string, (...args: never[]) => unknown>>(\n\tactions: T,\n): T {\n\tfor (const key of Object.keys(actions)) {\n\t\tserverAction(actions[key]);\n\t}\n\treturn actions;\n}\n","import { KibinError } from './errors.js';\nimport { getRegisteredActions, isBrandedAction } from './registry.js';\nimport type { RpcRequest, RpcResponse } from './types.js';\n\ntype Services = Record<string, object>;\n\nfunction jsonResponse(body: RpcResponse, status: number): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: { 'Content-Type': 'application/json' },\n\t});\n}\n\nexport function createRouter<T extends Services>(services: T) {\n\tasync function handler(request: Request): Promise<Response> {\n\t\tlet body: RpcRequest;\n\t\ttry {\n\t\t\tbody = (await request.json()) as RpcRequest;\n\t\t} catch {\n\t\t\treturn jsonResponse({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } }, 400);\n\t\t}\n\n\t\tconst { namespace, method, args } = body;\n\n\t\tconst service = services[namespace];\n\t\tif (!service) {\n\t\t\treturn jsonResponse(\n\t\t\t\t{ error: { code: 'NOT_FOUND', message: `Namespace \"${namespace}\" not found` } },\n\t\t\t\t404,\n\t\t\t);\n\t\t}\n\n\t\tconst fn = (service as Record<string, unknown>)[method];\n\t\tconst isAllowed = getRegisteredActions(service).has(method) || isBrandedAction(fn);\n\t\tif (!isAllowed) {\n\t\t\treturn jsonResponse(\n\t\t\t\t{\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: 'METHOD_NOT_FOUND',\n\t\t\t\t\t\tmessage: `\"${method}\" is not a registered server action`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t404,\n\t\t\t);\n\t\t}\n\n\t\tif (typeof fn !== 'function') {\n\t\t\treturn jsonResponse(\n\t\t\t\t{ error: { code: 'METHOD_NOT_FOUND', message: `Method \"${method}\" not found` } },\n\t\t\t\t404,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await fn.apply(service, args);\n\t\t\treturn jsonResponse({ data: result }, 200);\n\t\t} catch (error) {\n\t\t\tif (error instanceof KibinError) {\n\t\t\t\treturn jsonResponse(\n\t\t\t\t\t{ error: { code: error.code, message: error.message } },\n\t\t\t\t\terror.statusCode,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn jsonResponse(\n\t\t\t\t{ error: { code: 'INTERNAL_ERROR', message: 'Internal server error' } },\n\t\t\t\t500,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { services, handler };\n}\n"],"mappings":";AAAA,MAAa,qBAAqB,OAAO,eAAe;AACxD,MAAa,sBAAsB,OAAO,gBAAgB;AAE1D,SAAgB,qBAAqB,UAA+B;CACnE,OAAQ,QAAQ,IAAI,UAAU,kBAAkB,qBAAiC,IAAI,IAAI;AAC1F;AAEA,SAAgB,gBAAgB,IAAsB;CACrD,OAAO,OAAO,OAAO,cAAc,QAAQ,IAAI,IAAI,mBAAmB,MAAM;AAC7E;;;ACPA,SAAgB,eAAe;CAC9B,QACC,QACA,YACI;EACJ,QAAQ,eAAe,WAAsB;GAC5C,IAAI,CAAC,QAAQ,IAAI,MAAgB,kBAAkB,GAClD,QAAQ,IAAI,MAAgB,oCAAoB,IAAI,IAAY,CAAC;GAElE,QAAS,IAAI,MAAgB,kBAAkB,EAAkB,IAAI,OAAO,QAAQ,IAAI,CAAC;EAC1F,CAAC;EACD,OAAO;CACR;AACD;;;ACfA,IAAa,aAAb,cAAgC,MAAM;CACrC;CACA;CAEA,YAAY,MAAc,SAAiB,aAAa,KAAK;EAC5D,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,aAAa;CACnB;AACD;;;ACRA,SAAgB,aAAsD,IAAU;CAC/E,QAAQ,IAAI,IAAI,qBAAqB,IAAI;CACzC,OAAO;AACR;AAEA,SAAgB,cACf,SACI;CACJ,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACpC,aAAa,QAAQ,IAAI;CAE1B,OAAO;AACR;;;ACRA,SAAS,aAAa,MAAmB,QAA0B;CAClE,OAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;EACzC;EACA,SAAS,EAAE,gBAAgB,mBAAmB;CAC/C,CAAC;AACF;AAEA,SAAgB,aAAiC,UAAa;CAC7D,eAAe,QAAQ,SAAqC;EAC3D,IAAI;EACJ,IAAI;GACH,OAAQ,MAAM,QAAQ,KAAK;EAC5B,QAAQ;GACP,OAAO,aAAa,EAAE,OAAO;IAAE,MAAM;IAAe,SAAS;GAAoB,EAAE,GAAG,GAAG;EAC1F;EAEA,MAAM,EAAE,WAAW,QAAQ,SAAS;EAEpC,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,SACJ,OAAO,aACN,EAAE,OAAO;GAAE,MAAM;GAAa,SAAS,cAAc,UAAU;EAAa,EAAE,GAC9E,GACD;EAGD,MAAM,KAAM,QAAoC;EAEhD,IAAI,EADc,qBAAqB,OAAO,EAAE,IAAI,MAAM,KAAK,gBAAgB,EAAE,IAEhF,OAAO,aACN,EACC,OAAO;GACN,MAAM;GACN,SAAS,IAAI,OAAO;EACrB,EACD,GACA,GACD;EAGD,IAAI,OAAO,OAAO,YACjB,OAAO,aACN,EAAE,OAAO;GAAE,MAAM;GAAoB,SAAS,WAAW,OAAO;EAAa,EAAE,GAC/E,GACD;EAGD,IAAI;GAEH,OAAO,aAAa,EAAE,MAAM,MADP,GAAG,MAAM,SAAS,IAAI,EACR,GAAG,GAAG;EAC1C,SAAS,OAAO;GACf,IAAI,iBAAiB,YACpB,OAAO,aACN,EAAE,OAAO;IAAE,MAAM,MAAM;IAAM,SAAS,MAAM;GAAQ,EAAE,GACtD,MAAM,UACP;GAED,OAAO,aACN,EAAE,OAAO;IAAE,MAAM;IAAkB,SAAS;GAAwB,EAAE,GACtE,GACD;EACD;CACD;CAEA,OAAO;EAAE;EAAU;CAAQ;AAC5B"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@kibinrpc/server",
3
+ "version": "0.0.2",
4
+ "description": "Devloper-friendly RPC server router with full type inference",
5
+ "license": "MIT",
6
+ "author": "ixexel661",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/ixexel661/kibin",
10
+ "directory": "packages/server"
11
+ },
12
+ "keywords": [
13
+ "rpc",
14
+ "server",
15
+ "typescript",
16
+ "web-api",
17
+ "api",
18
+ "backend"
19
+ ],
20
+ "type": "module",
21
+ "sideEffects": false,
22
+ "exports": {
23
+ ".": {
24
+ "import": "./dist/index.mjs",
25
+ "types": "./dist/index.d.mts"
26
+ }
27
+ },
28
+ "main": "./dist/index.mjs",
29
+ "types": "./dist/index.d.mts",
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "build": "tsdown",
38
+ "dev": "tsdown --watch"
39
+ }
40
+ }