@yumerijs/core 2.2.2 → 3.0.0-alpha.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/dist/server.d.ts CHANGED
@@ -1,24 +1,42 @@
1
1
  import { Core } from './core.js';
2
+ /** 服务器配置接口 */
2
3
  export interface ServerConfig {
4
+ /** 监听端口 */
3
5
  port: number;
6
+ /** 监听主机 */
4
7
  host: string;
8
+ /** 是否启用跨域 */
5
9
  enableCors: boolean;
6
- staticDir: string;
10
+ /** 是否启用 WebSocket */
7
11
  enableWs: boolean;
8
12
  }
13
+ /**
14
+ * 核心服务器类
15
+ * 负责底层 HTTP 请求的监听、分发和响应
16
+ */
9
17
  export declare class Server {
10
18
  core: Core;
11
19
  private port;
12
20
  private host;
13
21
  private enableCors;
14
- private staticDir;
15
22
  private httpServer;
16
23
  constructor(core: Core, config?: Partial<ServerConfig>);
17
- getStaticDir(): string;
24
+ /**
25
+ * 创建一个新的会话对象
26
+ */
18
27
  private createSession;
28
+ /**
29
+ * 解析请求中的 Cookie
30
+ */
19
31
  private parseCookies;
32
+ /**
33
+ * 获取客户端真实 IP
34
+ */
20
35
  private getClientIP;
21
- private serveStaticFile;
36
+ /**
37
+ * 启动服务器
38
+ */
22
39
  start(): Promise<void>;
40
+ /** 停止服务器 */
23
41
  stop(): void;
24
42
  }
package/dist/server.js CHANGED
@@ -1,38 +1,41 @@
1
1
  import { Logger } from './logger.js';
2
2
  import { Session } from './session.js';
3
3
  import http from 'http';
4
- import * as fs from 'fs';
5
- import * as path from 'path';
6
4
  import { URL } from 'url';
7
- import * as mime from 'mime-types';
8
5
  import { resolveVirtualAsset } from '@yumerijs/types';
9
6
  const logger = new Logger('server');
7
+ /** 判断是否为流对象 */
10
8
  function isStream(value) {
11
9
  return value && typeof value.pipe === "function";
12
10
  }
11
+ /**
12
+ * 核心服务器类
13
+ * 负责底层 HTTP 请求的监听、分发和响应
14
+ */
13
15
  export class Server {
14
16
  core;
15
17
  port;
16
18
  host;
17
19
  enableCors;
18
- staticDir;
19
20
  httpServer = null;
20
21
  constructor(core, config = {}) {
21
22
  this.core = core;
22
23
  this.port = config.port ?? 14510;
23
24
  this.host = config.host ?? '0.0.0.0';
24
25
  this.enableCors = config.enableCors ?? true;
25
- this.staticDir = config.staticDir ?? 'static';
26
- }
27
- getStaticDir() {
28
- return this.staticDir;
29
26
  }
27
+ /**
28
+ * 创建一个新的会话对象
29
+ */
30
30
  createSession(ip, cookies, res, req, pathname, pluginContext, extra = {}) {
31
31
  const session = new Session(ip, cookies, this, req, res, pathname, undefined, pluginContext);
32
32
  Object.assign(session.properties, extra);
33
33
  session.protocol = extra.protocol ?? 'http';
34
34
  return session;
35
35
  }
36
+ /**
37
+ * 解析请求中的 Cookie
38
+ */
36
39
  parseCookies(req) {
37
40
  const cookieHeader = req.headers.cookie;
38
41
  if (!cookieHeader)
@@ -44,25 +47,19 @@ export class Server {
44
47
  });
45
48
  return cookies;
46
49
  }
50
+ /**
51
+ * 获取客户端真实 IP
52
+ */
47
53
  getClientIP(req) {
48
54
  const xff = req.headers['x-forwarded-for'];
49
55
  return xff ? xff.split(',')[0].trim() : req.socket.remoteAddress || '127.0.0.1';
50
56
  }
51
- serveStaticFile(pathname, res) {
52
- const fullPath = path.join(process.cwd(), this.staticDir, pathname);
53
- fs.readFile(fullPath, (err, data) => {
54
- if (err) {
55
- res.writeHead(404, { 'Content-Type': 'text/html' });
56
- res.end(`<html><body><h1>404 Not Found</h1></body></html>`);
57
- }
58
- else {
59
- res.writeHead(200, { 'Content-Type': mime.lookup(fullPath) || 'text/plain' });
60
- res.end(data);
61
- }
62
- });
63
- }
57
+ /**
58
+ * 启动服务器
59
+ */
64
60
  async start() {
65
61
  this.httpServer = http.createServer(async (req, res) => {
62
+ // 处理跨域预检请求
66
63
  if (req.method === 'OPTIONS' && this.enableCors) {
67
64
  res.writeHead(204, {
68
65
  'Access-Control-Allow-Origin': '*',
@@ -77,6 +74,7 @@ export class Server {
77
74
  const queryParams = url.searchParams;
78
75
  const ip = this.getClientIP(req);
79
76
  const cookies = this.parseCookies(req);
77
+ // 处理虚拟资产加载
80
78
  if (req.method === 'GET' || req.method === 'HEAD') {
81
79
  const virtualAsset = await resolveVirtualAsset(pathname);
82
80
  if (virtualAsset) {
@@ -92,14 +90,18 @@ export class Server {
92
90
  const pluginContext = route ? route.context : (rootroute ? rootroute.context : undefined);
93
91
  const session = this.createSession(ip, cookies, res, req, pathname, pluginContext, { protocol: 'http', header: req.headers });
94
92
  session._startAt = Date.now();
93
+ /** 路由处理逻辑 */
95
94
  const handleRoute = async (routePath) => {
96
95
  const matched = await this.core.executeRoute(routePath, session, queryParams);
97
96
  if (!matched) {
98
- this.serveStaticFile(pathname, res);
97
+ // 核心层不再提供静态文件服务,直接返回 404
98
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
99
+ res.end('Not Found');
99
100
  return;
100
101
  }
101
102
  if (session.responseHandled)
102
103
  return;
104
+ // 组装响应头
103
105
  const head = { ...session.head };
104
106
  head['Set-Cookie'] = Object.entries(session.newCookie).map(([name, cookie]) => {
105
107
  let cookieString = `${name}=${cookie.value}`;
@@ -121,6 +123,7 @@ export class Server {
121
123
  head['Access-Control-Allow-Origin'] = '*';
122
124
  }
123
125
  res.writeHead(session.status ?? 200, head);
126
+ // 根据响应类型输出内容
124
127
  switch (session.restype) {
125
128
  case 'plain':
126
129
  res.end(session.body);
@@ -138,9 +141,10 @@ export class Server {
138
141
  }
139
142
  break;
140
143
  default:
141
- res.end(session.body); // fallback
144
+ res.end(session.body);
142
145
  }
143
146
  };
147
+ // 分发请求
144
148
  if (route && route.allowedMethods.includes(req.method ?? 'GET')) {
145
149
  await handleRoute(pathname);
146
150
  }
@@ -148,9 +152,11 @@ export class Server {
148
152
  await handleRoute('root');
149
153
  }
150
154
  else {
151
- this.serveStaticFile(pathname, res);
155
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
156
+ res.end('Not Found');
152
157
  }
153
158
  });
159
+ // 处理 WebSocket 升级
154
160
  this.httpServer.on('upgrade', async (req, socket, head) => {
155
161
  const url = new URL(req.url || '/', `http://${req.headers.host}`);
156
162
  const pathname = url.pathname;
@@ -163,9 +169,7 @@ export class Server {
163
169
  if (route && route.ws != null) {
164
170
  const matched = await this.core.executeRoute(pathname, session, queryParams);
165
171
  if (!matched) {
166
- socket.write('HTTP/1.1 400 Bad Request\r\n' +
167
- 'Content-Type: text/plain\r\n' +
168
- 'Connection: close\r\n');
172
+ socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
169
173
  socket.destroy();
170
174
  }
171
175
  else {
@@ -175,9 +179,7 @@ export class Server {
175
179
  }
176
180
  }
177
181
  else {
178
- socket.write('HTTP/1.1 400 Bad Request\r\n' +
179
- 'Content-Type: text/plain\r\n' +
180
- 'Connection: close\r\n');
182
+ socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
181
183
  socket.destroy();
182
184
  }
183
185
  });
@@ -185,6 +187,7 @@ export class Server {
185
187
  logger.info(`Server listening on ${this.host}:${this.port}`);
186
188
  });
187
189
  }
190
+ /** 停止服务器 */
188
191
  stop() {
189
192
  if (this.httpServer)
190
193
  this.httpServer.close(() => logger.info('Server stopped'));
package/dist/session.d.ts CHANGED
@@ -7,152 +7,253 @@ import { Server } from './server.js';
7
7
  import { IncomingMessage, ServerResponse } from 'http';
8
8
  import { Stream } from "stream";
9
9
  import { Context } from './context.js';
10
+ /** 解析后的请求参数类型,支持字符串或字符串数组 */
10
11
  type ParsedParams = Record<string, string | string[] | undefined>;
11
- type MissingMode = 'keep-template' | 'keep-key' | 'remove';
12
+ /** 客户端对象,包含原始的 HTTP 请求和响应对象 */
12
13
  export interface Client {
14
+ /** 原始的 Node.js HTTP 请求对象 */
13
15
  req: IncomingMessage;
16
+ /** 原始的 Node.js HTTP 响应对象 */
14
17
  res: ServerResponse;
18
+ /** 经过处理后的规范化请求头 */
15
19
  headers?: Record<string, string>;
16
20
  }
21
+ /** 设置 Cookie 时的配置项 */
17
22
  export interface CookieOptions {
23
+ /** Cookie 的过期时间 */
18
24
  expires?: Date;
25
+ /** Cookie 的有效路径,默认为 '/' */
19
26
  path?: string;
27
+ /** Cookie 的有效域名 */
20
28
  domain?: string;
29
+ /** 是否仅在 HTTPS 连接中传输 */
21
30
  secure?: boolean;
31
+ /** 是否禁止客户端脚本访问该 Cookie */
22
32
  httpOnly?: boolean;
33
+ /** 控制 Cookie 是否随跨站请求发送 */
23
34
  sameSite?: 'Strict' | 'Lax' | 'None';
24
35
  }
36
+ /** 响应缓存的配置项 */
25
37
  export interface CacheOptions {
38
+ /** 强缓存标识 ETag */
26
39
  etag?: string;
40
+ /** 资源最后修改时间 */
27
41
  modified?: Date;
42
+ /** 浏览器缓存最大时长(秒) */
28
43
  maxAge?: number;
44
+ /** 代理服务器缓存最大时长(秒) */
29
45
  smaxAge?: number;
46
+ /** 标识资源是否永不改变 */
30
47
  isImmutable?: boolean;
48
+ /** 显式的过期时间 */
31
49
  expires?: Date;
50
+ /** 缓存控制的具体策略 */
32
51
  cacheControl?: 'public' | 'private' | 'no-cache' | 'no-store' | 'must-revalidate' | 'proxy-revalidate';
33
52
  }
53
+ /** 静态文件专用的缓存配置 */
34
54
  export interface StaticCacheOptions {
55
+ /** 是否启用 ETag 生成 */
35
56
  etag?: boolean;
57
+ /** 最大缓存时长 */
36
58
  maxAge?: number;
59
+ /** 代理缓存时长 */
37
60
  smaxAge?: number;
61
+ /** 计算 ETag 使用的哈希算法 */
38
62
  etagType?: 'md5' | 'sha1' | 'sha256' | 'sha512';
39
63
  }
64
+ /** 响应体的数据类型分类 */
40
65
  type ResType = "plain" | "json" | "stream" | "buffer";
66
+ /** 响应类型与具体数据结构的映射表 */
41
67
  interface BodyMap {
42
68
  plain: string;
43
69
  json: Record<string, any>;
44
70
  stream: Stream;
45
71
  buffer: Buffer;
46
72
  }
47
- export declare class Session {
73
+ /**
74
+ * 会话请求包装类
75
+ * 负责收集和整理所有来自客户端的请求信息
76
+ */
77
+ declare class SessionRequest {
78
+ /** 客户端的 IP 地址 */
48
79
  ip: string;
49
- cookie: Record<string, string>;
80
+ /** 请求中携带的原始 Cookie 键值对 */
81
+ cookies: Record<string, string>;
82
+ /** URL 中的查询字符串参数 */
50
83
  query: Record<string, string> | undefined;
51
- sessionid: string;
52
- data: Record<string, any>;
53
- newCookie: Record<string, {
84
+ /** 请求的资源路径(不含查询参数) */
85
+ pathname: string;
86
+ /** 所有的请求头信息 */
87
+ headers: Record<string, string>;
88
+ /** 客户端支持的语言列表,已按权重排序 */
89
+ languages: string[];
90
+ /** 请求使用的协议 */
91
+ protocol: string;
92
+ /** 原始的 IncomingMessage 对象 */
93
+ raw: IncomingMessage;
94
+ constructor(data: {
95
+ ip: string;
96
+ cookies: Record<string, string>;
97
+ query?: Record<string, string>;
98
+ pathname?: string;
99
+ headers: Record<string, string>;
100
+ raw: IncomingMessage;
101
+ });
102
+ }
103
+ /**
104
+ * 会话响应包装类
105
+ * 负责管理和准备发往客户端的数据
106
+ */
107
+ declare class SessionResponse {
108
+ /** 待发送的 HTTP 状态码 */
109
+ status: number;
110
+ /** 待发送的响应头 */
111
+ headers: Record<string, any>;
112
+ /** 准备设置到客户端的 Cookie 集合 */
113
+ cookies: Record<string, {
54
114
  value: string;
55
115
  options: CookieOptions;
56
116
  }>;
57
- head: Record<string, any>;
58
- status: number;
59
- private _restype;
60
- private _body;
61
- properties?: Record<string, any>;
62
- client: Client;
117
+ /** 响应体的具体类型 */
118
+ type: ResType;
119
+ /** 响应的具体内容 */
120
+ body: any;
121
+ /** 标识该响应是否已经处理完毕 */
122
+ handled: boolean;
123
+ /** 原始的 ServerResponse 对象 */
124
+ raw: ServerResponse;
125
+ constructor(res: ServerResponse);
126
+ }
127
+ /**
128
+ * 会话对象 (Session)
129
+ * 是插件开发中最核心的对象,封装了请求、响应及各种便捷工具
130
+ */
131
+ export declare class Session {
132
+ /** 封装后的请求对象 */
133
+ request: SessionRequest;
134
+ /** 封装后的响应对象 */
135
+ response: SessionResponse;
136
+ /** 当前会话的唯一标识符 (UUID) */
137
+ sessionid: string;
138
+ /** 持久化的会话数据存储 */
139
+ data: Record<string, any>;
140
+ /** 供插件或中间件挂载的临时属性 */
141
+ properties: Record<string, any>;
142
+ /** 对核心服务器实例的引用 */
63
143
  server: Server;
64
- protocol: string;
65
- pathname: string;
66
- languages: string[];
67
- responseHandled: boolean;
144
+ /** 当前插件的上下文环境 */
68
145
  pluginContext: Context | undefined;
69
146
  /**
70
- * @constructor
71
- * @param ip 用户IP
72
- * @param cookie 会话cookie
73
- * @param query 请求字符串
147
+ * 初始化一个新的会话
148
+ * @param ip 客户端IP
149
+ * @param cookie 请求Cookie
150
+ * @param server 服务器实例
151
+ * @param req 原始请求对象
152
+ * @param res 原始响应对象
153
+ * @param pathname 请求路径
154
+ * @param query 查询参数
155
+ * @param pluginContext 插件上下文
74
156
  */
75
157
  constructor(ip: string, cookie: Record<string, string>, server: Server, req?: IncomingMessage, res?: ServerResponse, pathname?: string, query?: Record<string, string>, pluginContext?: Context);
76
- response<T extends ResType>(body: BodyMap[T], type?: T): void;
158
+ /** 获取客户端 IP 地址 */
159
+ get ip(): string;
160
+ /** 获取请求中的 Cookie 集合 */
161
+ get cookie(): Record<string, string>;
162
+ /** 获取 URL 查询参数 */
163
+ get query(): Record<string, string>;
164
+ /** 获取请求路径 */
165
+ get pathname(): string;
166
+ /** 获取语言列表 */
167
+ get languages(): string[];
168
+ /** 获取请求协议 */
169
+ get protocol(): string;
170
+ set protocol(val: string);
171
+ /** 获取或设置响应状态码 */
172
+ get status(): number;
173
+ set status(val: number);
174
+ /** 获取待发送的新 Cookie */
175
+ get newCookie(): Record<string, {
176
+ value: string;
177
+ options: CookieOptions;
178
+ }>;
179
+ /** 获取响应头对象 */
180
+ get head(): Record<string, any>;
181
+ /** 获取当前响应内容类型 */
182
+ get _restype(): ResType;
183
+ /** 获取响应是否已处理的标记 */
184
+ get responseHandled(): boolean;
185
+ set responseHandled(val: boolean);
186
+ /** 组装并获取 Client 兼容对象 */
187
+ get client(): Client;
188
+ /**
189
+ * 设置响应体内容
190
+ * @param body 响应内容
191
+ * @param type 内容类型,默认为 'plain'
192
+ */
193
+ respond<T extends ResType>(body: BodyMap[T], type?: T): void;
194
+ /** 获取响应类型 */
77
195
  get restype(): ResType;
78
- get body(): string;
79
- set body(value: string);
196
+ /** 获取响应体内容 */
197
+ get body(): any;
198
+ /** 设置响应体内容,自动推断类型 */
199
+ set body(value: any);
200
+ /**
201
+ * 设置响应 Cookie
202
+ * @param name Cookie 键名
203
+ * @param value Cookie 值
204
+ * @param options 配置项
205
+ */
80
206
  setCookie(name: string, value: string, options?: CookieOptions): void;
81
207
  /**
82
- * 解析 Accept-Language 字符串为排序后的语言数组
83
- * @param header Accept-Language 头,比如 "zh-CN,zh-TW;q=0.8,en-US;q=0.6"
84
- * @returns 按优先级排序的语言数组,自动补上父语言
208
+ * 解析 Accept-Language 请求头
209
+ * @param header 原始头字符串
210
+ * @returns 排序后的语言数组
85
211
  */
86
- parseAcceptLanguages(header: string): any[];
212
+ parseAcceptLanguages(header: string): string[];
87
213
  /**
88
- * 生成 sessionid
89
- * @param ip 用户IP
90
- * @param option 选项
214
+ * 生成会话 ID (UUID)
91
215
  */
92
216
  private generateId;
93
217
  /**
94
- * 解析请求体
95
- * @param client 客户端
96
- * @returns Promise<ParsedParams>
218
+ * 异步解析请求体
219
+ * @returns 返回解析后的参数
97
220
  */
98
221
  parseRequestBody(client?: Client): Promise<ParsedParams>;
99
222
  /**
100
- * 获取请求体
101
- * @param req 请求对象
102
- * @returns Promise<string>
223
+ * 获取请求体文本
103
224
  */
104
225
  getReqBody(req: IncomingMessage): Promise<string>;
226
+ /**
227
+ * MD5 加密
228
+ */
105
229
  private md5;
230
+ /** 设置会话数据 */
106
231
  setData(key: string, value: any): void;
232
+ /** 删除会话数据 */
107
233
  deleteData(key: string): void;
234
+ /** 清空会话数据 */
108
235
  clearData(): void;
236
+ /** 销毁会话 */
109
237
  destroy(): void;
110
- setMime(mimeType: 'png' | 'jpg' | 'jpeg' | 'pdf' | 'plain' | 'html' | 'json' | 'xml' | string): void;
238
+ /** 设置 MIME 类型 */
239
+ setMime(mimeType: string): void;
240
+ /** 向响应流写入数据 */
111
241
  send(data: any): any;
242
+ /** 结束响应 */
112
243
  endsession(message: any): any;
113
- /**
114
- * 获取I18n文本
115
- * @param name 文本点名称
116
- */
244
+ /** 获取国际化文本 */
117
245
  text(name: string): any;
118
- /**
119
- * 设置缓存标头
120
- * @param option 选项
121
- */
246
+ /** 设置缓存标头 */
122
247
  setCache(option: CacheOptions): void;
123
- /**
124
- * 设置静态文件
125
- * @param content 文件内容
126
- * @param option 选项
127
- */
248
+ /** 发送静态内容并处理缓存 */
128
249
  static(content: string, option: CacheOptions): void;
129
- /**
130
- * 发送静态文件
131
- * @param path 文件路径
132
- * @param option 缓存选项
133
- * @returns void
134
- */
250
+ /** 发送文件并处理缓存 */
135
251
  file(path: string, option: StaticCacheOptions): void;
136
- /**
137
- * 发送普通文件
138
- * @param path 文件路径
139
- * @param isStream 是否流式传输
140
- */
252
+ /** 直接发送文件 */
141
253
  sendFile(path: string, isStream?: boolean): void;
142
- /**
143
- * 渲染模板
144
- * @template 模板字符串
145
- * @data 数据
146
- * @missing 缺失值处理方式,keep-template: 保留模板,keep-key: 保留键,remove: 移除
147
- * @regex 模板匹配正则,默认为{{ xxx.xxx }}
148
- */
149
- render(template: string, data: Record<string, any>, missing?: MissingMode, regex?: RegExp): string;
150
- /**
151
- * Renders a UI component using the plugin's declared renderer.
152
- * @param component The component object to render.
153
- * @param data The data/props to pass to the component.
154
- */
254
+ /** 渲染组件视图 */
155
255
  renderView(component: any, data?: Record<string, any>): Promise<void>;
156
- renderFile(filePath: string, data?: any): Promise<void>;
256
+ /** 渲染文件 */
257
+ render(filePath: string, data?: any): Promise<void>;
157
258
  }
158
259
  export {};