mm_statics 1.9.1 → 1.9.3

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
@@ -9,7 +9,7 @@
9
9
  - 🔄 自动ES6到AMD格式转换
10
10
  - ⚡ Vue单文件组件直接转换(无需AMD包装)
11
11
  - 🛡️ 安全文件访问
12
- - 📦 轻量级中间件
12
+ - 🧩 服务式调用 `(params, ctx, options)`,不是中间件、不接收 `next`
13
13
  - 🔧 Vue 2/Vue 3兼容性支持
14
14
 
15
15
  ## 安装
@@ -23,29 +23,38 @@ npm install mm_statics
23
23
  ### 基本使用
24
24
 
25
25
  ```javascript
26
- const Koa = require('koa');
26
+ const path = require('path');
27
+ const { WebServer } = require('mm_web_server');
27
28
  const { statics } = require('mm_statics');
28
29
 
29
- const app = new Koa();
30
+ const web = new WebServer({ name: 'web', port: 3000 });
31
+ const events = web.getEventManager();
30
32
 
31
- // 使用默认配置(root 为 ./static)
32
- app.use(statics({ root: './static' }));
33
+ // 创建静态资源处理器:签名统一为 (params, ctx, options)
34
+ const serve = statics({ name: 'static', root: path.join(__dirname, 'static'), path: '/' });
33
35
 
34
- app.listen(3000, () => {
35
- console.log('服务器运行在 http://localhost:3000');
36
- });
36
+ // 在 before 阶段挂载:命中文件返回物理路径即短路后续事件,未命中返回空串继续执行
37
+ events.add({ name: 'static', target: '*', stage: 'before', end: true }).main =
38
+ async (params, ctx, options) => serve(params, ctx, options);
39
+
40
+ await web.init();
41
+ await web.start();
37
42
  ```
38
43
 
39
- ### 自定义配置
44
+ ### 调用约定
40
45
 
41
- ```javascript
42
- const Koa = require('koa');
43
- const { statics } = require('mm_statics');
46
+ | 项 | 说明 |
47
+ |---|---|
48
+ | 签名 | `(params, ctx, options)`,与事件管道、适配器保持一致 |
49
+ | 返回值 | 命中返回物理文件路径(字符串),未命中返回空串 |
50
+ | 无 `next` | 未命中也只是返回空串,是否继续由调用方决定,因此它不是中间件 |
51
+ | 响应写出 | 命中时已写出响应(`ctx.res` 直写或 `ctx.set` + `ctx.body`),并标记 `ctx.handled = true` |
44
52
 
45
- const app = new Koa();
53
+ ### 自定义配置
46
54
 
47
- // 自定义配置
48
- app.use(statics({
55
+ ```javascript
56
+ const serve = statics({
57
+ name: 'public', // 驱动名称(必填)
49
58
  root: './public', // 静态文件根目录
50
59
  path: '/src', // 需要转换的文件路径前缀
51
60
  files: ['.js', '.vue'], // 需要转换的文件类型
@@ -55,15 +64,17 @@ app.use(statics({
55
64
  immutable: true, // 是否启用不可变缓存
56
65
  hidden: false, // 是否支持隐藏文件
57
66
  index: 'index.html' // 默认索引文件
58
- }));
67
+ });
59
68
 
60
- app.listen(3000);
69
+ // 命中返回文件路径,未命中返回空串
70
+ const file = await serve({ path: '/src/app.js' }, ctx, {});
61
71
  ```
62
72
 
63
73
  ## 配置选项
64
74
 
65
75
  | 配置项 | 类型 | 默认值 | 描述 |
66
76
  |--------|------|--------|------|
77
+ | `name` | string | 无(必填) | 驱动名称 |
67
78
  | `root` | string | `''` | 静态文件根目录,为空时由 `dir` 推导,处理器兜底为 `./static` |
68
79
  | `path` | string | `''` | 路由根路径,为空时自动生成 `/${app}/` |
69
80
  | `files` | array | `['.js', '.vue', '.html']` | 需要转换的文件类型 |
@@ -86,50 +97,54 @@ app.listen(3000);
86
97
 
87
98
  ```javascript
88
99
  // 禁用AMD转换,仅提供静态文件服务
89
- app.use(statics({
100
+ const serve = statics({
101
+ name: 'public',
90
102
  convert_amd: false,
91
103
  compile_vue: false,
92
104
  root: './public'
93
- }));
105
+ });
94
106
  ```
95
107
 
96
108
  ### 2. ES6模块转换
97
109
 
98
110
  ```javascript
99
111
  // 启用AMD转换,自动转换ES6模块
100
- app.use(statics({
112
+ const serve = statics({
113
+ name: 'modules',
101
114
  convert_amd: true,
102
115
  compile_vue: false,
103
116
  root: './src',
104
117
  path: '/modules',
105
118
  files: ['.js', '.ts']
106
- }));
119
+ });
107
120
  ```
108
121
 
109
122
  ### 3. Vue单文件组件支持(AMD转换)
110
123
 
111
124
  ```javascript
112
125
  // 启用AMD转换,将Vue组件转换为AMD格式
113
- app.use(statics({
126
+ const serve = statics({
127
+ name: 'vue',
114
128
  convert_amd: true,
115
129
  compile_vue: true,
116
130
  root: './components',
117
131
  path: '/vue',
118
132
  files: ['.vue']
119
- }));
133
+ });
120
134
  ```
121
135
 
122
136
  ### 4. Vue单文件组件直接转换(无需AMD包装)
123
137
 
124
138
  ```javascript
125
139
  // 禁用AMD转换,但启用Vue编译,生成可直接使用的组件
126
- app.use(statics({
140
+ const serve = statics({
141
+ name: 'vue_component',
127
142
  convert_amd: false,
128
143
  compile_vue: true,
129
144
  root: './components',
130
145
  path: '/vue',
131
146
  files: ['.vue']
132
- }));
147
+ });
133
148
  ```
134
149
 
135
150
  ## Vue组件转换特性
@@ -218,13 +233,20 @@ new Static(config)
218
233
 
219
234
  #### 方法
220
235
 
221
- - `main(ctx, next)` - 主处理逻辑,作为中间件使用(路径取自 `ctx.path`)
222
- - `main(ctx, file_path)` - 直接发送指定文件
223
- - `run(ctx, next)` - 中间件方式处理静态文件
224
- - `send(ctx, file_path, config)` - 直接发送指定文件
236
+ - `main(params, ctx, options)` - 主处理逻辑,服务式调用;命中返回物理文件路径,未命中返回空串
237
+ - `run(params, ctx, options)` - 与 `main` 等价,委托给内部 `StaticHandle` 处理
238
+ - `send(ctx, file_path, config)` - 直接发送指定文件(低层 API,不参与请求分发)
225
239
  - `_delCache(url)` - 删除指定请求路径的服务端缓存
226
240
  - `init()` / `start()` / `stop()` - 生命周期方法(继承自 `mm_mod`)
227
241
 
242
+ ### StaticManager类
243
+
244
+ - `run(params, ctx, options)` - 依次尝试各驱动;已处理返回 `true`,未命中返回 `false`
245
+ - `add(config)` / `del(name)` / `reload(name)` - 驱动注册、注销、重启
246
+
247
+ > 说明:本模块已不再是 Koa/Express 风格的中间件,**不接收 `next` 参数**。
248
+ > 未命中时返回空串或 `false`,由调用方(事件管道、`OS._routeRequest` 等)决定是否继续后续处理。
249
+
228
250
  ## 缓存策略
229
251
 
230
252
  模块支持以下缓存策略:
@@ -243,12 +265,12 @@ new Static(config)
243
265
 
244
266
  ## 错误处理
245
267
 
246
- 模块会自动处理以下错误:
268
+ 模块不会自行决定最终状态码,命中失败一律返回空串(管理器返回 `false`),由调用方的响应写出器决定:
247
269
 
248
- - 文件不存在(返回404)
249
- - 权限不足(返回403)
250
- - 转换失败(返回500)
251
- - Vue解析失败(使用回退方案)
270
+ - 文件不存在 / 路径越界:捕获异常后返回空串,交由后续事件或响应层处理
271
+ - 非 GET/HEAD 请求:直接返回空串,不读取文件
272
+ - 转换失败:记录告警并在可用时回退原内容
273
+ - Vue解析失败:使用回退方案,避免整体请求失败
252
274
 
253
275
  ## 开发指南
254
276
 
@@ -306,6 +328,15 @@ ISC License
306
328
 
307
329
  ## 更新日志
308
330
 
331
+ ### 未发布(破坏性变更)
332
+
333
+ - **不再作为中间件**:移除 `next` 参数,`main` / `run` 统一为服务式签名 `(params, ctx, options)`
334
+ - 命中返回物理文件路径,未命中返回空串;`StaticManager.run` 返回布尔值表示是否已处理
335
+ - 仅处理 `GET` / `HEAD`,其它方法直接返回空串(不再读取文件系统)
336
+ - 删除失效的 `_handleRequest`(原实现引用了不存在的 `_handleGetRequest`)
337
+ - `StaticManager.run` 修正为读取注册表驱动列表(此前读 `configs` 恒为空,多驱动分发失效)
338
+ - 同步更新调用方:`mm_os._routeRequest`、`mm_web_server` 示例、模块示例与测试
339
+
309
340
  ### v1.5.2
310
341
  - 新增文件监听功能,支持文件修改时自动更新缓存
311
342
  - 集成chokidar模块实现稳定的文件监听
package/index.js CHANGED
@@ -1,35 +1,38 @@
1
- const Static = require('./lib/static');
2
- const StaticHandle = require('./lib/static_handle');
3
- const Manager = require('./lib/manager');
4
- const Admin = require('mm_corejs').Admin;
5
-
6
- const admin = new Admin();
7
- admin.setClass(Manager);
8
-
9
- module.exports = {
10
- Static,
11
- StaticHandle,
12
- Manager,
13
- admin,
14
- /**
15
- * 创建静态文件中间件工厂函数
16
- * @param {Object} config 静态文件配置
17
- * @returns {Function} 可直接挂载到 Koa / mm_web_server 的中间件函数
18
- */
19
- statics(config) {
20
- const instance = new Static(config);
21
- return instance.main.bind(instance);
22
- },
1
+ const Static = require('./lib/static');
2
+ const StaticHandle = require('./lib/static_handle');
3
+ const Manager = require('./lib/manager');
4
+ const Admin = require('mm_corejs').Admin;
5
+
6
+ const admin = new Admin();
7
+ admin.setClass(Manager);
8
+
9
+ module.exports = {
10
+ Static,
11
+ StaticHandle,
12
+ Manager,
13
+ admin,
14
+ /**
15
+ * 创建静态文件处理器(服务式调用,不是中间件,不接收 next)
16
+ *
17
+ * 返回函数签名 `(params, ctx, options)`:命中文件即写出响应并返回物理路径,
18
+ * 未命中返回空串,由事件管道自行决定是否继续执行后续事件。
19
+ * @param {Object} config 静态文件配置
20
+ * @returns {Function} 处理器函数 (params, ctx, options) => Promise<string>
21
+ */
22
+ statics(config) {
23
+ const instance = new Static(config);
24
+ return instance.main.bind(instance);
25
+ },
23
26
  /**
24
27
  * 获取静态文件管理器实例
25
28
  * @param {string} scope 作用域名称,为空时使用 os
26
29
  * @param {Object} config 管理器配置
27
30
  * @returns {Object} 静态文件管理器实例
28
31
  */
29
- staticAdmin(scope, config) {
30
- if (!scope) {
31
- scope = 'os';
32
- }
33
- return admin.get(scope, config);
34
- }
35
- };
32
+ staticAdmin(scope, config) {
33
+ if (!scope) {
34
+ scope = 'os';
35
+ }
36
+ return admin.get(scope, config);
37
+ }
38
+ };
package/lib/manager.js CHANGED
@@ -13,11 +13,6 @@ class StaticManager extends Manager {
13
13
  * @type {object}
14
14
  */
15
15
  static config = {
16
- /**
17
- * 名称
18
- * @type {string}
19
- */
20
- name: "",
21
16
  /**
22
17
  * 标题
23
18
  * @type {string}
@@ -55,57 +50,51 @@ class StaticManager extends Manager {
55
50
  }
56
51
 
57
52
  /**
58
- * 处理请求
59
- * @param {object} ctx Http请求上下文
60
- * @param {object} next 跳过函数
53
+ * 读取请求路径(优先取事件参数,其次取上下文)
54
+ * @param {object} params 请求参数
55
+ * @param {object} ctx 请求上下文
56
+ * @returns {string} 请求路径
61
57
  */
62
- async _handleRequest(ctx, next) {
63
- var md = ctx.method || (ctx.params && ctx.params.method) || '';
64
- var path = ctx.path || (ctx.params && ctx.params.path) || '';
65
- if (md === "GET" || md === "HEAD") {
66
- await this._handleGetRequest(ctx, next, path);
67
- } else {
68
- await next();
69
- }
58
+ _requestPath(params, ctx) {
59
+ return (params && params.path) || (ctx && ctx.path) || '';
70
60
  }
71
61
 
72
62
  /**
73
- * 处理GET请求
74
- * @param {object} ctx Http请求上下文
75
- * @param {object} next 跳过函数
63
+ * 执行请求(服务式调用,不是中间件,不接收 next)
64
+ * 命中静态文件即写出响应并返回 true;未命中返回 false,由上层继续执行后续事件
65
+ * @param {object} params 请求参数(可含 path / url / method)
66
+ * @param {object} ctx 请求上下文
67
+ * @param {object} options 运行选项
68
+ * @returns {Promise<boolean>} 已处理返回 true
76
69
  */
77
- async run(ctx, next) {
78
- var done;
70
+ async run(params, ctx, options) {
71
+ var path = this._requestPath(params, ctx);
79
72
  var infos = this._getInfos();
80
73
  var len = infos.length;
81
- let path = ctx.path || (ctx.params && ctx.params.path) || '';
82
74
  if (path.indexOf(".") !== -1) {
83
- done = await this._tryServeFile(ctx, infos, len, async () => {});
84
- if (!done) {
85
- await next();
86
- }
87
- } else {
88
- await next();
89
- await this._handleDirReq(ctx, infos, len, path);
75
+ return await this._tryServeFile(params, ctx, options, infos, len);
90
76
  }
77
+ return await this._handleDirReq(params, ctx, options, infos, len, path);
91
78
  }
92
79
 
93
80
  /**
94
81
  * 尝试提供文件服务
95
- * @param {object} ctx Http请求上下文
82
+ * @param {object} params 请求参数
83
+ * @param {object} ctx 请求上下文
84
+ * @param {object} options 运行选项
96
85
  * @param {Array} infos 驱动信息列表
97
86
  * @param {number} len 列表长度
98
- * @param {object} next 跳过函数
99
- * @returns {boolean} 是否成功
87
+ * @returns {Promise<boolean>} 是否命中
100
88
  */
101
- async _tryServeFile(ctx, infos, len, next) {
89
+ async _tryServeFile(params, ctx, options, infos, len) {
102
90
  for (var i = 0; i < len; i++) {
103
91
  var info = infos[i];
104
- if (info.state === 1) {
92
+ let is_running = await this.isRunning(info.name);
93
+ if (is_running) {
105
94
  let mod = this.get(info.name);
106
95
  if (mod) {
107
96
  await this._initDrive(mod);
108
- var done = await mod.run(ctx, next);
97
+ var done = await mod.run(params, ctx, options);
109
98
  if (done) {
110
99
  return true;
111
100
  }
@@ -117,32 +106,37 @@ class StaticManager extends Manager {
117
106
 
118
107
  /**
119
108
  * 处理目录请求
120
- * @param {object} ctx Http请求上下文
109
+ * @param {object} params 请求参数
110
+ * @param {object} ctx 请求上下文
111
+ * @param {object} options 运行选项
121
112
  * @param {Array} infos 驱动信息列表
122
113
  * @param {number} len 列表长度
123
114
  * @param {string} path 请求路径
115
+ * @returns {Promise<boolean>} 驱动已处理目录请求返回 true,仅重定向或未命中返回 false
124
116
  */
125
- async _handleDirReq(ctx, infos, len, path) {
117
+ async _handleDirReq(params, ctx, options, infos, len, path) {
126
118
  var q = this._getQueryString(ctx);
127
119
  for (var i = 0; i < len; i++) {
128
120
  var info = infos[i];
129
- if (info.state === 1) {
121
+ let is_running = await this.isRunning(info.name);
122
+ if (is_running) {
130
123
  let mod = this.get(info.name);
131
124
  if (mod) {
132
125
  var p = mod.config.path;
133
126
  if (path === p) {
134
127
  this._handleExactMatch(ctx, q, p);
135
- break;
128
+ return false;
136
129
  } else {
137
130
  await this._initDrive(mod);
138
- var done = await mod.run(ctx, () => {});
131
+ var done = await mod.run(params, ctx, options);
139
132
  if (done) {
140
- break;
133
+ return true;
141
134
  }
142
135
  }
143
136
  }
144
137
  }
145
138
  }
139
+ return false;
146
140
  }
147
141
 
148
142
  /**
@@ -171,9 +165,11 @@ class StaticManager extends Manager {
171
165
  _handleExactMatch(ctx, q, p) {
172
166
  // mm_web_server 原生 ctx 无 status 字段,未设置时视为 404 以兼容旧逻辑
173
167
  var status = ctx.status === undefined ? 404 : ctx.status;
174
- if (status === 404 && typeof ctx.redirect === 'function') {
168
+ // 驱动路径已带尾部斜杠时(如根路径 /),请求路径与驱动路径相同即已规范,无需重定向,避免自跳死循环
169
+ var need_redirect = status === 404 && typeof ctx.redirect === 'function' && !p.endsWith('/');
170
+ if (need_redirect) {
175
171
  // 驱动路径可能已带尾部斜杠,避免拼接出双斜杠
176
- var target = p.endsWith('/') ? p : p + '/';
172
+ var target = p + '/';
177
173
  if (q) {
178
174
  ctx.redirect(target + "?" + q);
179
175
  } else {
@@ -246,15 +242,13 @@ StaticManager.prototype._getInfos = function () {
246
242
  };
247
243
 
248
244
  /**
249
- * 初始化驱动实例(仅未初始化时执行生命周期 init,重复调用安全)
245
+ * 初始化驱动实例(重复调用安全由驱动自身保证)
250
246
  * @param {object} drive 驱动实例
251
247
  * @returns {Promise<void>} 初始化完成
252
248
  * @private
253
249
  */
254
250
  StaticManager.prototype._initDrive = async function (drive) {
255
- if (await drive.isState("created")) {
256
- await drive.init();
257
- }
251
+ await drive.init();
258
252
  };
259
253
 
260
254
  /**
package/lib/static.js CHANGED
@@ -17,8 +17,6 @@ class Static extends Item {
17
17
  'root': '',
18
18
  // 路由根路径
19
19
  'path': '',
20
- // 名称
21
- 'name': '',
22
20
  // 首页
23
21
  'index': 'index.html',
24
22
  // 重定向
@@ -154,46 +152,45 @@ Static.prototype._getRoot = function (file) {
154
152
  };
155
153
 
156
154
  /**
157
- * 执行前
158
- * @param {object} ctx http请求上下文
159
- * @param {object} path 文件路径
155
+ * 执行前钩子
156
+ * @param {object} params 请求参数
157
+ * @param {object} ctx 请求上下文
158
+ * @param {object} options 运行选项
160
159
  */
161
- Static.prototype.before = async function (ctx, path) { };
160
+ Static.prototype.before = async function (params, ctx, options) { };
162
161
 
163
162
  /**
164
- * 执行
165
- * 兼容两种调用方式:
166
- * - 中间件方式:main(ctx, next),文件路径取自 ctx.path / ctx.params.path
167
- * - 显式路径方式:main(ctx, file_path),直接发送指定文件
168
- * @param {object} ctx http请求上下文
169
- * @param {object|Function} path 文件路径或下一个中间件
170
- * @returns {Promise<string>} 发送成功返回物理文件路径,未命中返回空字符串
163
+ * 主处理函数(服务式调用,不是中间件,不接收 next)
164
+ * 签名与事件管道保持一致:`main(params, ctx, options)`
165
+ * @param {object} params 请求参数(可含 path / url / method)
166
+ * @param {object} ctx 请求上下文
167
+ * @param {object} options 运行选项
168
+ * @returns {Promise<string>} 命中返回物理文件路径,未命中返回空串
171
169
  */
172
- Static.prototype.main = async function (ctx, path) {
173
- if (typeof path === 'function') {
174
- return await this.run(ctx, path);
175
- }
176
- return await this.send(ctx, path, this.config);
170
+ Static.prototype.main = async function (params, ctx, options) {
171
+ return await this.run(params, ctx, options);
177
172
  };
178
173
 
179
174
  /**
180
- * 执行后
181
- * @param {object} ctx http请求上下文
182
- * @param {object} path 文件路径
175
+ * 执行后钩子
176
+ * @param {object} params 请求参数
177
+ * @param {object} ctx 请求上下文
178
+ * @param {object} options 运行选项
183
179
  */
184
- Static.prototype.after = async function (ctx, path) { };
180
+ Static.prototype.after = async function (params, ctx, options) { };
185
181
 
186
182
  /**
187
- * 执行静态文件
188
- * @param {object} ctx Http请求上下文
189
- * @param {object} next 跳过当前函数
190
- * @returns {string} 执行成功返回文件路径
183
+ * 执行静态文件(命中即写出响应,未命中返回空串,由调用方继续后续事件)
184
+ * @param {object} params 请求参数(可含 path / url / method)
185
+ * @param {object} ctx 请求上下文
186
+ * @param {object} options 运行选项
187
+ * @returns {Promise<string>} 命中返回物理文件路径,未命中返回空串
191
188
  */
192
- Static.prototype.run = async function (ctx, next) {
189
+ Static.prototype.run = async function (params, ctx, options) {
193
190
  if (!this.static) {
194
191
  this._preset();
195
192
  }
196
- return await this.static.main(ctx, next);
193
+ return await this.static.main(params, ctx, options);
197
194
  };
198
195
 
199
196
  /**
@@ -356,7 +356,8 @@ StaticHandle.prototype._checkFilePath = function (file_path) {
356
356
  * @returns {object|null} {start, end} 范围对象,无法解析时返回 null
357
357
  */
358
358
  StaticHandle.prototype._parseRange = function (ctx, total) {
359
- const headers = (ctx.params && ctx.params.headers) || (ctx.request && ctx.request.headers) || {};
359
+ const headers = ctx.headers || (ctx.params && ctx.params.headers)
360
+ || (ctx.request && ctx.request.headers) || {};
360
361
  const range_header = headers['range'];
361
362
  if (!range_header) return null;
362
363
  const match = /^bytes=(\d*)-(\d*)$/.exec(range_header);
@@ -772,20 +773,67 @@ StaticHandle.prototype._pathToRoot = function (path) {
772
773
  }
773
774
 
774
775
  /**
775
- * 主处理函数
776
- * 兼容两种上下文:
777
- * - Koa 风格 ctx:使用 ctx.path / ctx.url
778
- * - mm_web_server 原生 ctx:从 ctx.params 读取 path,发送文件后标记 ctx.handled
776
+ * 读取请求路径(优先取事件参数,其次取上下文)
777
+ * @param {object} params 请求参数
778
+ * @param {object} ctx 请求上下文
779
+ * @returns {string} 请求路径
780
+ */
781
+ StaticHandle.prototype._requestPath = function (params, ctx) {
782
+ return (params && params.path) || (ctx && ctx.path) || '';
783
+ };
784
+
785
+ /**
786
+ * 读取请求方法(兼容参数与上下文两处来源)
787
+ * @param {object} params 请求参数
788
+ * @param {object} ctx 请求上下文
789
+ * @returns {string} 大写的请求方法,缺省为空串
790
+ */
791
+ StaticHandle.prototype._requestMethod = function (params, ctx) {
792
+ let method = (params && params.method) || (ctx && ctx.method) || '';
793
+ return String(method).toUpperCase();
794
+ };
795
+
796
+ /**
797
+ * 读取完整请求地址(含查询串,用作缓存键)
798
+ * @param {object} params 请求参数
779
799
  * @param {object} ctx 请求上下文
780
- * @param {Function} next 跳过当前处理函数
800
+ * @param {string} path 已解析出的请求路径
801
+ * @returns {string} 完整请求地址
781
802
  */
782
- StaticHandle.prototype.main = async function (ctx, next) {
783
- let p = ctx.path || (ctx.params && ctx.params.path) || '';
784
- let url = ctx.url || p; // 使用完整URL(包含查询参数)
803
+ StaticHandle.prototype._requestUrl = function (params, ctx, path) {
804
+ return (params && params.url) || (ctx && (ctx.url || ctx.path)) || path;
805
+ };
806
+
807
+ /**
808
+ * 判断请求方法是否允许读取静态文件
809
+ * @param {string} method 请求方法
810
+ * @returns {boolean} GET / HEAD / 未指定方法时返回 true
811
+ */
812
+ StaticHandle.prototype._isReadable = function (method) {
813
+ return !method || method === 'GET' || method === 'HEAD';
814
+ };
815
+
816
+ /**
817
+ * 主处理函数(服务式调用,不是中间件,不接收 next)
818
+ *
819
+ * 约定签名 `main(params, ctx, options)`,由事件管道或上层路由直接调用:
820
+ * - 命中文件:写出响应(或返回物理路径由调用方继续处理)并返回物理文件路径;
821
+ * - 未命中:直接返回空串,由调用方决定是否继续执行后续事件,无需回调放行。
822
+ * @param {object} params 请求参数(可含 path / url / method)
823
+ * @param {object} ctx 请求上下文(可含 path / url / res / set)
824
+ * @param {object} options 运行选项(保留,供上层透传)
825
+ * @returns {Promise<string>} 命中返回物理文件路径,未命中返回空串
826
+ */
827
+ StaticHandle.prototype.main = async function (params, ctx, options) {
828
+ let p = this._requestPath(params, ctx);
829
+ if (!this._isReadable(this._requestMethod(params, ctx))) {
830
+ return '';
831
+ }
832
+ // 无扩展名的路径交给目录分支与后续事件处理
785
833
  if (p.indexOf('.') === -1) {
786
- await next();
787
834
  return '';
788
835
  }
836
+ let url = this._requestUrl(params, ctx, p);
789
837
  let path = this._getRealPath(p);
790
838
 
791
839
  // 检查缓存
@@ -805,9 +853,8 @@ StaticHandle.prototype.main = async function (ctx, next) {
805
853
  return await this._sendFile(ctx, path);
806
854
  }
807
855
  catch {
808
- await next();
856
+ return '';
809
857
  }
810
- return '';
811
858
  };
812
859
 
813
860
  /**
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "mm_statics",
3
- "version": "1.9.1",
4
- "description": "这是超级美眉statics函数模块,用于web服务端statics缓存",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "mocha tests/index.js",
8
- "tests": "mocha tests/*.js"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://gitee.com/qiuwenwu91/mm_statics.git"
13
- },
14
- "keywords": [
15
- "mm_mod",
16
- "mm_infra",
17
- "mm_common",
18
- "statics",
19
- "async",
20
- "await",
21
- "promise",
22
- "add",
23
- "del",
24
- "set",
25
- "get",
26
- "query",
27
- "run",
28
- "exec"
29
- ],
30
- "layer": "infra",
31
- "group": "web_infra",
32
- "author": "邱文武",
33
- "license": "ISC",
34
- "bugs": {
35
- "url": "https://gitee.com/qiuwenwu91/mm_statics/issues"
36
- },
37
- "homepage": "https://gitee.com/qiuwenwu91/mm_statics#readme",
38
- "files": [
39
- "index.js",
40
- "lib/vue_compiler.js",
41
- "lib/static.js",
42
- "lib/static_handle.js",
43
- "lib/manager.js",
44
- "eslint.config.js",
45
- "static/"
46
- ],
47
- "dependencies": {
48
- "@vue/compiler-sfc": "^3.5.44",
49
- "abstract-syntax-tree": "^2.25.0",
50
- "chokidar": "^5.0.0",
51
- "marked": "^18.1.1",
52
- "mm_cache": "^1.5.4",
53
- "mm_corejs": "^1.1.5",
54
- "mm_es6_to_amd": "^1.5.9",
55
- "mm_helper": "^1.0.0",
56
- "mm_machine": "^3.5.4",
57
- "prettier": "^3.9.10"
58
- },
59
- "devDependencies": {
60
- "eslint": "^10.12.0",
61
- "eslint-plugin-jsdoc": "^65.3.4",
62
- "koa": "^3.2.1",
63
- "mm_eslint": "^1.7.7",
64
- "mocha": "^12.0.3",
65
- "supertest": "^7.3.1"
66
- }
67
- }
1
+ {
2
+ "name": "mm_statics",
3
+ "version": "1.9.3",
4
+ "description": "这是超级美眉statics函数模块,用于web服务端statics缓存",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "mocha tests/index.js",
8
+ "tests": "mocha tests/*.js"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://gitee.com/qiuwenwu91/mm_statics.git"
13
+ },
14
+ "keywords": [
15
+ "mm_mod",
16
+ "mm_infra",
17
+ "mm_common",
18
+ "statics",
19
+ "async",
20
+ "await",
21
+ "promise",
22
+ "add",
23
+ "del",
24
+ "set",
25
+ "get",
26
+ "query",
27
+ "run",
28
+ "exec"
29
+ ],
30
+ "layer": "infra",
31
+ "group": "web_infra",
32
+ "author": "邱文武",
33
+ "license": "ISC",
34
+ "bugs": {
35
+ "url": "https://gitee.com/qiuwenwu91/mm_statics/issues"
36
+ },
37
+ "homepage": "https://gitee.com/qiuwenwu91/mm_statics#readme",
38
+ "files": [
39
+ "index.js",
40
+ "lib/vue_compiler.js",
41
+ "lib/static.js",
42
+ "lib/static_handle.js",
43
+ "lib/manager.js",
44
+ "eslint.config.js",
45
+ "static/"
46
+ ],
47
+ "dependencies": {
48
+ "@vue/compiler-sfc": "^3.5.44",
49
+ "abstract-syntax-tree": "^2.25.0",
50
+ "chokidar": "^5.0.0",
51
+ "marked": "^18.1.1",
52
+ "mm_cache": "^1.5.4",
53
+ "mm_corejs": "^1.1.5",
54
+ "mm_es6_to_amd": "^1.5.9",
55
+ "mm_helper": "^1.0.0",
56
+ "mm_machine": "^3.5.4",
57
+ "prettier": "^3.9.10"
58
+ },
59
+ "devDependencies": {
60
+ "eslint": "^10.12.0",
61
+ "eslint-plugin-jsdoc": "^65.3.4",
62
+ "koa": "^3.2.1",
63
+ "mm_eslint": "^1.7.7",
64
+ "mocha": "^12.0.3",
65
+ "supertest": "^7.3.1"
66
+ }
67
+ }