whistle.script 1.2.9 → 1.3.0

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,264 +1,249 @@
1
- # whistle.script
2
- whistle.script为[whistle](https://github.com/avwo/whistle)的一个扩展脚本插件,可以直接在界面上引用全局安装的Node模块及Node的内容模块编写脚本操作请求及其响应,所有正常Node程序可以实现的功能,都可以通过该插件实现,包括:
3
-
4
- 1. HTTP[s]:
5
- - 动态设置[whistle规则](https://avwo.github.io/whistle/rules/)
6
- - 拦截请求响应
7
- - 控制请求响应速度
8
- - 修改请求url、请求方法、请求头、请求内容
9
- - 修改响应状态码、响应头、响应内容
10
- - 在插件界面的Console上显示脚本程序 `console.xxx` 的内容,如果可以打印响应的内容或调试信息等
11
- 2. WebSocket:
12
- - 动态设置[whistle规则](https://avwo.github.io/whistle/rules/)
13
- - 拦截请求响应
14
- - 修改发送或收到的数据
15
- - 直接向WebSocket客户端或服务端发送数据
16
- - 在插件界面的Console上显示脚本程序 `console.xxx` 的内容,如果可以打印发送和接收到的数据或调试信息等,从而通过该插件可以直接查看WebSocket的数据
17
- 3. Tunnel: 基本功能同WebSocket,可以用来直接操作Socket请求,如Protobuf协议的请求等
18
-
19
- # 安装
20
-
21
- 1. 安装Node: [官网下载安装最新版本(LTS和Stable都可以)](https://nodejs.org/)
22
- 2. 安装最新版的[whistle](https://github.com/avwo/whistle)。
23
-
24
- npm install -g whistle
25
-
26
- # Mac、Linux用户可能需要加sudo
27
- sudo npm install -g whistle
28
-
29
- 3. 安装script插件:
30
-
31
- w2 i whistle.script
32
-
33
- # 使用
34
-
35
- 打开script插件的界面,创建一个名字为 `test` 的脚本:
36
-
37
- 1. 可以通过 `Plugins->Home->script`打开或右键并选择 `在新标签页中打开`
38
- 2. 直接访问 [http://local.whistlejs.com/plugin.script](http://local.whistlejs.com/plugin.script/)
39
-
40
- ![whistle.script界面](https://user-images.githubusercontent.com/11450939/126302159-0c533ea7-3bc0-484a-bd30-698d5a7881df.gif)
41
-
42
- #### 设置规则
43
-
44
- 1. 设置HTTP或HTTPs请求的[whistle规则](https://avwo.github.io/whistle/rules/)(操作HTTPs需要[开启HTTPs拦截](https://avwo.github.io/whistle/webui/https.html))
45
-
46
- 在界面中的`test` 脚本输入(也可以在其它编辑器编辑后再copy进来):
47
-
48
- exports.handleRequestRules = (ctx) => {
49
- // ctx.fullUrl 可以获取请求url
50
- // ctx.headers 可以获取请求头
51
- // ctx.options 里面包含一些特殊的请求头字段,分别可以获取一些额外信息,如请求方法、设置的规则等
52
- ctx.rules = ['www.qq.com file://{test.html}'];
53
- ctx.values = { 'test.html': 'Hello world.' };
54
- };
55
-
56
- Note: 如果里面包含一些异步方法可以采用 async 函数,即:`exports.handleRequestRules = async () => {}`
57
-
58
- 在whistle的Rules配置界面上输入规则:
59
-
60
- whistle.script://test www.ifeng.com www.qq.com www.baidu.com echo.websocket.org
61
-
62
- 分别访问[http://www.ifeng.com](http://www.ifeng.com)和[http://www.qq.com](http://www.qq.com),前者可以正常访问,后者输出 `Hello world.`。
63
-
64
- 具体效果见图:[demo1](https://user-images.githubusercontent.com/11450939/126302225-2598772c-d6a3-45e3-97d6-685fbed1ba37.gif)
65
-
66
- 如果需要通过配置给脚本传递一些额外参数,可以如下配置(注意中间不能有空格):
67
-
68
- whistle.script://test(a,b,c) www.ifeng.com www.qq.com www.baidu.com echo.websocket.org
69
-
70
- 可以在脚本中通过 `process.args` 获取:
71
-
72
- exports.handleRequestRules = (ctx) => {
73
- console.log(process.args); // output: ["a", "b", "c"]
74
- ctx.rules = ['www.qq.com file://{test.html}'];
75
- ctx.values = { 'test.html': 'Hello world.' };
76
- };
77
-
78
- 2. 设置WebSocket请求的规则(需要[开启HTTPs拦截](https://avwo.github.io/whistle/webui/https.html)):
79
-
80
- exports.handleWebSocketRules = (ctx) => {
81
- // ctx.fullUrl 可以获取请求url
82
- // ctx.headers 可以获取请求头
83
- // ctx.options 里面包含一些特殊的请求头字段,分别可以获取一些额外信息,如请求方法、设置的规则等
84
- this.rules = '127.0.0.1 echo.websocket.org';
85
- };
86
-
87
- 接下来的操作同上。
88
-
89
- 3. 设置Tunnel请求的规则(要测试可以暂时[关闭HTTPs拦截](https://avwo.github.io/whistle/webui/https.html)):
90
-
91
- exports.handleTunnel = (ctx) => {
92
- // ctx.fullUrl 可以获取请求url
93
- // ctx.headers 可以获取请求头
94
- // ctx.options 里面包含一些特殊的请求头字段,分别可以获取一些额外信息,如请求方法、设置的规则等
95
- this.rules = '127.0.0.1 www.baidu.com';
96
- };
97
-
98
-
99
- 接下来的操作同上。
100
-
101
- #### 操作请求
102
-
103
- 1. 操作HTTP或HTTPs请求(操作HTTPs需要[开启HTTPs拦截](https://avwo.github.io/whistle/webui/https.html))
104
- ``` js
105
- exports.handleRequest = async (ctx, request) => {
106
- // ctx.fullUrl 可以获取请求url
107
- // ctx.headers 可以获取请求头
108
- // ctx.options 里面包含一些特殊的请求头字段,分别可以获取一些额外信息,如请设置的规则等
109
- // ctx.method 获取和设置请求方法
110
- // ctx.req
111
- // ctx.res
112
- const { req, res } = ctx;
113
- const client = request((svrRes) => {
114
- res.writeHead(svrRes.statusCode, svrRes.headers);
115
- svrRes.pipe(res);
116
- // try {
117
- // const body = await ctx.getStreamBuffer(svrRes);
118
- // delete svrRes.headers['content-encoding'];
119
- // res.writeHead(svrRes.statusCode, svrRes.headers);
120
- // res.end(body);
121
- // } catch (err) {}
122
- });
123
- req.pipe(client);
124
- };
125
- ```
126
- 在whistle的Rules配置界面上输入规则:
127
- ``` txt
128
- # 这里不能用whistle.script,否则请求不会转发到handleRequest
129
- # whistle.script只会执行handleXxxRules
130
- # 你也可以通过在handleXxxRules里面设置 script://test(a,b,c),实现转发
131
- script://test www.ifeng.com www.qq.com www.baidu.com echo.websocket.org
132
- ```
133
- 分别访问[http://www.ifeng.com](http://www.ifeng.com)和[http://www.qq.com](http://www.qq.com),可以在script的界面中的Consle看到打印出来的请求的url、响应状态吗和头部。
134
-
135
- 具体效果见图:[demo2](https://user-images.githubusercontent.com/11450939/126302210-e3aa0b56-9001-4e03-83c8-8986d8f544ff.gif)
136
-
137
- 需要在配置中带上参数,可以参考上面的规则设置
138
- 2. 操作WebSocket请求(需要[开启HTTPs拦截](https://avwo.github.io/whistle/webui/https.html))
139
- ``` js
140
- exports.handleWebSocket = async (socket, connect) => {
141
- // 与服务器建立连接
142
- const svrSocket = await connect();
143
- // 客户端 pong 服务端
144
- socket.on('pong', (data) => {
145
- svrSocket.pong(data);
146
- });
147
- // 客户端 ping 服务pong 端
148
- socket.on('ping', (data) => {
149
- svrSocket.ping(data);
150
- });
151
- // 服务端 ping 客户端
152
- svrSocket.on('ping', (data) => {
153
- socket.ping(data);
154
- });
155
- // 服务端 pong 客户端
156
- svrSocket.on('pong', (data) => {
157
- socket.pong(data);
158
- });
159
- // 正常断开 WebSocket 连接
160
- socket.on('disconnect', (code, message, opts) => {
161
- console.log(code, 'client disconnect');
162
- svrSocket.disconnect(code, opts);
163
- });
164
- // 正常断开 WebSocket 连接
165
- svrSocket.on('disconnect', (code, message, opts) => {
166
- console.log(code, 'server disconnect');
167
- socket.disconnect(code, opts);
168
- });
169
- // 获取客户端解析后的帧数据
170
- socket.on('message', (data, opts) => {
171
- console.log(data, 'client data');
172
- svrSocket.send(data, opts);
173
- });
174
- // 获取服务端解析后的帧数据
175
- svrSocket.on('message', (data, opts) => {
176
- console.log(data, 'server data');
177
- socket.send(data, opts);
178
- });
179
- };
180
-
181
- ```
182
-
183
- whistle规则配置同上,访问[https://www.websocket.org/echo.html](https://www.websocket.org/echo.html),点击下面的connect按钮及send按钮,可以如下效果:[demo3](https://user-images.githubusercontent.com/11450939/126302243-26c8b4af-851c-4b00-87b9-3286e9e67251.gif)
184
- 3. 操作Tunnel请求
185
- ``` js
186
- exports.handleTunnel = async (socket, connect) => {
187
- const svrSocket = await connect();
188
- socket.pipe(svrSocket).pipe(socket);
189
- };
190
- ```
191
- whistle规则配置同上
192
-
193
- 4. 鉴权
194
- 插件 `v1.2.0` 版本开始支持自定义鉴权方法(要求 Whistle 版本 >= `v2.7.16`):
195
- ``` js
196
- exports.auth = async (req, options) => {
197
- // 给请求添加自定义头,必须与 `x-whistle-` 开头
198
- // 这样可以在插件的其他 hook 里面获取到该请求头(除了 http 请求的 reqRead 钩子)
199
- req.setHeader('x-whistle-test', '1111111111');
200
- // return false; // 直接返回 403
201
- };
202
- ```
1
+ # 🧩 whistle.script - 脚本扩展插件
203
2
 
204
- 5. pipe
205
- 插件 `v1.2.1` 版本开始支持自定义 pipe 方法:
206
- ``` js
3
+ 中文 · [English](./README-en_US.md)
207
4
 
208
- exports.handleReqRead = (req, res, options) => {
209
- req.pipe(res);
210
- };
5
+ > 环境要求:Whistle 版本需为 2.10.0 或更高。
211
6
 
212
- exports.handleReqWrite = (req, res, options) => {
213
- req.pipe(res);
214
- };
7
+ whistle.script [Whistle](https://github.com/avwo/whistle) 的脚本扩展插件。通过在 Web 界面编写 Node.js 脚本,您可以为 Whistle 注入动态逻辑,实现对网络请求、响应及 WebSocket 等协议的**编程式深度控制**。
215
8
 
216
- exports.handleResRead = (req, res, options) => {
217
- req.pipe(res);
218
- };
9
+ ## 🎯 核心功能
219
10
 
220
- exports.handleResWrite = (req, res, options) => {
221
- req.pipe(res);
222
- };
11
+ ### HTTP/HTTPS 处理
12
+ - **动态规则生成** - 根据请求URL、头部等信息,实时生成并注入 Whistle 匹配规则。
13
+ - **请求/响应拦截与修改** - 完整拦截 HTTP(S) 请求流与响应流,支持修改 URL、方法、头部、状态码及响应体。
14
+ - **调试与日志** - 脚本内的 `console.log` 等输出会实时显示在插件控制台中,便于调试。
223
15
 
224
- exports.handleWsReqRead = (req, res, options) => {
225
- req.pipe(res);
226
- };
16
+ ### WebSocket 处理
17
+ - **双向通信拦截** - 拦截客户端与服务器之间的 WebSocket 握手及数据帧。
18
+ - **消息动态处理** - 实时查看、修改或转发 `ping`、`pong`、`message` 及控制帧。
19
+ - **直接数据发送** - 可主动向任一端发送数据或断开连接。
227
20
 
228
- exports.handleWsReqWrite = (req, res, options) => {
229
- req.pipe(res);
230
- };
21
+ ### Tunnel / 原始 Socket 处理
22
+ - **透明管道操作** - 处理如 HTTPS 隧道等原始 TCP 连接,实现底层数据流转发或修改。
23
+ - **灵活性高** - 提供与 WebSocket 类似的 API,用于处理非 HTTP 协议。
24
+
25
+ ## 📦 安装指南
26
+
27
+ ### 1. 安装 Whistle 运行时
28
+ **推荐方式(桌面用户)**:下载并安装可视化客户端,管理更便捷。
29
+ 👉 [Whistle 客户端下载](https://github.com/avwo/whistle-client)
231
30
 
232
- exports.handleWsResRead = (req, res, options) => {
233
- req.pipe(res);
31
+ **命令行方式**:
32
+ 1. **安装 Node.js (>= 8.8)**
33
+ 请从 [Node.js 官网](https://nodejs.org/) 下载并安装最新的 LTS 版本。
34
+ 2. **全局安装 Whistle**
35
+ ```bash
36
+ npm install -g whistle
37
+ ```
38
+ > **提示**:若安装过程提示权限不足,可尝试使用 `sudo`(不推荐)或参考官方文档配置 npm 全局安装路径。
39
+
40
+ ### 2. 安装 whistle.script 插件
41
+ 在 Whistle 运行后,执行以下命令:
42
+ ```bash
43
+ w2 i whistle.script
44
+ ```
45
+ **或通过管理界面安装**:
46
+ 1. 启动 Whistle 并打开管理界面(默认为 `http://127.0.0.1:8899`)。
47
+ 2. 进入 **Plugins** 页面。
48
+ 3. 点击顶部的 `Install` 按钮。
49
+ 4. 输入 `whistle.script` 并确认安装。
50
+
51
+ ## 🚀 快速开始
52
+
53
+ ### 1. 打开插件界面
54
+ - 在 Whistle 管理界面,通过菜单 `Plugins -> script` 进入。
55
+ - 或直接访问地址:[http://local.whistlejs.com/plugin.script/](http://local.whistlejs.com/plugin.script/)。
56
+
57
+ ### 2. 创建并关联你的第一个脚本
58
+ 1. 在插件界面点击 **Create**,创建一个名为 `test` 的脚本。
59
+ 2. 在右侧编辑器中,输入以下示例代码:
60
+ ```javascript
61
+ exports.handleRequestRules = (ctx) => {
62
+ console.log('收到请求:', ctx.fullUrl);
63
+ ctx.rules = ['www.example.com 127.0.0.1:8080']; // 将请求转发到本地8080端口
64
+ };
65
+ ```
66
+ 3. 在 Whistle 的 **Rules** 配置页面,添加规则:
67
+ ```txt
68
+ www.example.com whistle.script://test
69
+ ```
70
+ 4. 现在,访问 `http://www.example.com` 的请求将被脚本处理,并可在插件 **Console** 标签页查看日志。
71
+
72
+ ![插件界面操作演示](https://user-images.githubusercontent.com/11450939/126302159-0c533ea7-3bc0-484a-bd30-698d5a7881df.gif)
73
+
74
+ ## 📖 功能详解
75
+
76
+ ### 1. 规则动态设置
77
+ 此模式允许脚本根据请求信息,动态返回需要执行的 Whistle 规则(字符串或数组),规则会与 `whistle.script://` 配置的原始规则合并执行。
78
+
79
+ #### HTTP/HTTPS 规则
80
+ > **重要**:如需拦截 HTTPS 请求,须先[开启并安装 Whistle 的 HTTPS 根证书](https://wproxy.org/docs/gui/https.html)。
81
+
82
+ **脚本示例 (`test`)**:
83
+ ```javascript
84
+ exports.handleRequestRules = (ctx) => {
85
+ // 根据请求路径动态返回本地文件
86
+ if (ctx.fullUrl.includes('/api/test')) {
87
+ ctx.rules = ['api.example.com/api/test file://{mockData.json}'];
88
+ ctx.values = {
89
+ 'mockData.json': JSON.stringify({ code: 200, data: 'mocked' })
90
+ };
91
+ }
234
92
  };
93
+ ```
94
+ **Whistle 规则配置**:
95
+ ```txt
96
+ # 将多个域名的请求交给 `test` 脚本处理
97
+ whistle.script://test www.test.com api.example.com
98
+ ```
235
99
 
236
- exports.handleWsResWrite = (req, res, options) => {
237
- req.pipe(res);
100
+ #### 向脚本传递参数
101
+ 可以在规则中向脚本传递参数(参数内请避免使用空格)。
102
+ ```txt
103
+ whistle.script://test(prod,env1) www.example.com
104
+ ```
105
+ 脚本内通过以下方式获取:
106
+ ```javascript
107
+ exports.handleRequestRules = (ctx) => {
108
+ console.log(process.args); // 输出:["prod", "env1"]
109
+ console.log(ctx.scriptValue); // 输出 (v1.3.0+):"prod,env1"
110
+ // 可根据参数执行不同逻辑
111
+ ctx.rules = 'www.test.com 127.0.0.1:8080';
238
112
  };
113
+ ```
239
114
 
240
- exports.handleTunnelReqRead = (req, res, options) => {
241
- req.pipe(res);
115
+ #### WebSocket 规则设置
116
+ ```javascript
117
+ exports.handleWebSocketRules = (ctx) => {
118
+ // 动态决定哪些 WebSocket 连接需要被本插件处理
119
+ this.rules = 'echo.websocket.org statusCode://101';
242
120
  };
121
+ ```
243
122
 
244
- exports.handleTunnelReqWrite = (req, res, options) => {
245
- req.pipe(res);
123
+ ### 2. 请求与响应的直接操作
124
+ 此模式赋予脚本对网络流量的完全控制权,可以手动发起请求、读取和修改数据。
125
+
126
+ #### HTTP/HTTPS 请求处理
127
+ 使用 `script://` 协议触发此模式。
128
+
129
+ ```javascript
130
+ exports.handleRequest = (ctx, request) => {
131
+ const { req, res } = ctx;
132
+
133
+ req.passThrough({
134
+ // 可选
135
+ transformReq: function(req, next) {
136
+ // getBuffer, getText, getJson 都可以用来获取请求体,参数和回调函数的用法也完全一样
137
+ req.getJson(function(err, data) {
138
+ if (err) {
139
+ return next();
140
+ }
141
+ // data.a.b.c = 'test';
142
+ next(JSON.stringify(data));
143
+ });
144
+ },
145
+ // 可选
146
+ transformRes: function(svrRes, next) {
147
+ // getBuffer, getText, getJson 都可以用来获取请求体,参数和回调函数的用法也完全一样
148
+ svrRes.getText(function(err, text) {
149
+ if (err) {
150
+ return next();
151
+ }
152
+ next('[' + text + ', 123' + ']');
153
+ });
154
+ }
155
+ });
246
156
  };
157
+ ```
158
+ **关联规则**:
159
+ ```txt
160
+ # 注意:此处使用 script:// 触发 handleRequest 方法
161
+ www.example.com/api script://test
162
+ ```
247
163
 
248
- exports.handleTunnelResRead = (req, res, options) => {
249
- req.pipe(res);
164
+ #### WebSocket 连接处理
165
+ ```javascript
166
+ exports.handleWebSocket = async (socket, connect) => {
167
+ console.log('WebSocket 连接已建立');
168
+
169
+ // 连接到原始后端服务器
170
+ const serverSocket = await connect();
171
+
172
+ // 监听客户端消息,转发至服务器
173
+ socket.on('message', (data, opts) => {
174
+ console.log('<< 来自客户端:', data);
175
+ // 可在此处修改 data
176
+ serverSocket.send(`[中转] ${data}`, opts);
177
+ });
178
+
179
+ // 监听服务器消息,转发至客户端
180
+ serverSocket.on('message', (data, opts) => {
181
+ console.log('>> 来自服务器:', data);
182
+ socket.send(data, opts);
183
+ });
184
+
185
+ // 处理连接关闭
186
+ socket.on('disconnect', (code, reason) => {
187
+ console.log(`客户端断开连接 [${code}]: ${reason}`);
188
+ serverSocket.disconnect(code, reason);
189
+ });
250
190
  };
191
+ ```
251
192
 
252
- exports.handleTunnelResWrite = (req, res, options) => {
253
- req.pipe(res);
193
+ #### Tunnel (原始TCP) 处理
194
+
195
+ 用于处理 `CONNECT` 方法建立的隧道(如 HTTPS)。
196
+ ```javascript
197
+ exports.handleTunnel = async (clientSocket, connect) => {
198
+ const targetSocket = await connect();
199
+ // 建立双向透明管道
200
+ clientSocket.pipe(targetSocket).pipe(clientSocket);
201
+
202
+ // 可监听 data 事件进行更底层的二进制数据操作
254
203
  };
204
+ ```
255
205
 
206
+ ### 3. 高级功能
207
+
208
+ #### 请求鉴权 (`auth`)
209
+ 在请求进入其他处理阶段前,进行身份验证。
210
+ ```javascript
211
+ exports.auth = async (req, options) => {
212
+ const token = req.headers['x-auth-token'];
213
+ // 1. 添加内部透传头 (以 x-whistle- 开头)
214
+ req.setHeader('x-whistle-req-id', Date.now());
215
+
216
+ // 2. 进行异步验证
217
+ // const isValid = await verifyToken(token);
218
+ // return isValid; // 返回 false 将直接响应 403 Forbidden
219
+
220
+ // 3. 默认允许通过
221
+ return true;
222
+ };
256
223
  ```
257
224
 
258
- # 如何引入第三方模块
259
- 使用绝对路径引入,如假设你的模块安装路径为 `/Users/test/node_modules/xxx`,则可以在脚本里面通过 `require('/Users/test/node_modules/xxx')` 引入。
225
+ #### 管道钩子 (Pipe Hooks)
226
+ 在请求/响应的不同生命周期阶段进行轻量拦截。
227
+ ```javascript
228
+ // 在请求体被 Whistle 规则引擎读取前处理
229
+ exports.handleReqRead = (req, res, options) => {
230
+ // 可用于记录原始请求体或进行早期修改
231
+ req.pipe(res); // 通常直接管道传输
232
+ };
260
233
 
261
- # License
234
+ // 在请求体被 Whistle 规则引擎处理后、发送到目标服务器前处理
235
+ exports.handleReqWrite = (req, res, options) => {
236
+ // 可用于基于规则结果进行最终修改
237
+ req.pipe(res);
238
+ };
239
+ // 类似钩子:handleResRead, handleResWrite, handleWsReqRead 等
240
+ ```
262
241
 
263
- [MIT](https://github.com/whistle-plugins/whistle.script/blob/master/LICENSE)
242
+ ## 🔗 更多资源
243
+ - [Whistle 核心文档](https://wproxy.org/)
244
+ - [Whistle 规则配置语法](https://wproxy.org/docs/rules/rule.html)
245
+ - [Whistle GitHub 仓库](https://github.com/avwo/whistle)
246
+ - [插件开发类型定义参考](https://github.com/avwo/lack/blob/master/assets/ts/src/types/global.d.ts)
264
247
 
248
+ ## 📄 许可证
249
+ 本项目基于 [MIT License](./LICENSE) 开源。
package/lib/scripts.js CHANGED
@@ -77,11 +77,14 @@ const execScript = (script, value) => {
77
77
  const parseRuleValue = (ctx) => {
78
78
  const { ruleValue } = ctx.req.originalReq;
79
79
  if (!RULE_VALUE_RE.test(ruleValue)) {
80
+ ctx.scriptValue = '';
80
81
  return '';
81
82
  }
83
+ const value = RegExp.$2;
84
+ ctx.scriptValue = value;
82
85
  return {
83
86
  name: RegExp.$1,
84
- value: RegExp.$2,
87
+ value,
85
88
  };
86
89
  };
87
90
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "whistle.script",
3
3
  "description": "The plugin for the extension script for whistle",
4
- "version": "1.2.9",
4
+ "version": "1.3.0",
5
5
  "author": "avenwu <avenwu@vip.qq.com>",
6
6
  "contributors": [],
7
7
  "license": "MIT",
@@ -53,7 +53,7 @@
53
53
  "koa": "^2.13.1",
54
54
  "koa-bodyparser": "^4.3.0",
55
55
  "koa-onerror": "^4.1.0",
56
- "koa-router": "^10.0.0",
56
+ "koa-router": "^12.0.1",
57
57
  "koa-static": "^5.0.0"
58
58
  }
59
59
  }
package/public/index.html CHANGED
@@ -7,6 +7,6 @@
7
7
  </head>
8
8
  <body>
9
9
  <div id="main" class="main"></div>
10
- <script src="index.js?v=0.4.0"></script>
10
+ <script src="index.js?v=1.3.0"></script>
11
11
  </body>
12
12
  </html>