@yumerijs/core 2.2.2 → 3.0.0-alpha.1

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/context.d.ts CHANGED
@@ -10,6 +10,7 @@ interface Plugin {
10
10
  provide: Array<string>;
11
11
  }
12
12
  export interface Components {
13
+ [key: string]: any;
13
14
  }
14
15
  /**
15
16
  * 插件上下文对象
@@ -27,7 +28,7 @@ export declare class Context {
27
28
  private i18ns;
28
29
  component: Components;
29
30
  renderer?: IRenderer;
30
- instance: any;
31
+ module: any;
31
32
  childpath: string;
32
33
  /** 插件名称 */
33
34
  pluginname: string;
@@ -36,7 +37,7 @@ export declare class Context {
36
37
  * @param core Core 实例
37
38
  * @param pluginname 插件名称
38
39
  */
39
- constructor(core: Core, pluginname: string, instance?: any, injections?: Record<string, any>);
40
+ constructor(core: Core, pluginname: string, module?: any, injections?: Record<string, any>);
40
41
  /**
41
42
  * 注入依赖
42
43
  * @param name 依赖名称
@@ -101,10 +102,16 @@ export declare class Context {
101
102
  fork(name?: string, path?: string): Context;
102
103
  /**
103
104
  * 注册子插件
104
- * @param plugin 插件实例
105
+ * @param module 插件模块
105
106
  * @param config 插件配置
106
107
  */
107
- apply(plugin: Plugin, config: any): Promise<void>;
108
+ apply(module: Plugin, config: any): Promise<void>;
109
+ /**
110
+ * 快速注册子插件(自动 fork)
111
+ * @param module 插件模块
112
+ * @param config 插件配置
113
+ */
114
+ plugin(module: Plugin, config?: any): Promise<Context>;
108
115
  /**
109
116
  * 注册 i18n
110
117
  * @param content 内容(可以是嵌套对象或单个key)
package/dist/context.js CHANGED
@@ -16,7 +16,7 @@ export class Context {
16
16
  i18ns = [];
17
17
  component;
18
18
  renderer;
19
- instance;
19
+ module;
20
20
  childpath = '/';
21
21
  /** 插件名称 */
22
22
  pluginname;
@@ -25,9 +25,9 @@ export class Context {
25
25
  * @param core Core 实例
26
26
  * @param pluginname 插件名称
27
27
  */
28
- constructor(core, pluginname, instance, injections = {}) {
28
+ constructor(core, pluginname, module, injections = {}) {
29
29
  this.core = core;
30
- this.instance = instance;
30
+ this.module = module;
31
31
  this.pluginname = pluginname;
32
32
  this.childPlugins = new Map();
33
33
  this.component = injections;
@@ -145,15 +145,28 @@ export class Context {
145
145
  }
146
146
  /**
147
147
  * 注册子插件
148
- * @param plugin 插件实例
148
+ * @param module 插件模块
149
149
  * @param config 插件配置
150
150
  */
151
- async apply(plugin, config) {
152
- if (!plugin || !plugin.apply)
151
+ async apply(module, config) {
152
+ if (!module)
153
153
  return;
154
154
  const ctx = this.fork();
155
- await plugin.apply(ctx, config);
156
- this.childPlugins.set(ctx, plugin);
155
+ await this.core.plugin(module, ctx, config);
156
+ this.childPlugins.set(ctx, module);
157
+ }
158
+ /**
159
+ * 快速注册子插件(自动 fork)
160
+ * @param module 插件模块
161
+ * @param config 插件配置
162
+ */
163
+ async plugin(module, config = {}) {
164
+ if (!module)
165
+ return;
166
+ const ctx = this.fork();
167
+ await this.core.plugin(module, ctx, config);
168
+ this.childPlugins.set(ctx, module);
169
+ return ctx;
157
170
  }
158
171
  /**
159
172
  * 注册 i18n
package/dist/core.d.ts CHANGED
@@ -16,12 +16,12 @@ interface Plugin {
16
16
  provide: Array<string>;
17
17
  }
18
18
  export interface CoreOptions {
19
- port: number;
20
- host: string;
21
- staticDir: string;
22
- enableCors: boolean;
23
- enableWs: boolean;
24
- lang: string[];
19
+ port?: number;
20
+ host?: string;
21
+ enableCors?: boolean;
22
+ enableWs?: boolean;
23
+ lang?: string[];
24
+ skipcheckUpdates?: boolean;
25
25
  }
26
26
  export declare const coreConfigSchema: Schema<CoreOptions>;
27
27
  export declare const enum PluginStatus {
@@ -44,12 +44,13 @@ export declare class Core {
44
44
  loader: any;
45
45
  renderers: Map<string, IRenderer>;
46
46
  pluginRenderers: Map<string, string>;
47
- constructor(loader?: any, coreConfig?: CoreOptions, setCore?: boolean);
47
+ constructor(loader?: any, coreConfig?: CoreOptions, loggersetCore?: boolean, splash?: boolean);
48
+ checkUpdate(): Promise<void>;
48
49
  addRenderer(renderer: IRenderer): void;
49
50
  getRendererForPlugin(pluginName: string): string | undefined;
50
51
  runCore(): Promise<void>;
51
52
  getShortPluginName(pluginName: string): string;
52
- plugin(pluginInstance: Plugin, context: Context, config: Config): Promise<void>;
53
+ plugin(module: Plugin, context: Context, config: Config): Promise<void>;
53
54
  registerComponent(name: string, component: any): void;
54
55
  getComponent(name: string): any;
55
56
  unregisterComponent(name: string): void;
package/dist/core.js CHANGED
@@ -1,16 +1,18 @@
1
1
  import { EventEmitter } from 'events';
2
- import { Schema } from './config.js';
2
+ import { Schema, fallback } from './config.js';
3
3
  import { Logger } from './logger.js';
4
4
  import { Route } from './route.js';
5
5
  import { Hook } from './hook.js';
6
6
  import { Server as CoreServer } from './server.js';
7
+ import * as fs from 'fs';
8
+ const version = JSON.parse(await fs.promises.readFile(new URL('../package.json', import.meta.url), 'utf-8')).version;
7
9
  export const coreConfigSchema = Schema.object({
8
10
  port: Schema.number('监听端口').default(14510),
9
11
  host: Schema.string('监听地址').default('0.0.0.0'),
10
- staticDir: Schema.string('静态目录').default('public'),
11
12
  enableCors: Schema.boolean('启用跨域').default(false),
12
13
  enableWs: Schema.boolean('启用 WebSocket').default(false),
13
14
  lang: Schema.array(Schema.string(), '语言列表').default(['zh', 'en']),
15
+ skipcheckUpdates: Schema.boolean('启动时检查更新').default(false)
14
16
  });
15
17
  export class Core {
16
18
  emitter = new EventEmitter();
@@ -25,12 +27,32 @@ export class Core {
25
27
  loader;
26
28
  renderers = new Map();
27
29
  pluginRenderers = new Map(); // Stores which plugin uses which renderer
28
- constructor(loader, coreConfig, setCore = true) {
29
- this.coreConfig = coreConfig || {};
30
+ constructor(loader, coreConfig = {}, loggersetCore = true, splash = true) {
31
+ this.coreConfig = fallback(coreConfigSchema, coreConfig);
30
32
  this.loader = loader;
31
- if (setCore)
33
+ if (splash)
34
+ this.logger.info('Welcome to use Yumeri ver.' + version);
35
+ if (!this.coreConfig.skipcheckUpdates)
36
+ this.checkUpdate();
37
+ if (loggersetCore)
32
38
  Logger.setCore(this);
33
39
  }
40
+ async checkUpdate() {
41
+ try {
42
+ const response = await fetch('https://registry.npmjs.org/yumeri/latest');
43
+ if (!response.ok)
44
+ return;
45
+ const { version: latest } = await response.json();
46
+ if (latest !== version) {
47
+ this.logger.info(`There is new version for Yumeri: ${version} -> ${latest}`);
48
+ }
49
+ else {
50
+ }
51
+ }
52
+ catch (error) {
53
+ this.logger.error('Error while checking updates: ', error.message);
54
+ }
55
+ }
34
56
  addRenderer(renderer) {
35
57
  if (this.renderers.has(renderer.name)) {
36
58
  this.logger.warn(`Renderer "${renderer.name}" is already registered and will be overwritten.`);
@@ -46,7 +68,6 @@ export class Core {
46
68
  host: this.coreConfig.host || '0.0.0.0',
47
69
  enableCors: this.coreConfig.enableCors || false,
48
70
  enableWs: this.coreConfig.enableWs || false,
49
- staticDir: this.coreConfig.staticDir || 'public',
50
71
  });
51
72
  await this.server.start();
52
73
  }
@@ -60,12 +81,20 @@ export class Core {
60
81
  }
61
82
  return pluginName;
62
83
  }
63
- async plugin(pluginInstance, context, config) {
84
+ async plugin(module, context, config) {
64
85
  const shortName = this.getShortPluginName(context.pluginname);
65
- context.instance = pluginInstance;
86
+ context.module = module;
87
+ // 自动依赖注入
88
+ const depend = module.depend || [];
89
+ for (const name of depend) {
90
+ const component = this.getComponent(name);
91
+ if (component) {
92
+ context.inject(name, component);
93
+ }
94
+ }
66
95
  this.logger.info(`apply plugin ${shortName}`);
67
- if (pluginInstance.apply) {
68
- await pluginInstance.apply(context, config);
96
+ if (module.apply) {
97
+ await module.apply(context, config);
69
98
  }
70
99
  }
71
100
  registerComponent(name, component) {
@@ -123,7 +152,7 @@ export class Core {
123
152
  async executeRoute(pathname, session, queryParams) {
124
153
  for (const routePath in this.routes) {
125
154
  const route = this.routes[routePath];
126
- const result = route.match(pathname);
155
+ const result = route.match(pathname, session.client?.headers?.host);
127
156
  if (result) {
128
157
  try {
129
158
  this.emit('request:start', {
@@ -151,7 +180,7 @@ export class Core {
151
180
  }
152
181
  else {
153
182
  const start = Date.now();
154
- await route.executeHandler(session, queryParams, result.pathParams);
183
+ await route.executeHandler(session, queryParams, result.pathParams, result.hostParams);
155
184
  this.emit('route:end', {
156
185
  path: pathname,
157
186
  route: routePath,
package/dist/route.d.ts CHANGED
@@ -25,6 +25,7 @@ export declare class Route {
25
25
  allowedMethods: string[];
26
26
  ws: WebSocketServer;
27
27
  context: Context;
28
+ private routehost;
28
29
  /**
29
30
  * 创建路由
30
31
  * @param path 路由路径
@@ -37,6 +38,12 @@ export declare class Route {
37
38
  * @returns this
38
39
  */
39
40
  action(handler: RouteHandler): this;
41
+ /**
42
+ * 设置允许的host
43
+ * @param host host列表
44
+ * @returns this
45
+ */
46
+ host(host: string[] | string): this;
40
47
  /**
41
48
  * 挂载中间件
42
49
  * @param middleware 中间件
@@ -46,11 +53,13 @@ export declare class Route {
46
53
  /**
47
54
  * 匹配路由
48
55
  * @param pathname 请求路径
56
+ * @param host 请求host
49
57
  * @returns 匹配结果
50
58
  */
51
- match(pathname: string): {
59
+ match(pathname: string, host?: string): {
52
60
  params: Record<string, string | undefined>;
53
61
  pathParams: string[];
62
+ hostParams: Record<string, string>;
54
63
  } | null;
55
64
  /**
56
65
  * 执行路由处理器
@@ -58,7 +67,7 @@ export declare class Route {
58
67
  * @param params 查询参数
59
68
  * @param pathParams 路由参数
60
69
  */
61
- executeHandler(session: Session, params: URLSearchParams, pathParams: string[]): Promise<void>;
70
+ executeHandler(session: Session, params: URLSearchParams, pathParams: string[], hostParams: Record<string, string>): Promise<void>;
62
71
  /**
63
72
  * 设置可用方法
64
73
  * @param methods 可用方法
package/dist/route.js CHANGED
@@ -22,6 +22,23 @@ function parsePatternToSegments(pattern) {
22
22
  }
23
23
  return { segments, params };
24
24
  }
25
+ function parseHostToSegments(hostPattern) {
26
+ // 移除可能的空格,按 . 分割
27
+ const parts = hostPattern.split('.');
28
+ return parts.map(part => {
29
+ // 简单的参数解析逻辑
30
+ if (part.startsWith(':')) {
31
+ const lastChar = part[part.length - 1];
32
+ const hasModifier = ['?', '+', '*'].includes(lastChar);
33
+ return {
34
+ type: 'param',
35
+ name: hasModifier ? part.slice(1, -1) : part.slice(1),
36
+ modifier: hasModifier ? lastChar : '',
37
+ };
38
+ }
39
+ return { type: 'static', value: part };
40
+ });
41
+ }
25
42
  /**
26
43
  * Route class using segment-based matching (no fragile capture-group-index mapping).
27
44
  * Behavior matches the SimpleRouter in the webpage:
@@ -39,6 +56,7 @@ export class Route {
39
56
  allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'];
40
57
  ws = null;
41
58
  context;
59
+ routehost = null;
42
60
  /**
43
61
  * 创建路由
44
62
  * @param path 路由路径
@@ -60,6 +78,20 @@ export class Route {
60
78
  this.handler = handler;
61
79
  return this;
62
80
  }
81
+ /**
82
+ * 设置允许的host
83
+ * @param host host列表
84
+ * @returns this
85
+ */
86
+ host(host) {
87
+ if (typeof host === 'string') {
88
+ this.routehost = [host];
89
+ }
90
+ else {
91
+ this.routehost = host;
92
+ }
93
+ return this;
94
+ }
63
95
  /**
64
96
  * 挂载中间件
65
97
  * @param middleware 中间件
@@ -72,9 +104,117 @@ export class Route {
72
104
  /**
73
105
  * 匹配路由
74
106
  * @param pathname 请求路径
107
+ * @param host 请求host
75
108
  * @returns 匹配结果
76
109
  */
77
- match(pathname) {
110
+ match(pathname, host) {
111
+ let hostParams = {};
112
+ // Host 匹配逻辑开始
113
+ if (this.routehost && this.routehost.length > 0) {
114
+ if (!host)
115
+ return null; // 如果设置了 host 限制但没有传入 host,直接失败
116
+ const actualHost = host.toLowerCase().split(':')[0]; // 移除端口并转小写
117
+ const hostParts = actualHost.split('.');
118
+ let hostMatched = false;
119
+ for (const pattern of this.routehost) {
120
+ const hSegments = parseHostToSegments(pattern); // 这里的 parsePattern 应该和你 path 解析逻辑一致
121
+ let hi = 0; // pattern segment index
122
+ let hj = 0; // actual host parts index
123
+ let tempParams = {};
124
+ let possible = true;
125
+ while (hi < hSegments.length) {
126
+ const seg = hSegments[hi];
127
+ const nextSegIsStatic = !!hSegments[hi + 1] && hSegments[hi + 1].type === 'static';
128
+ if (seg.type === 'static') {
129
+ if (hj >= hostParts.length || hostParts[hj] !== seg.value.toLowerCase()) {
130
+ possible = false;
131
+ break;
132
+ }
133
+ hi++;
134
+ hj++;
135
+ }
136
+ else {
137
+ // 参数匹配逻辑 (:, ?, +, *)
138
+ switch (seg.modifier) {
139
+ case '': // required single
140
+ if (hj >= hostParts.length) {
141
+ possible = false;
142
+ break;
143
+ }
144
+ tempParams[seg.name] = hostParts[hj];
145
+ hi++;
146
+ hj++;
147
+ break;
148
+ case '?': // optional single
149
+ if (hj < hostParts.length) {
150
+ if (nextSegIsStatic && hostParts[hj] === hSegments[hi + 1].value.toLowerCase()) {
151
+ tempParams[seg.name] = undefined;
152
+ hi++;
153
+ }
154
+ else {
155
+ tempParams[seg.name] = hostParts[hj];
156
+ hj++;
157
+ hi++;
158
+ }
159
+ }
160
+ else {
161
+ tempParams[seg.name] = undefined;
162
+ hi++;
163
+ }
164
+ break;
165
+ case '+': // required multi
166
+ if (hj >= hostParts.length) {
167
+ possible = false;
168
+ break;
169
+ }
170
+ let endP = hostParts.length;
171
+ if (nextSegIsStatic) {
172
+ let found = hostParts.indexOf(hSegments[hi + 1].value.toLowerCase(), hj);
173
+ if (found === -1) {
174
+ possible = false;
175
+ break;
176
+ }
177
+ endP = found;
178
+ }
179
+ if (endP === hj) {
180
+ possible = false;
181
+ break;
182
+ }
183
+ tempParams[seg.name] = hostParts.slice(hj, endP).join('.');
184
+ hj = endP;
185
+ hi++;
186
+ break;
187
+ case '*': // optional multi
188
+ let endS = hostParts.length;
189
+ if (nextSegIsStatic) {
190
+ let found = hostParts.indexOf(hSegments[hi + 1].value.toLowerCase(), hj);
191
+ if (found !== -1)
192
+ endS = found;
193
+ }
194
+ const val = hostParts.slice(hj, endS).join('.');
195
+ tempParams[seg.name] = val === '' ? undefined : val;
196
+ hj = endS;
197
+ hi++;
198
+ break;
199
+ default:
200
+ possible = false;
201
+ break;
202
+ }
203
+ if (!possible)
204
+ break;
205
+ }
206
+ }
207
+ if (possible && hj === hostParts.length) {
208
+ hostMatched = true;
209
+ hostParams = tempParams;
210
+ break; // 只要匹配到一个 host pattern 就行
211
+ }
212
+ }
213
+ if (!hostMatched)
214
+ return null;
215
+ }
216
+ // Host 匹配逻辑结束
217
+ // 路由匹配逻辑开始
78
218
  if (pathname.startsWith('/')) {
79
219
  // normalize pathname: remove leading/trailing slashes
80
220
  const normPath = pathname.replace(/^\/+|\/+$/g, '');
@@ -198,10 +338,12 @@ export class Route {
198
338
  // after finishing pattern, path must be fully consumed (no extra segments)
199
339
  if (j !== parts.length)
200
340
  return null;
201
- return { params, pathParams };
341
+ const result = { params, pathParams, hostParams };
342
+ return result;
202
343
  }
203
344
  else if (pathname.startsWith('root') && this.path == pathname) {
204
- return { params: { pathname }, pathParams: [pathname] };
345
+ const result = { params: { pathname }, pathParams: [pathname], hostParams };
346
+ return result;
205
347
  }
206
348
  return null;
207
349
  }
@@ -211,9 +353,9 @@ export class Route {
211
353
  * @param params 查询参数
212
354
  * @param pathParams 路由参数
213
355
  */
214
- async executeHandler(session, params, pathParams) {
356
+ async executeHandler(session, params, pathParams, hostParams) {
215
357
  if (this.handler) {
216
- await this.handler(session, params, ...pathParams);
358
+ await this.handler(session, params, ...pathParams, ...Object.values(hostParams));
217
359
  }
218
360
  }
219
361
  /**
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
  }