kb-server 0.0.1-beta.37 → 0.0.1-beta.39

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/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # KB Server
2
2
 
3
- 快速创建一个 Node 服务
3
+ 快速创建一个 `Node` 服务 - 基于 `Express`
4
4
 
5
- 快速创建 API、标准错误码、Server、支持统一鉴权函数(API 级别)、自定义中间件、日志、支持SSE
5
+ 快速创建 `API`、标准错误码、`Server`、支持统一鉴权函数(API 级别)、自定义中间件、日志、支持 `SSE`
6
6
 
7
- ```javascript
7
+ ```typescript
8
8
  import { createServer } from "kb-server";
9
9
  import * as apis from "./apis";
10
10
 
@@ -18,3 +18,27 @@ server.listen(3000);
18
18
  return createServer({ apis });
19
19
  })().then((app) => app.listen(3000));
20
20
  ```
21
+
22
+ ## SSE 支持
23
+
24
+ ```typescript
25
+ import { createServer } from "kb-server";
26
+ import * as apis from "./apis";
27
+ import * as sse from "./sse";
28
+
29
+
30
+ (async () => {
31
+ // 其他的异步操作,例如:初始化数据库
32
+ return createServer({
33
+ apis,
34
+ sse: {
35
+ handlers: sse, // ...SSE处理函数
36
+ },
37
+ authFn: (action, req) => {
38
+ // ...统一鉴权函数
39
+ },
40
+ middlewares: [], // ...中间键列表
41
+ log: true, // ...框架日志
42
+ });
43
+ })().then((app) => app.listen(3000));
44
+ ```
@@ -27,7 +27,7 @@ const packAPI = (apis, options) => {
27
27
  // API 解析
28
28
  const { Action, ...params } = req.body || {};
29
29
  if (log) {
30
- logger_1.logger.log("info", `请求入参:${JSON.stringify(req.body)} - RequestId: ${requestId}`);
30
+ logger_1.logger.info(`请求入参:${JSON.stringify(req.body)} - RequestId: ${requestId}`);
31
31
  }
32
32
  // 接口未定义
33
33
  if (!Action) {
@@ -63,12 +63,12 @@ const packAPI = (apis, options) => {
63
63
  };
64
64
  // 完成响应
65
65
  took = Date.now() - start;
66
- logger_1.logger.log("info", `响应:${JSON.stringify(response)}`);
67
- logger_1.logger.log("info", `耗时:${took} ms - RequestId: ${requestId}`);
66
+ logger_1.logger.info(`响应:${JSON.stringify(response)}`);
67
+ logger_1.logger.info(`耗时:${took} ms - RequestId: ${requestId}`);
68
68
  return res.send(response);
69
69
  }
70
70
  catch (rawError) {
71
- logger_1.logger.log("error", rawError);
71
+ logger_1.logger.error(rawError);
72
72
  // 未知错误
73
73
  let error = new create_errors_1.CommonErrors.InternalError.UnknownError();
74
74
  // 可控错误
@@ -90,8 +90,8 @@ const packAPI = (apis, options) => {
90
90
  };
91
91
  // 完成响应
92
92
  took = Date.now() - start;
93
- logger_1.logger.log("info", `响应:${JSON.stringify(response)}`);
94
- logger_1.logger.log("info", `耗时:${took} ms - RequestId: ${requestId}`);
93
+ logger_1.logger.info(`响应:${JSON.stringify(response)}`);
94
+ logger_1.logger.info(`耗时:${took} ms - RequestId: ${requestId}`);
95
95
  return res.send(response);
96
96
  }
97
97
  };
@@ -1,13 +1,9 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.callService = void 0;
7
- const node_fetch_1 = __importDefault(require("node-fetch"));
8
4
  const create_errors_1 = require("./create-errors");
9
5
  const callService = async (endpoint, body) => {
10
- const response = await (0, node_fetch_1.default)(endpoint, {
6
+ const response = await fetch(endpoint, {
11
7
  headers: {
12
8
  "Content-Type": "application/json",
13
9
  },
@@ -1,20 +1,22 @@
1
1
  import { Class } from "utility-types";
2
2
  import { ServerContext } from "./create-api";
3
+ export type PushHandler = (data: Record<string, any>) => void;
4
+ export type CloseHandler = () => void;
5
+ export type SSEHandlers = {
6
+ /** 数据推送 */
7
+ push: PushHandler;
8
+ /** 关闭函数(调用结束要显式关闭) */
9
+ close: CloseHandler;
10
+ abortController: AbortController;
11
+ };
3
12
  export type API<P, R> = (param: P) => Promise<R>;
4
13
  export type SseExecution<P, R, A> = (
5
14
  /** 请求入参 */
6
15
  params: P,
16
+ /** SSE相关操作函数 */
17
+ sse: SSEHandlers,
7
18
  /** 请求上下文 */
8
- ctx: ServerContext<A>,
9
- /**
10
- * 操作函数
11
- */
12
- operations: {
13
- /**
14
- * 向客户端推送消息
15
- */
16
- send: (event: string, message: string) => void;
17
- }) => Promise<R>;
19
+ ctx: ServerContext<A>) => Promise<R>;
18
20
  export type AnySseExecution = SseExecution<any, any, any>;
19
21
  export type SseHandlers = Record<string, AnySseExecution>;
20
22
  export declare function createSseAPI<P, R, A>(ParamsClass: Class<P>, execution: SseExecution<P, R, A>): API<P, R>;
@@ -6,7 +6,7 @@ const class_validator_1 = require("class-validator");
6
6
  const create_errors_1 = require("./create-errors");
7
7
  const create_api_1 = require("./create-api");
8
8
  function createSseAPI(ParamsClass, execution) {
9
- const runtime = async (params, ctx, operations) => {
9
+ const runtime = async (params, sse, ctx) => {
10
10
  if (!params) {
11
11
  throw new create_errors_1.CommonErrors.InvalidParameter.EmptyParameter();
12
12
  }
@@ -20,7 +20,7 @@ function createSseAPI(ParamsClass, execution) {
20
20
  throw new create_errors_1.CommonErrors.InvalidParameter.ValidationError(errorMessages.join("\n"));
21
21
  }
22
22
  // 执行函数
23
- return await execution(params, ctx, operations);
23
+ return await execution(params, sse, ctx);
24
24
  };
25
25
  return runtime;
26
26
  }
@@ -7,5 +7,5 @@ interface IPackSSEOptions {
7
7
  log?: boolean;
8
8
  }
9
9
  export declare const packSSE: (sseHandlers: SseHandlers, options?: IPackSSEOptions) => express.RequestHandler;
10
- export declare const createSSEMessage: (event: string, message: string) => string;
10
+ export declare const createSSEMsg: (event: string, message: string) => string;
11
11
  export {};
@@ -1,11 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSSEMessage = exports.packSSE = void 0;
3
+ exports.createSSEMsg = exports.packSSE = void 0;
4
4
  const uuid_1 = require("uuid");
5
5
  const logger_1 = require("../helper/logger");
6
6
  const create_errors_1 = require("./create-errors");
7
7
  const short_id_1 = require("../helper/short-id");
8
- const sleep_1 = require("../helper/sleep");
9
8
  const packSSE = (sseHandlers, options) => {
10
9
  const { authFn, log = true, route = "/sse" } = options || {};
11
10
  return async (req, res, next) => {
@@ -38,7 +37,7 @@ const packSSE = (sseHandlers, options) => {
38
37
  // API 解析
39
38
  const { Action, ...params } = req.body || {};
40
39
  if (log) {
41
- logger_1.logger.log("info", `请求入参:${JSON.stringify(req.body)} - RequestId: ${requestId}`);
40
+ logger_1.logger.info(`请求入参:${JSON.stringify(req.body)} - RequestId: ${requestId}`);
42
41
  }
43
42
  // 接口未定义
44
43
  if (!Action) {
@@ -63,50 +62,55 @@ const packSSE = (sseHandlers, options) => {
63
62
  if (typeof execution !== "function") {
64
63
  throw new create_errors_1.CommonErrors.ResourceNotFound.APINotFound();
65
64
  }
66
- // 在调用 execution 前添加中断监听
67
- let isClientConnected = true;
68
- // 监听客户端断开事件
69
- res.on("close", () => {
70
- isClientConnected = false; // 标记连接已断开
71
- logger_1.logger.log("info", `客户端已断开 - RequestId: ${requestId}`);
72
- });
73
- // 写请求头
65
+ // 写头
74
66
  res.writeHead(200, {
75
67
  "Content-Type": "text/event-stream",
76
68
  "Cache-Control": "no-cache",
77
69
  Connection: "keep-alive",
78
70
  });
79
- const send = (event, message) => {
80
- if (!isClientConnected) {
81
- logger_1.logger.log("warning", "客户端已断开,停止发送");
82
- return; // 终止发送逻辑
71
+ const abortController = new AbortController();
72
+ // 连接断开
73
+ let isConnected = true;
74
+ res.on("close", () => {
75
+ isConnected = false;
76
+ abortController.abort();
77
+ // 完成响应
78
+ took = Date.now() - start;
79
+ logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
80
+ return res.end();
81
+ });
82
+ // 推送消息
83
+ const push = (data) => {
84
+ if (!isConnected) {
85
+ logger_1.logger.warning(`连接已关闭: ${requestId}`);
86
+ return;
83
87
  }
84
- const response = (0, exports.createSSEMessage)(event, message);
88
+ const msg = createResponseMsg(data);
85
89
  try {
86
- res.write(response);
87
- if (log) {
88
- logger_1.logger.log("info", `发送消息:\n${response}`);
89
- }
90
+ res.write(msg);
91
+ logger_1.logger.info(`推送数据: ${msg}`);
90
92
  }
91
- catch (err) {
92
- if (err.code === "EPIPE" || err.code === "ECONNRESET") {
93
- logger_1.logger.log("error", "客户端连接已关闭");
94
- isClientConnected = false;
93
+ catch (error) {
94
+ if (error.code === "EPIPE" || error.code === "ECONNRESET") {
95
+ logger_1.logger.warning(`连接已关闭: ${requestId}`);
96
+ isConnected = false;
95
97
  }
96
98
  }
97
99
  };
98
- await execution(params, ctx, { send });
99
- // 完成响应
100
- took = Date.now() - start;
101
- logger_1.logger.log("info", `耗时:${took} ms - RequestId: ${requestId}`);
102
- // 延迟1ms关闭连接
103
- await (0, sleep_1.sleep)(1);
104
- return res.end();
100
+ // 主动关闭
101
+ const close = () => {
102
+ logger_1.logger.info(`主动关闭连接: ${requestId}`);
103
+ // 完成响应
104
+ took = Date.now() - start;
105
+ logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
106
+ res.end();
107
+ };
108
+ await execution(params, { push, close, abortController }, ctx);
105
109
  }
106
110
  catch (rawError) {
107
- logger_1.logger.log("error", rawError);
111
+ logger_1.logger.error(rawError);
108
112
  if (!res.headersSent) {
109
- // 写请求头
113
+ // 写头
110
114
  res.writeHead(200, {
111
115
  "Content-Type": "text/event-stream",
112
116
  "Cache-Control": "no-cache",
@@ -123,29 +127,36 @@ const packSSE = (sseHandlers, options) => {
123
127
  if (rawError?.sql) {
124
128
  error = new create_errors_1.CommonErrors.InternalError.DatabaseError();
125
129
  }
126
- const response = (0, exports.createSSEMessage)("error", JSON.stringify({
130
+ const errResponse = {
127
131
  Response: {
128
132
  Error: { Code: error.code, Message: error.message },
129
133
  RequestId: requestId,
130
134
  },
131
- }));
135
+ };
136
+ const response = (0, exports.createSSEMsg)("error", JSON.stringify(errResponse));
132
137
  // 完成响应
133
138
  took = Date.now() - start;
134
- logger_1.logger.log("info", `发送消息:\n${response}`);
135
- logger_1.logger.log("info", `耗时:${took} ms - RequestId: ${requestId}`);
136
- res.write(response);
137
- // 延迟100ms关闭连接
138
- await (0, sleep_1.sleep)(100);
139
- return res.end();
139
+ logger_1.logger.info(`发送消息:\n${response}`);
140
+ logger_1.logger.info(`耗时:${took} ms - ${requestId}`);
141
+ res.write(response, () => res.end());
140
142
  }
141
143
  };
142
144
  };
143
145
  exports.packSSE = packSSE;
144
- const createSSEMessage = (event, message) => {
146
+ const createSSEMsg = (event, message) => {
145
147
  const id = (0, short_id_1.shortId)();
146
148
  const idStr = `id: ${id}\n`;
147
149
  const eventStr = `event: ${event}\n`;
148
150
  const dataStr = `data: ${message}\n\n`;
149
- return idStr + eventStr + dataStr;
151
+ return `${idStr}${eventStr}${dataStr}`;
152
+ };
153
+ exports.createSSEMsg = createSSEMsg;
154
+ const createResponseMsg = (data, requestId) => {
155
+ const msg = {
156
+ Response: {
157
+ RequestId: requestId || (0, uuid_1.v4)(),
158
+ Data: data,
159
+ },
160
+ };
161
+ return JSON.stringify(msg);
150
162
  };
151
- exports.createSSEMessage = createSSEMessage;
@@ -1,6 +1,5 @@
1
1
  export declare const logger: {
2
- log: typeof log;
2
+ info: (message: unknown) => void;
3
+ error: (message: unknown) => void;
4
+ warning: (message: unknown) => void;
3
5
  };
4
- type LogLevel = "info" | "error" | "warning";
5
- declare function log(level: LogLevel, message: unknown): void;
6
- export {};
@@ -4,14 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logger = void 0;
7
- const moment_1 = __importDefault(require("moment"));
8
- exports.logger = { log };
9
- function log(level, message) {
10
- const logTime = (0, moment_1.default)().format(`YYYY-MM-DD HH:mm:ss`);
7
+ const dayjs_1 = __importDefault(require("dayjs"));
8
+ const log = (level, message) => {
9
+ const logTime = (0, dayjs_1.default)().format(`YYYY-MM-DD HH:mm:ss`);
11
10
  if (typeof message === "string") {
12
11
  console.log(`[${level}] - [${logTime}] - ${message}`);
13
12
  }
14
13
  else {
15
14
  console.log(`[${level}] - [${logTime}] - `, message);
16
15
  }
17
- }
16
+ };
17
+ const info = (message) => log("info", message);
18
+ const error = (message) => log("error", message);
19
+ const warning = (message) => log("warning", message);
20
+ exports.logger = { info, error, warning };
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "kb-server",
3
- "version": "0.0.1-beta.37",
3
+ "version": "0.0.1-beta.39",
4
4
  "description": "A fast server for Node.JS,made by express.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
7
  "build": "rm -rf ./dist && tsc",
8
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "release": "npm run build && npm publish"
9
9
  },
10
10
  "keywords": [
11
11
  "2kb",
@@ -16,21 +16,23 @@
16
16
  "dist/",
17
17
  "README.md"
18
18
  ],
19
+ "engines": {
20
+ "node": ">=18.0.0"
21
+ },
19
22
  "author": "broxiang",
20
23
  "license": "ISC",
21
24
  "dependencies": {
25
+ "better-sse": "^0.13.0",
22
26
  "class-transformer": "^0.5.1",
23
27
  "class-validator": "^0.14.1",
28
+ "dayjs": "^1.11.13",
24
29
  "express": "^4.21.1",
25
30
  "is-plain-object": "^5.0.0",
26
- "moment": "^2.30.1",
27
31
  "nanoid": "^3.3.9",
28
- "node-fetch": "^2.7.0",
29
32
  "utility-types": "^3.11.0",
30
33
  "uuid": "^11.0.3"
31
34
  },
32
35
  "devDependencies": {
33
- "@types/express": "^4.17.21",
34
- "@types/node-fetch": "^2.6.12"
36
+ "@types/express": "^4.17.21"
35
37
  }
36
38
  }