mm_machine 2.0.1 → 2.0.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/README.md CHANGED
@@ -1,210 +1,576 @@
1
1
  # mm_machine
2
2
 
3
- 这是超级美眉框架机制构建辅助模块,用于快速构建一个机制。本模块提供了一个灵活的机制管理系统,支持动态加载、热更新和模块管理等功能。
3
+ 一个灵活的Node.js插件化机制系统,用于动态加载、管理和执行模块。
4
4
 
5
- ## 特性
5
+ ## 快速开始
6
6
 
7
- - 支持动态加载和卸载模块
8
- - 支持热更新
9
- - 支持模块状态管理
10
- - 灵活的配置系统
11
- - 支持多种运行模式
12
-
13
- ## 安装
7
+ ### 安装
14
8
 
15
9
  ```bash
16
- npm install mm_machine
10
+ npm install mm_machine --save
17
11
  ```
18
12
 
19
- ## 核心类
13
+ ### 目录结构
20
14
 
21
- ### Item 类
15
+ 推荐的项目结构:
22
16
 
23
- `Item` 类是模块的基础类,用于管理单个模块的配置和状态。
17
+ ```
18
+ my_project/
19
+ ├── index.js # 主应用入口
20
+ ├── app/ # 默认模块目录
21
+ │ ├── module1/ # 模块1
22
+ │ │ ├── config_demo.json # 配置文件 (注意命名规则)
23
+ │ │ └── index.js # 模块实现
24
+ │ └── module2/ # 模块2
25
+ │ ├── config_demo.json # 配置文件
26
+ │ └── main.js # 模块实现 (可以使用index.js或main.js)
27
+ └── package.json
28
+ ```
24
29
 
25
- #### 构造函数
30
+ ### 使用示例
26
31
 
27
32
  ```javascript
28
- new Item(dir, dir_base)
33
+ const $ = require('mm_expand');
34
+ const Machine = require('mm_machine');
35
+
36
+ // 创建引擎实例
37
+ const engine = new Machine();
38
+
39
+ // 初始化并加载模块
40
+ async function init() {
41
+ // 更新配置(可选)
42
+ engine.update_config_all({
43
+ searchPath: './app/' // 默认路径,可自定义
44
+ });
45
+
46
+ // 加载所有模块
47
+ await engine.load_list();
48
+
49
+ // 执行所有模块的main方法
50
+ const results = await engine.execute('main');
51
+ console.log('执行结果:', results);
52
+
53
+ // 获取特定模块
54
+ const demo1 = engine.get('demo1');
55
+ if (demo1) {
56
+ console.log('找到模块:', demo1.name);
57
+ }
58
+
59
+ console.log(`已加载 ${engine.list.length} 个模块`);
60
+ }
61
+
62
+ // 启动应用
63
+ init().catch(err => {
64
+ console.error('初始化失败:', err);
65
+ });
29
66
  ```
30
67
 
31
- 参数:
32
- - `dir` {String} 当前目录
33
- - `dir_base` {String} 模块目录
68
+ ### 创建模块
34
69
 
35
- #### 属性
70
+ **1. 创建配置文件**
36
71
 
37
- - `dir` {String} 当前路径
38
- - `default_file` {String} 默认配置文件路径,默认为 "./sys.json"
39
- - `filename` {String} 当前文件名
40
- - `config` {Object} 配置参数对象
41
- - `name` {String} 模块名称
42
- - `title` {String} 模块标题
43
- - `description` {String} 模块描述
44
- - `func_file` {String} 函数文件路径
45
- - `func_name` {String} 回调函数名
46
- - `sort` {Number} 排序值
47
- - `state` {Number} 状态(0:未启用, 1:启用)
48
- - `show` {Number} 显示状态(0:不显示, 1:显示)
49
- - `mode` {Number} 运行模式
50
- - 1: 生产模式,文件改变不会重新加载
51
- - 2: 热更新模式,文件改变时重新加载
52
- - 3: 重载模式,执行完后重新加载
53
- - 4: 热更新+重载模式
72
+ 在模块目录中创建 `config_demo.json` 文件(注意命名规则):
54
73
 
55
- ### Index 类
74
+ ```json
75
+ {
76
+ "name": "demo1",
77
+ "title": "测试模块1",
78
+ "description": "这是第一个测试模块",
79
+ "sort": 10,
80
+ "state": 1,
81
+ "show": 1,
82
+ "config": {
83
+ "debug": true,
84
+ "timeout": 3000
85
+ }
86
+ }
87
+ ```
56
88
 
57
- `Index` 类是机制管理的核心类,用于管理多个模块。
89
+ **2. 创建模块实现**
58
90
 
59
- #### 构造函数
91
+ 在同一目录中创建 `index.js` 或 `main.js` 文件:
60
92
 
61
93
  ```javascript
62
- new Index(scope, dir_base)
94
+ const $ = require('mm_expand');
95
+
96
+ /**
97
+ * 主方法
98
+ */
99
+ async function main() {
100
+ $.log.info(`[${this.name}] 测试模块执行`);
101
+
102
+ // 访问模块配置
103
+ const debug = this.config.debug;
104
+ if (debug) {
105
+ $.log.debug(`[${this.name}] 调试模式已启用`);
106
+ }
107
+
108
+ return {
109
+ success: true,
110
+ message: `${this.name} 执行成功`
111
+ };
112
+ }
113
+
114
+ // 导出方法
115
+ module.exports = {
116
+ main
117
+ };
63
118
  ```
64
119
 
65
- 参数:
66
- - `scope` {Object} 作用域
67
- - `dir_base` {String} 模块目录
120
+ ### 重要:文件命名规则
68
121
 
69
- #### 属性
122
+ **配置文件必须按照以下格式命名:**
70
123
 
71
- - `scope` {String} 作用域
72
- - `list` {Array} 模块列表
73
- - `type` {String} 机制类型
74
- - `sort_key` {String} 排序键名
75
- - `mode` {Number} 运行模式,同 Item 类的 mode
124
+ ```
125
+ config_{type}.json
126
+ ```
76
127
 
77
- ## 使用示例
128
+ 其中`{type}`必须与您在Engine类中设置的`this.type`属性值完全匹配。例如:
129
+ - 如果`this.type = "demo"`,配置文件必须命名为`config_demo.json`
130
+ - 如果`this.type = "plugin"`,配置文件必须命名为`config_plugin.json`
78
131
 
79
- ### 基础使用
132
+ ## 完整使用示例
80
133
 
81
- ```javascript
82
- const { Item, Index } = require('mm_machine');
134
+ ### 示例1:创建和管理基础模块
83
135
 
84
- // 创建自定义驱动类
85
- class Drive extends Item {
86
- constructor(dir, dir_base) {
87
- super(dir, dir_base);
88
- this.default_file = "./demo.json";
89
- }
90
- }
136
+ 以下是一个完整的示例,展示如何创建和管理基础模块:
91
137
 
92
- // 创建自定义引擎类
93
- class Engine extends Index {
94
- constructor(scope, dir_base) {
95
- super(scope, dir_base);
96
- this.mode = 0;
97
- this.type = "demo";
98
- }
99
- }
138
+ **1. 项目结构**
100
139
 
101
- Engine.prototype.Drive = Drive;
140
+ ```
141
+ my_project/
142
+ ├── index.js # 主应用入口
143
+ ├── app/ # 模块目录
144
+ │ ├── demo1/ # 模块1
145
+ │ │ ├── config_demo.json # 模块配置文件
146
+ │ │ └── index.js # 模块实现
147
+ │ └── demo2/ # 模块2
148
+ │ ├── config_demo.json # 模块配置文件
149
+ │ └── main.js # 模块实现
150
+ └── package.json
151
+ ```
102
152
 
103
- // 使用示例
104
- async function demo() {
105
- const engine = new Engine();
106
-
153
+ **2. 主应用入口 (`index.js`)**
154
+
155
+ ```javascript
156
+ const $ = require('mm_expand');
157
+ const Machine = require('mm_machine');
158
+
159
+ // 创建引擎实例
160
+ const engine = new Machine();
161
+
162
+ // 初始化引擎
163
+ async function init() {
107
164
  // 加载模块
108
- await engine.update("./");
109
-
110
- // 执行特定模块的方法
111
- await engine.run('demo1', 'init');
112
- await engine.exec('demo1', 'init');
165
+ await engine.load_list();
113
166
 
114
- // 执行所有模块的方法
115
- await engine.run(null, 'main');
116
- await engine.exec(null, 'main');
167
+ // 执行所有模块的main方法
168
+ await engine.execute('main');
117
169
 
118
170
  // 获取特定模块
119
- const plug = engine.get('demo1');
120
- if (plug) {
121
- plug.loadFile(plug.filename);
171
+ const demo1 = engine.get('demo1');
172
+ if (demo1) {
173
+ console.log('模块1状态:', demo1.state ? '启用' : '禁用');
174
+
175
+ // 执行特定模块的自定义方法
176
+ const result = await engine.execute('my_custom_method', 'demo1');
177
+ console.log('自定义方法结果:', result);
122
178
  }
179
+
180
+ // 监听文件变化(热更新模式)
181
+ engine.mode = 2;
182
+ engine.watch();
183
+
184
+ console.log(`已加载 ${engine.list.length} 个模块`);
123
185
  }
186
+
187
+ // 启动应用
188
+ init().catch(err => {
189
+ console.error('初始化失败:', err);
190
+ });
124
191
  ```
125
192
 
126
- ### 模块配置示例
193
+ **3. 模块配置文件示例 (`app/demo1/config_demo.json`)**
127
194
 
128
195
  ```json
129
196
  {
130
197
  "name": "demo1",
131
- "title": "示例脚本1",
132
- "description": "用于测试动态加载、更新、卸载、删除脚本",
133
- "func_file": "./index.js",
134
- "func_name": "",
198
+ "title": "测试模块1",
199
+ "description": "这是第一个测试模块",
135
200
  "sort": 10,
136
- "state": 0,
137
- "show": 0
201
+ "state": 1,
202
+ "show": 1,
203
+ "config": {
204
+ "debug": true,
205
+ "timeout": 3000,
206
+ "api_key": "your_api_key_here"
207
+ }
138
208
  }
139
209
  ```
140
210
 
141
- ### 模块实现示例
211
+ **4. 模块实现示例 (`app/demo1/index.js`)**
142
212
 
143
213
  ```javascript
144
- var i = 0;
214
+ const $ = require('mm_expand');
215
+
216
+ // 模块状态变量
217
+ let counter = 0;
218
+
219
+ /**
220
+ * 初始化方法
221
+ */
222
+ async function init() {
223
+ $.log.info(`[${this.name}] 模块初始化中...`);
224
+ counter = 0;
225
+ return true;
226
+ }
145
227
 
146
- function test() {
147
- console.log("你好", i++)
228
+ /**
229
+ * 加载方法
230
+ */
231
+ async function load() {
232
+ $.log.info(`[${this.name}] 模块加载中...`);
233
+ // 可以在这里执行加载资源等操作
234
+ return true;
148
235
  }
149
236
 
150
- function main() {
151
- test();
152
- return i;
237
+ /**
238
+ * 主方法
239
+ * @param {Object} params - 传入参数
240
+ */
241
+ async function main(params = {}) {
242
+ $.log.info(`[${this.name}] 主方法执行,当前计数: ${counter++}`);
243
+
244
+ // 访问模块配置
245
+ const debug = this.config.debug;
246
+ if (debug) {
247
+ $.log.debug(`[${this.name}] 调试模式已启用`);
248
+ }
249
+
250
+ // 返回结果
251
+ return {
252
+ success: true,
253
+ message: `${this.name} 执行成功`,
254
+ count: counter,
255
+ params: params
256
+ };
153
257
  }
154
258
 
155
- exports.main = main;
259
+ /**
260
+ * 主方法执行前钩子
261
+ */
262
+ async function main_before() {
263
+ $.log.info(`[${this.name}] 主方法执行前处理`);
264
+ return true;
265
+ }
156
266
 
157
- exports.main_before = function() {
158
- console.log("请求前")
267
+ /**
268
+ * 主方法执行后钩子
269
+ */
270
+ async function main_after(result) {
271
+ $.log.info(`[${this.name}] 主方法执行后处理,结果:`, result);
272
+ return result;
159
273
  }
160
274
 
161
- exports.main_after = async function(ret) {
162
- console.log("请求后", ret)
275
+ /**
276
+ * 自定义方法
277
+ */
278
+ async function my_custom_method() {
279
+ return {
280
+ custom: "这是一个自定义方法",
281
+ module_name: this.name
282
+ };
163
283
  }
164
284
 
165
- exports.init = function() {
166
- console.log("初始化");
285
+ // 导出方法
286
+ module.exports = {
287
+ init,
288
+ load,
289
+ main,
290
+ main_before,
291
+ main_after,
292
+ my_custom_method
293
+ };
294
+ ```
295
+
296
+ ### 示例2:使用自定义模块目录
297
+
298
+ 如果需要使用非默认的模块目录,可以按以下方式配置:
299
+
300
+ ```javascript
301
+ const engine = new Machine();
302
+
303
+ // 使用自定义目录
304
+ engine.update_config_all({
305
+ searchPath: './custom_modules/'
306
+ });
307
+
308
+ // 然后加载模块
309
+ await engine.load_list();
310
+ ```
311
+
312
+ ### 示例3:实现热更新和动态管理
313
+
314
+ 以下示例展示如何实现模块的热更新和动态管理:
315
+
316
+ ```javascript
317
+ const engine = new Machine();
318
+
319
+ // 设置为热更新模式
320
+ engine.mode = 2;
321
+
322
+ // 初始化
323
+ async function setup() {
324
+ // 加载模块
325
+ await engine.load_list();
326
+
327
+ // 开始监听文件变化
328
+ engine.watch();
329
+
330
+ // 定时检查模块数量
331
+ setInterval(async () => {
332
+ console.log(`当前模块数量: ${engine.list.length}`);
333
+ }, 5000);
334
+
335
+ // 模拟动态管理模块
336
+ setTimeout(async () => {
337
+ // 重载特定模块
338
+ await engine.reload('demo1');
339
+ console.log('已重载模块 demo1');
340
+ }, 10000);
341
+
342
+ setTimeout(async () => {
343
+ // 卸载模块
344
+ await engine.unload('demo2');
345
+ console.log('已卸载模块 demo2');
346
+ }, 20000);
167
347
  }
348
+
349
+ setup().catch(console.error);
168
350
  ```
169
351
 
170
- ## 高级功能
352
+ ### 示例4:错误处理和异常捕获
171
353
 
172
- ### 热更新
354
+ ```javascript
355
+ const engine = new Machine();
356
+
357
+ async function run() {
358
+ try {
359
+ // 加载模块并捕获可能的错误
360
+ await engine.load_list();
361
+
362
+ // 安全地执行模块方法
363
+ try {
364
+ const result = await engine.execute('main');
365
+ console.log('执行结果:', result);
366
+ } catch (err) {
367
+ console.error('执行方法时出错:', err);
368
+ // 继续执行,不会中断整个应用
369
+ }
370
+
371
+ // 单独执行特定模块并捕获可能的错误
372
+ try {
373
+ const demo1Result = await engine.execute('main', 'demo1');
374
+ console.log('demo1 执行结果:', demo1Result);
375
+ } catch (err) {
376
+ console.error('执行 demo1 时出错:', err);
377
+ }
378
+ } catch (err) {
379
+ console.error('初始化失败:', err);
380
+ }
381
+ }
173
382
 
174
- 系统支持多种运行模式,可以通过设置 `mode` 属性来控制:
383
+ run();
384
+ ```
175
385
 
176
- 1. 生产模式 (mode = 1):文件改变不会触发重新加载
177
- 2. 热更新模式 (mode = 2):文件改变时自动重新加载
178
- 3. 重载模式 (mode = 3):执行完后重新加载,避免变量污染
179
- 4. 热更新+重载模式 (mode = 4):结合了模式2和3的特性
386
+ ## 高级功能
180
387
 
181
388
  ### 模块生命周期
182
389
 
183
- 模块支持以下生命周期方法:
390
+ 每个模块支持完整的生命周期方法:
184
391
 
185
- - `init`: 初始化时调用
186
- - `main`: 主要执行方法
392
+ - `init`: 模块初始化时调用
393
+ - `load`: 模块加载时调用
394
+ - `main`: 主执行方法
187
395
  - `main_before`: 主方法执行前的钩子
188
396
  - `main_after`: 主方法执行后的钩子
189
397
 
190
- ### 模块管理
398
+ ### 运行模式说明
399
+
400
+ 系统支持4种运行模式,可通过`mode`属性设置:
401
+
402
+ 1. **生产模式** (`mode = 1`): 最高性能,文件改变不会触发重新加载
403
+ 2. **热更新模式** (`mode = 2`): 文件改变时自动重新加载配置和代码
404
+ 3. **重载模式** (`mode = 3`): 执行完后重新加载脚本,避免变量污染
405
+ 4. **热更新+重载模式** (`mode = 4`): 结合热更新和重载的特性
406
+
407
+ ### 模块管理API
191
408
 
192
409
  ```javascript
193
- // 重载模块
194
- engine.reload("demo1");
410
+ // 重载特定模块
411
+ await engine.reload("moduleName");
412
+
413
+ // 卸载模块(保留文件)
414
+ await engine.unload("moduleName");
195
415
 
196
- // 卸载模块
197
- engine.unload("demo2");
416
+ // 卸载并删除模块文件
417
+ await engine.unload("moduleName", true);
198
418
 
199
- // 卸载并删除模块
200
- engine.unload("demo1", true);
419
+ // 保存模块配置
420
+ await engine.save();
421
+
422
+ // 获取特定模块
423
+ const module = engine.get("moduleName");
201
424
  ```
202
425
 
426
+ ### 路径处理
427
+
428
+ - 所有相对路径都会基于模块的目录进行解析
429
+ - 使用`fullname()`方法可以获取绝对路径,避免路径问题
430
+
431
+ ## 常见问题与故障排除
432
+
433
+ ### 1. 模块无法加载
434
+
435
+ **检查项目:**
436
+ - 确认配置文件命名格式正确 (`config_{type}.json`)
437
+ - 确认`type`属性与配置文件前缀匹配
438
+ - 检查模块的`state`配置是否为1(启用)
439
+ - 检查文件权限是否正确
440
+
441
+ ### 2. 热更新不工作
442
+
443
+ **检查项目:**
444
+ - 确认`mode`设置为2或4(热更新模式)
445
+ - 确认文件确实被修改了(注意编辑器的保存设置)
446
+ - 检查文件路径是否正确
447
+
448
+ ### 3. 执行方法返回null
449
+
450
+ **检查项目:**
451
+ - 确认模块已正确加载 (`engine.list.length > 0`)
452
+ - 确认方法名拼写正确
453
+ - 检查模块实现中是否正确导出了该方法
454
+ - 检查模块的`state`是否为1(启用状态)
455
+
456
+ ### 4. 路径错误
457
+
458
+ **解决方案:**
459
+ - 使用绝对路径或确保相对路径正确
460
+ - 利用`__dirname`和`fullname()`方法构建可靠的路径
461
+
462
+ ## 模块系统设计理念与工作流程
463
+
464
+ ### 设计理念
465
+
466
+ mm_machine 模块系统基于以下核心设计理念:
467
+
468
+ 1. **高内聚、低耦合**:每个模块独立封装功能,通过明确的接口与其他模块交互
469
+ 2. **可插拔架构**:模块可以动态加载、卸载,不影响系统整体运行
470
+ 3. **配置驱动**:通过配置文件控制模块行为,无需修改代码
471
+ 4. **生命周期管理**:提供完整的模块生命周期钩子,便于资源管理
472
+ 5. **热更新支持**:无需重启应用即可更新模块功能
473
+
474
+ ### 工作流程
475
+
476
+ 以下是模块系统的基本工作流程:
477
+
478
+ ```
479
+ 初始化引擎 → 扫描模块目录 → 解析配置文件 → 加载模块代码 → 初始化模块 → 执行方法 → 监听文件变化(热更新模式)
480
+ ```
481
+
482
+ 详细工作流程说明:
483
+
484
+ 1. **初始化引擎**:创建 Machine 实例,设置配置项
485
+ 2. **扫描模块目录**:根据 searchPath 扫描目录,查找 `config_{type}.json` 文件
486
+ 3. **解析配置文件**:读取并解析配置,验证模块状态
487
+ 4. **加载模块代码**:根据配置中的信息,动态加载模块的实现文件
488
+ 5. **初始化模块**:按排序顺序初始化模块,执行 init 和 load 方法
489
+ 6. **执行方法**:根据应用需求,执行模块的特定方法(如 main)
490
+ 7. **监听文件变化**:在热更新模式下,监听文件变化并自动重新加载模块
491
+
492
+ ### 数据流向
493
+
494
+ 模块间的数据流转遵循以下规则:
495
+
496
+ 1. 引擎将调用参数传递给模块方法
497
+ 2. 模块方法处理后返回结果
498
+ 3. 钩子方法可以拦截和修改输入/输出数据
499
+ 4. 模块可以访问自己的配置,但不能直接访问其他模块的数据
500
+
501
+ ## 最佳实践与开发建议
502
+
503
+ ### 模块设计最佳实践
504
+
505
+ 1. **单一职责原则**:每个模块应专注于单一功能领域
506
+ 2. **接口一致性**:遵循模块生命周期接口约定,确保兼容性
507
+ 3. **错误处理**:在模块内部实现完善的错误处理机制
508
+ 4. **日志记录**:使用统一的日志接口记录模块活动
509
+ 5. **资源管理**:在适当的生命周期钩子中释放资源
510
+
511
+ ### 配置管理建议
512
+
513
+ 1. **配置分层**:将公共配置和模块特有配置分开
514
+ 2. **配置验证**:在模块初始化时验证配置有效性
515
+ 3. **默认值处理**:为重要配置项提供合理的默认值
516
+ 4. **敏感信息保护**:避免在配置文件中直接存储敏感信息
517
+
518
+ ### 性能优化建议
519
+
520
+ 1. **懒加载**:对于资源密集型模块,考虑实现懒加载机制
521
+ 2. **缓存策略**:合理使用缓存减少重复计算
522
+ 3. **异步操作**:使用 async/await 处理异步操作,避免阻塞
523
+ 4. **减少全局状态**:尽量减少模块间的状态共享
524
+
525
+ ### 调试技巧
526
+
527
+ 1. **日志级别**:利用不同级别的日志(debug、info、error)辅助调试
528
+ 2. **热更新调试**:使用热更新模式(mode=2)进行开发调试
529
+ 3. **模块隔离**:出现问题时,尝试单独测试特定模块
530
+ 4. **配置检查**:验证配置文件格式和内容是否正确
531
+
532
+ ### 部署注意事项
533
+
534
+ 1. **生产环境模式**:部署到生产环境时,设置 mode=1 以获得最佳性能
535
+ 2. **配置备份**:定期备份模块配置文件
536
+ 3. **版本控制**:对模块代码和配置进行版本控制
537
+ 4. **依赖管理**:明确声明和管理模块依赖
538
+
539
+ ## 贡献指南
540
+
541
+ 如果您有兴趣为 mm_machine 项目贡献代码或改进,请遵循以下步骤:
542
+
543
+ 1. Fork 项目仓库
544
+ 2. 创建您的功能分支 (`git checkout -b feature/amazing-feature`)
545
+ 3. 提交您的更改 (`git commit -m 'Add some amazing feature'`)
546
+ 4. 推送到分支 (`git push origin feature/amazing-feature`)
547
+ 5. 打开 Pull Request
548
+
549
+ ## 更新日志
550
+
551
+ ### v1.0.0 (初始版本)
552
+ - 基础模块管理功能
553
+ - 配置文件加载和解析
554
+ - 模块生命周期管理
555
+ - 热更新支持
556
+ - 基础错误处理
557
+
558
+ ### v1.1.0
559
+ - 改进模块加载逻辑,支持自定义目录
560
+ - 增强错误处理和异常捕获
561
+ - 添加更多生命周期钩子
562
+ - 优化性能和内存使用
563
+
203
564
  ## 依赖
204
565
 
205
566
  - mm_config: ^1.1.4
206
567
  - mm_hot_reload: ^1.0.5
568
+ - mm_expand: 用于路径处理和文件操作
207
569
 
208
570
  ## 许可证
209
571
 
210
572
  ISC
573
+
574
+ ## 作者信息
575
+
576
+ 本模块由 mm_modules 团队开发和维护。如有问题或建议,请联系我们。
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "test1",
3
+ "title": "测试模块1",
4
+ "description": "完整测试模块1",
5
+ "func_file": "./index.js",
6
+ "func_name": "",
7
+ "sort": 10,
8
+ "state": 1,
9
+ "show": 1
10
+ }