mm_jsondb 0.0.0-stage → 1.0.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.
Files changed (186) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +402 -3
  3. package/eslint.config.js +235 -0
  4. package/index.js +16 -0
  5. package/index_manager.js +249 -0
  6. package/lib/db.js +612 -0
  7. package/lib/json_db.js +374 -0
  8. package/lib/sql.js +1157 -0
  9. package/lib/store.js +933 -0
  10. package/package.json +47 -3
  11. package/rpg_db/game_1785973149083/attributes/1.json +9 -0
  12. package/rpg_db/game_1785973149083/attributes/2.json +9 -0
  13. package/rpg_db/game_1785973149083/attributes/3.json +9 -0
  14. package/rpg_db/game_1785973149083/attributes/_meta.json +72 -0
  15. package/rpg_db/game_1785973149083/characters/1.json +11 -0
  16. package/rpg_db/game_1785973149083/characters/2.json +10 -0
  17. package/rpg_db/game_1785973149083/characters/3.json +10 -0
  18. package/rpg_db/game_1785973149083/characters/_index/class/_E5_BC_93_E7_AE_AD_E6_89_8B.json +1 -0
  19. package/rpg_db/game_1785973149083/characters/_index/class/_E6_88_98_E5_A3_AB.json +1 -0
  20. package/rpg_db/game_1785973149083/characters/_index/class/_E6_B3_95_E5_B8_88.json +1 -0
  21. package/rpg_db/game_1785973149083/characters/_meta.json +92 -0
  22. package/rpg_db/game_1785973149083/inventory/1.json +7 -0
  23. package/rpg_db/game_1785973149083/inventory/10.json +7 -0
  24. package/rpg_db/game_1785973149083/inventory/11.json +7 -0
  25. package/rpg_db/game_1785973149083/inventory/14.json +7 -0
  26. package/rpg_db/game_1785973149083/inventory/15.json +7 -0
  27. package/rpg_db/game_1785973149083/inventory/16.json +7 -0
  28. package/rpg_db/game_1785973149083/inventory/2.json +7 -0
  29. package/rpg_db/game_1785973149083/inventory/3.json +7 -0
  30. package/rpg_db/game_1785973149083/inventory/4.json +7 -0
  31. package/rpg_db/game_1785973149083/inventory/5.json +7 -0
  32. package/rpg_db/game_1785973149083/inventory/6.json +7 -0
  33. package/rpg_db/game_1785973149083/inventory/7.json +7 -0
  34. package/rpg_db/game_1785973149083/inventory/8.json +7 -0
  35. package/rpg_db/game_1785973149083/inventory/9.json +7 -0
  36. package/rpg_db/game_1785973149083/inventory/_meta.json +54 -0
  37. package/rpg_db/game_1785973149083/items/1.json +9 -0
  38. package/rpg_db/game_1785973149083/items/2.json +9 -0
  39. package/rpg_db/game_1785973149083/items/3.json +9 -0
  40. package/rpg_db/game_1785973149083/items/4.json +9 -0
  41. package/rpg_db/game_1785973149083/items/5.json +9 -0
  42. package/rpg_db/game_1785973149083/items/6.json +9 -0
  43. package/rpg_db/game_1785973149083/items/7.json +9 -0
  44. package/rpg_db/game_1785973149083/items/8.json +9 -0
  45. package/rpg_db/game_1785973149083/items/_index/rarity/_E4_BC_A0_E8_AF_B4.json +1 -0
  46. package/rpg_db/game_1785973149083/items/_index/rarity/_E5_8F_B2_E8_AF_97.json +1 -0
  47. package/rpg_db/game_1785973149083/items/_index/rarity/_E6_99_AE_E9_80_9A.json +1 -0
  48. package/rpg_db/game_1785973149083/items/_index/rarity/_E7_A8_80_E6_9C_89.json +1 -0
  49. package/rpg_db/game_1785973149083/items/_meta.json +74 -0
  50. package/rpg_db/game_1785973267458/attributes/1.json +9 -0
  51. package/rpg_db/game_1785973267458/attributes/2.json +9 -0
  52. package/rpg_db/game_1785973267458/attributes/3.json +9 -0
  53. package/rpg_db/game_1785973267458/attributes/_meta.json +72 -0
  54. package/rpg_db/game_1785973267458/characters/1.json +11 -0
  55. package/rpg_db/game_1785973267458/characters/2.json +10 -0
  56. package/rpg_db/game_1785973267458/characters/3.json +10 -0
  57. package/rpg_db/game_1785973267458/characters/_index/class/_E5_BC_93_E7_AE_AD_E6_89_8B.json +1 -0
  58. package/rpg_db/game_1785973267458/characters/_index/class/_E6_88_98_E5_A3_AB.json +1 -0
  59. package/rpg_db/game_1785973267458/characters/_index/class/_E6_B3_95_E5_B8_88.json +1 -0
  60. package/rpg_db/game_1785973267458/characters/_meta.json +92 -0
  61. package/rpg_db/game_1785973267458/inventory/1.json +7 -0
  62. package/rpg_db/game_1785973267458/inventory/10.json +7 -0
  63. package/rpg_db/game_1785973267458/inventory/11.json +7 -0
  64. package/rpg_db/game_1785973267458/inventory/14.json +7 -0
  65. package/rpg_db/game_1785973267458/inventory/15.json +7 -0
  66. package/rpg_db/game_1785973267458/inventory/16.json +7 -0
  67. package/rpg_db/game_1785973267458/inventory/2.json +7 -0
  68. package/rpg_db/game_1785973267458/inventory/3.json +7 -0
  69. package/rpg_db/game_1785973267458/inventory/4.json +7 -0
  70. package/rpg_db/game_1785973267458/inventory/5.json +7 -0
  71. package/rpg_db/game_1785973267458/inventory/6.json +7 -0
  72. package/rpg_db/game_1785973267458/inventory/7.json +7 -0
  73. package/rpg_db/game_1785973267458/inventory/8.json +7 -0
  74. package/rpg_db/game_1785973267458/inventory/9.json +7 -0
  75. package/rpg_db/game_1785973267458/inventory/_meta.json +54 -0
  76. package/rpg_db/game_1785973267458/items/1.json +9 -0
  77. package/rpg_db/game_1785973267458/items/2.json +9 -0
  78. package/rpg_db/game_1785973267458/items/3.json +9 -0
  79. package/rpg_db/game_1785973267458/items/4.json +9 -0
  80. package/rpg_db/game_1785973267458/items/5.json +9 -0
  81. package/rpg_db/game_1785973267458/items/6.json +9 -0
  82. package/rpg_db/game_1785973267458/items/7.json +9 -0
  83. package/rpg_db/game_1785973267458/items/8.json +9 -0
  84. package/rpg_db/game_1785973267458/items/_index/rarity/_E4_BC_A0_E8_AF_B4.json +1 -0
  85. package/rpg_db/game_1785973267458/items/_index/rarity/_E5_8F_B2_E8_AF_97.json +1 -0
  86. package/rpg_db/game_1785973267458/items/_index/rarity/_E6_99_AE_E9_80_9A.json +1 -0
  87. package/rpg_db/game_1785973267458/items/_index/rarity/_E7_A8_80_E6_9C_89.json +1 -0
  88. package/rpg_db/game_1785973267458/items/_meta.json +74 -0
  89. package/rpg_db/game_1785974596793/attributes/1.json +9 -0
  90. package/rpg_db/game_1785974596793/attributes/2.json +9 -0
  91. package/rpg_db/game_1785974596793/attributes/3.json +9 -0
  92. package/rpg_db/game_1785974596793/attributes/_meta.json +72 -0
  93. package/rpg_db/game_1785974596793/characters/1.json +11 -0
  94. package/rpg_db/game_1785974596793/characters/2.json +10 -0
  95. package/rpg_db/game_1785974596793/characters/3.json +10 -0
  96. package/rpg_db/game_1785974596793/characters/_index/class/_E5_BC_93_E7_AE_AD_E6_89_8B.json +1 -0
  97. package/rpg_db/game_1785974596793/characters/_index/class/_E6_88_98_E5_A3_AB.json +1 -0
  98. package/rpg_db/game_1785974596793/characters/_index/class/_E6_B3_95_E5_B8_88.json +1 -0
  99. package/rpg_db/game_1785974596793/characters/_meta.json +92 -0
  100. package/rpg_db/game_1785974596793/inventory/1.json +7 -0
  101. package/rpg_db/game_1785974596793/inventory/10.json +7 -0
  102. package/rpg_db/game_1785974596793/inventory/11.json +7 -0
  103. package/rpg_db/game_1785974596793/inventory/14.json +7 -0
  104. package/rpg_db/game_1785974596793/inventory/15.json +7 -0
  105. package/rpg_db/game_1785974596793/inventory/16.json +7 -0
  106. package/rpg_db/game_1785974596793/inventory/2.json +7 -0
  107. package/rpg_db/game_1785974596793/inventory/3.json +7 -0
  108. package/rpg_db/game_1785974596793/inventory/4.json +7 -0
  109. package/rpg_db/game_1785974596793/inventory/5.json +7 -0
  110. package/rpg_db/game_1785974596793/inventory/6.json +7 -0
  111. package/rpg_db/game_1785974596793/inventory/7.json +7 -0
  112. package/rpg_db/game_1785974596793/inventory/8.json +7 -0
  113. package/rpg_db/game_1785974596793/inventory/9.json +7 -0
  114. package/rpg_db/game_1785974596793/inventory/_meta.json +54 -0
  115. package/rpg_db/game_1785974596793/items/1.json +9 -0
  116. package/rpg_db/game_1785974596793/items/2.json +9 -0
  117. package/rpg_db/game_1785974596793/items/3.json +9 -0
  118. package/rpg_db/game_1785974596793/items/4.json +9 -0
  119. package/rpg_db/game_1785974596793/items/5.json +9 -0
  120. package/rpg_db/game_1785974596793/items/6.json +9 -0
  121. package/rpg_db/game_1785974596793/items/7.json +9 -0
  122. package/rpg_db/game_1785974596793/items/8.json +9 -0
  123. package/rpg_db/game_1785974596793/items/_index/rarity/_E4_BC_A0_E8_AF_B4.json +1 -0
  124. package/rpg_db/game_1785974596793/items/_index/rarity/_E5_8F_B2_E8_AF_97.json +1 -0
  125. package/rpg_db/game_1785974596793/items/_index/rarity/_E6_99_AE_E9_80_9A.json +1 -0
  126. package/rpg_db/game_1785974596793/items/_index/rarity/_E7_A8_80_E6_9C_89.json +1 -0
  127. package/rpg_db/game_1785974596793/items/_meta.json +74 -0
  128. package/rpg_test.js +644 -0
  129. package/test.js +407 -0
  130. package/test_db/test_1785972991156/users/1.json +5 -0
  131. package/test_db/test_1785972991156/users/_meta.json +36 -0
  132. package/test_db/test_1785973053110/users/1.json +5 -0
  133. package/test_db/test_1785973053110/users/_meta.json +36 -0
  134. package/test_db/test_1785973130182/users/1.json +5 -0
  135. package/test_db/test_1785973130182/users/_meta.json +36 -0
  136. package/test_db/test_1785973148614/test_users/1.json +6 -0
  137. package/test_db/test_1785973148614/test_users/2.json +6 -0
  138. package/test_db/test_1785973148614/test_users/3.json +6 -0
  139. package/test_db/test_1785973148614/test_users/5.json +6 -0
  140. package/test_db/test_1785973148614/test_users/6.json +6 -0
  141. package/test_db/test_1785973148614/test_users/7.json +6 -0
  142. package/test_db/test_1785973148614/test_users/8.json +6 -0
  143. package/test_db/test_1785973148614/test_users/9.json +6 -0
  144. package/test_db/test_1785973148614/test_users/_index/email/batch1_40test.com.json +1 -0
  145. package/test_db/test_1785973148614/test_users/_index/email/batch2_40test.com.json +1 -0
  146. package/test_db/test_1785973148614/test_users/_index/email/batch3_40test.com.json +1 -0
  147. package/test_db/test_1785973148614/test_users/_index/email/lisi_40test.com.json +1 -0
  148. package/test_db/test_1785973148614/test_users/_index/email/tx1_40test.com.json +1 -0
  149. package/test_db/test_1785973148614/test_users/_index/email/tx2_40test.com.json +1 -0
  150. package/test_db/test_1785973148614/test_users/_index/email/wangwu_40test.com.json +1 -0
  151. package/test_db/test_1785973148614/test_users/_index/email/zhangsan_new_40test.com.json +1 -0
  152. package/test_db/test_1785973148614/test_users/_meta.json +29 -0
  153. package/test_db/test_1785973266950/test_users/1.json +6 -0
  154. package/test_db/test_1785973266950/test_users/2.json +6 -0
  155. package/test_db/test_1785973266950/test_users/3.json +6 -0
  156. package/test_db/test_1785973266950/test_users/5.json +6 -0
  157. package/test_db/test_1785973266950/test_users/6.json +6 -0
  158. package/test_db/test_1785973266950/test_users/7.json +6 -0
  159. package/test_db/test_1785973266950/test_users/8.json +6 -0
  160. package/test_db/test_1785973266950/test_users/9.json +6 -0
  161. package/test_db/test_1785973266950/test_users/_index/email/batch1_40test.com.json +1 -0
  162. package/test_db/test_1785973266950/test_users/_index/email/batch2_40test.com.json +1 -0
  163. package/test_db/test_1785973266950/test_users/_index/email/batch3_40test.com.json +1 -0
  164. package/test_db/test_1785973266950/test_users/_index/email/lisi_40test.com.json +1 -0
  165. package/test_db/test_1785973266950/test_users/_index/email/tx1_40test.com.json +1 -0
  166. package/test_db/test_1785973266950/test_users/_index/email/tx2_40test.com.json +1 -0
  167. package/test_db/test_1785973266950/test_users/_index/email/wangwu_40test.com.json +1 -0
  168. package/test_db/test_1785973266950/test_users/_index/email/zhangsan_new_40test.com.json +1 -0
  169. package/test_db/test_1785973266950/test_users/_meta.json +29 -0
  170. package/test_db/test_1785974596281/test_users/1.json +6 -0
  171. package/test_db/test_1785974596281/test_users/2.json +6 -0
  172. package/test_db/test_1785974596281/test_users/3.json +6 -0
  173. package/test_db/test_1785974596281/test_users/5.json +6 -0
  174. package/test_db/test_1785974596281/test_users/6.json +6 -0
  175. package/test_db/test_1785974596281/test_users/7.json +6 -0
  176. package/test_db/test_1785974596281/test_users/8.json +6 -0
  177. package/test_db/test_1785974596281/test_users/9.json +6 -0
  178. package/test_db/test_1785974596281/test_users/_index/email/batch1_40test.com.json +1 -0
  179. package/test_db/test_1785974596281/test_users/_index/email/batch2_40test.com.json +1 -0
  180. package/test_db/test_1785974596281/test_users/_index/email/batch3_40test.com.json +1 -0
  181. package/test_db/test_1785974596281/test_users/_index/email/lisi_40test.com.json +1 -0
  182. package/test_db/test_1785974596281/test_users/_index/email/tx1_40test.com.json +1 -0
  183. package/test_db/test_1785974596281/test_users/_index/email/tx2_40test.com.json +1 -0
  184. package/test_db/test_1785974596281/test_users/_index/email/wangwu_40test.com.json +1 -0
  185. package/test_db/test_1785974596281/test_users/_index/email/zhangsan_new_40test.com.json +1 -0
  186. package/test_db/test_1785974596281/test_users/_meta.json +29 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Admin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,402 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # mm_jsondb
2
+
3
+ 一个基于 JSON 文件存储的 Node.js 数据库操作库,与 `mm_mysql` 保持完全一致的 API 接口,无需安装任何数据库服务。
4
+
5
+ ## 架构设计
6
+
7
+ ```
8
+ db/ # 数据库根目录
9
+ └── game/ # 数据库名 = 目录名
10
+ ├── characters/ # 表名 = 子目录名
11
+ │ ├── _meta.json # 表元数据(字段定义、索引配置)
12
+ │ ├── 1.json # 每行数据 = 一个 JSON 文件
13
+ │ ├── 2.json
14
+ │ └── _index/ # 索引目录
15
+ │ └── class/ # 按字段名分类
16
+ │ └── _E68898E5A3AB.json # 字段值 → 行ID列表
17
+ ├── items/
18
+ │ ├── _meta.json
19
+ │ ├── 1.json
20
+ │ └── _index/
21
+ └── inventory/
22
+ ```
23
+
24
+ ## 安装
25
+
26
+ ```bash
27
+ npm install mm_jsondb
28
+ ```
29
+
30
+ ## 基本使用
31
+
32
+ ```javascript
33
+ const { JsonDB } = require('mm_jsondb');
34
+
35
+ // 创建数据库实例
36
+ const db = new JsonDB({
37
+ dir: './data/', // 数据存储目录
38
+ database: 'game' // 数据库名
39
+ });
40
+
41
+ // 打开数据库
42
+ await db.open();
43
+
44
+ // 获取表管理器
45
+ const userDb = db.table('users', 'id');
46
+
47
+ // 增删改查
48
+ await userDb.add({ name: '张三', level: 10, gold: 5000 });
49
+ const list = await userDb.get({ level_min: 5 }, 'level DESC');
50
+ await userDb.set({ name: '张三' }, { level: 11 });
51
+ await userDb.del({ name: '张三' });
52
+
53
+ // 关闭
54
+ await db.close();
55
+ ```
56
+
57
+ ## 主要特性
58
+
59
+ - 🗂️ **零依赖数据库**:无需安装 MySQL、SQLite 等服务,纯 JSON 文件存储
60
+ - 🔄 **完全兼容 mm_mysql**:相同的 API 接口,无缝切换
61
+ - ⚡ **字段索引**:支持按字段建立索引,快速搜索
62
+ - 🔒 **事务支持**:完整的提交/回滚机制,保证数据一致性
63
+ - 📖 **人类可读**:JSON 格式存储,调试和手动编辑极其方便
64
+ - 📦 **轻量级**:适用于小型项目、原型开发、游戏存档
65
+
66
+ ## API 文档
67
+
68
+ ### JsonDB 类
69
+
70
+ #### 构造函数
71
+
72
+ ```javascript
73
+ const db = new JsonDB({
74
+ dir: './data/', // 数据存储目录,默认 './db/'
75
+ database: 'mm', // 数据库名,对应目录名
76
+ debug: false // 是否开启调试日志
77
+ });
78
+ ```
79
+
80
+ #### 配置参数
81
+
82
+ | 参数 | 类型 | 默认值 | 说明 |
83
+ |------|------|--------|------|
84
+ | `dir` | String | `'./db/'` | 数据存储根目录 |
85
+ | `database` | String | `'mm'` | 数据库名称 |
86
+ | `debug` | Boolean | `false` | 调试日志开关 |
87
+
88
+ #### 主要方法
89
+
90
+ ##### open()
91
+ 打开数据库(创建存储目录)
92
+ - 返回:`Promise<boolean>`
93
+
94
+ ##### close()
95
+ 关闭数据库
96
+ - 返回:`Promise<boolean>`
97
+
98
+ ##### table(name, key)
99
+ 获取表管理器
100
+ - `name` {String} 表名
101
+ - `key` {String} 主键字段名,默认自动推断
102
+ - 返回:`DB` 表管理器实例
103
+
104
+ ##### db()
105
+ 获取数据库管理器
106
+ - 返回:`DB` 数据库管理器实例
107
+
108
+ ##### run(descriptor, params)
109
+ 执行查询操作
110
+ - `descriptor` {Object} 查询描述符
111
+ - 返回:`Promise<Array>`
112
+
113
+ ##### exec(descriptor, params)
114
+ 执行写入操作
115
+ - `descriptor` {Object} 操作描述符
116
+ - 返回:`Promise<number>`
117
+
118
+ ##### read(table, condition, options)
119
+ 读取表数据
120
+ - `table` {String} 表名
121
+ - `condition` {Object} 查询条件
122
+ - `options` {Object} 选项(`orderBy`、`page`、`size`)
123
+ - 返回:`Promise<Array>`
124
+
125
+ ##### transaction(callback)
126
+ 执行事务
127
+ - `callback` {Function} 事务回调函数
128
+ - 返回:`Promise<any>`
129
+
130
+ ##### healthCheck()
131
+ 健康检查
132
+ - 返回:`Promise<Object>` 状态信息
133
+
134
+ ### DB 类(表管理器)
135
+
136
+ #### 增删改查
137
+
138
+ ```javascript
139
+ const db = jsonDB.table('users', 'id');
140
+
141
+ // 添加
142
+ await db.add({ name: '张三', level: 10 });
143
+ await db.addList([{ name: '李四' }, { name: '王五' }]);
144
+
145
+ // 查询
146
+ await db.get(); // 查询全部
147
+ await db.get({ name: '张三' }); // 精确查询
148
+ await db.get({ name_like: '张' }); // 模糊查询
149
+ await db.get({ name_like: '张' }, null, null, true); // 显式模糊
150
+ await db.get({ level_min: 10, level_max: 20 }); // 范围查询
151
+ await db.get({ name_not: '张三' }); // 排除查询
152
+ await db.get({}, 'level DESC'); // 排序
153
+ await db.getObj({ name: '张三' }); // 获取单条
154
+ await db.count(); // 统计总数
155
+ await db.count({ level_min: 10 }); // 条件统计
156
+
157
+ // 修改
158
+ await db.set({ name: '张三' }, { level: 11 });
159
+ await db.set({ name: '张三' }, { gold_add: 500 }); // 数值增加
160
+ await db.set({ name: '张三' }, { hp_del: 100 }); // 数值减少
161
+
162
+ // 删除
163
+ await db.del({ name: '张三' });
164
+ await db.del({ name_like: '测试' });
165
+ ```
166
+
167
+ #### 操作符说明
168
+
169
+ | 后缀 | 示例 | 含义 |
170
+ |------|------|------|
171
+ | `_min` | `level_min: 10` | `>=` |
172
+ | `_max` | `level_max: 20` | `<=` |
173
+ | `_not` | `name_not: '张三'` | `!=` |
174
+ | `_like` | `name_like: '张'` | 模糊匹配 |
175
+ | `_has` | `tags_has: 'a,b'` | 包含任一值 |
176
+ | `_add` | `gold_add: 100` | 数值增加 |
177
+ | `_del` | `hp_del: 50` | 数值减少 |
178
+
179
+ #### 分页查询
180
+
181
+ ```javascript
182
+ const db = jsonDB.table('users');
183
+ db.page = 1; // 第1页
184
+ db.size = 20; // 每页20条
185
+ const list = await db.get();
186
+ const total = await db.count();
187
+ ```
188
+
189
+ #### 表管理
190
+
191
+ ```javascript
192
+ const db = jsonDB.db();
193
+
194
+ // 创建表
195
+ await db.addTable('users', 'id', 'int', true, '用户表');
196
+
197
+ // 添加字段
198
+ await db.addField('phone', 'VARCHAR(20)', '', false, false, '电话');
199
+
200
+ // 修改字段
201
+ await db.setField('phone', 'VARCHAR(30)', '', false, false);
202
+
203
+ // 删除字段
204
+ await db.delField('users', 'phone');
205
+
206
+ // 重命名字段
207
+ await db.renameField('users', 'phone', 'mobile', 'VARCHAR(20)');
208
+
209
+ // 获取所有表
210
+ const tables = await db.tables();
211
+
212
+ // 获取所有字段
213
+ const fields = await db.fields('users');
214
+
215
+ // 检查表/字段是否存在
216
+ await db.hasTable('users');
217
+ await db.hasField('users', 'phone');
218
+
219
+ // 清空表
220
+ await db.emptyTable('users');
221
+
222
+ // 删除表
223
+ await db.dropTable('users');
224
+
225
+ // 备份表
226
+ await db.backupTable('users', 'users_backup');
227
+
228
+ // 获取建表语句
229
+ const sql = await db.getCreateTable('users');
230
+ ```
231
+
232
+ #### 事务
233
+
234
+ ```javascript
235
+ const db = jsonDB.table('users');
236
+
237
+ // 事务提交
238
+ await jsonDB.transaction(async () => {
239
+ await db.set({ name: '张三' }, { gold_del: 1000 });
240
+ await db.add({ name: '李四', gold: 1000 });
241
+ });
242
+
243
+ // 事务回滚(出错自动回滚)
244
+ try {
245
+ await jsonDB.transaction(async () => {
246
+ await db.set({ name: '张三' }, { gold_del: 99999 });
247
+ if (gold < 0) throw new Error('金币不足');
248
+ });
249
+ } catch {
250
+ // 数据已自动回滚
251
+ }
252
+ ```
253
+
254
+ #### 索引
255
+
256
+ ```javascript
257
+ const store = jsonDB.getStore();
258
+ const indexMgr = store.getIndexManager();
259
+
260
+ // 建立索引
261
+ const meta = await store.readMeta('items');
262
+ meta.indexes.push('rarity');
263
+ await store.writeMeta('items', meta);
264
+ await indexMgr.ensureIndex('items', 'rarity');
265
+
266
+ // 构建索引
267
+ const rows = await store.readAllRows('items');
268
+ for (const row of rows) {
269
+ await indexMgr.addToIndex('items', row.id, row);
270
+ }
271
+
272
+ // 通过索引快速查询
273
+ const ids = await indexMgr.queryByIndex('items', 'rarity', '传说');
274
+ ```
275
+
276
+ ## 游戏开发场景
277
+
278
+ ### 适用场景
279
+
280
+ | 场景 | 说明 |
281
+ |------|------|
282
+ | **单机 RPG 存档** | 角色数据、背包、任务进度、地图状态 |
283
+ | **游戏配置管理** | 道具定义、技能树、关卡数据、NPC 对话 |
284
+ | **Electron 桌面游戏** | 本地数据持久化,无需额外安装数据库 |
285
+ | **网页小游戏** | 不需服务器,纯前端存储 |
286
+ | **回合制游戏** | 存档/读档,战棋状态保存 |
287
+ | **成就系统** | 成就进度、解锁记录持久化 |
288
+ | **MOD 工具** | 玩家可手动编辑 JSON 文件修改存档 |
289
+ | **快速原型** | 开发阶段无需搭建数据库,直接读写文件 |
290
+
291
+ ### 游戏示例:RPG 角色系统
292
+
293
+ ```javascript
294
+ const { JsonDB } = require('mm_jsondb');
295
+
296
+ async function initGameDB() {
297
+ const db = new JsonDB({ dir: './save/', database: 'rpg_save' });
298
+ await db.open();
299
+
300
+ // 创建表
301
+ const mgr = db.db();
302
+ await mgr.addTable('characters', 'id', 'int', true, '角色表');
303
+ await mgr.addTable('inventory', 'id', 'int', true, '背包表');
304
+ await mgr.addTable('items', 'id', 'int', true, '道具表');
305
+ await mgr.addTable('quests', 'id', 'int', true, '任务表');
306
+
307
+ // 添加字段
308
+ const charDb = db.table('characters');
309
+ await charDb.addField('name', 'VARCHAR(50)', '', true);
310
+ await charDb.addField('level', 'INT', '1', true);
311
+ await charDb.addField('gold', 'INT', '0', true);
312
+ await charDb.addField('class', 'VARCHAR(20)', '', true);
313
+ await charDb.addField('hp', 'INT', '100', true);
314
+ await charDb.addField('mp', 'INT', '50', true);
315
+
316
+ return db;
317
+ }
318
+
319
+ // 创建角色
320
+ async function createCharacter(db, name, className) {
321
+ const charDb = db.table('characters');
322
+ const stats = {
323
+ '战士': { hp: 850, mp: 200 },
324
+ '法师': { hp: 400, mp: 950 },
325
+ '弓箭手': { hp: 550, mp: 350 }
326
+ };
327
+ const s = stats[className] || { hp: 500, mp: 300 };
328
+ return await charDb.add({
329
+ name, level: 1, gold: 100, class: className,
330
+ hp: s.hp, mp: s.mp
331
+ });
332
+ }
333
+
334
+ // 获取背包物品详情(跨表关联)
335
+ async function getInventory(db, characterId) {
336
+ const invDb = db.table('inventory');
337
+ const itemDb = db.table('items');
338
+ const slots = await invDb.get({ character_id: characterId });
339
+ const detail = [];
340
+ for (const slot of slots) {
341
+ const item = await itemDb.getObj({ id: slot.item_id });
342
+ detail.push({ ...item, quantity: slot.quantity, equipped: slot.equipped });
343
+ }
344
+ return detail;
345
+ }
346
+
347
+ // 购买道具(事务)
348
+ async function buyItem(db, characterId, itemId, quantity) {
349
+ const charDb = db.table('characters');
350
+ const itemDb = db.table('items');
351
+ const invDb = db.table('inventory');
352
+
353
+ return await db.transaction(async () => {
354
+ const char = await charDb.getObj({ id: characterId });
355
+ const item = await itemDb.getObj({ id: itemId });
356
+ const totalPrice = item.price * quantity;
357
+
358
+ if (char.gold < totalPrice) {
359
+ throw new Error('金币不足!');
360
+ }
361
+
362
+ await charDb.set({ id: characterId }, { gold_del: totalPrice });
363
+ await invDb.add({
364
+ character_id: characterId,
365
+ item_id: itemId,
366
+ quantity,
367
+ equipped: 0
368
+ });
369
+ return true;
370
+ });
371
+ }
372
+ ```
373
+
374
+ ### 与 mm_mysql 的对比
375
+
376
+ | 特性 | mm_mysql | mm_jsondb |
377
+ |------|----------|---------|
378
+ | 依赖 | MySQL 服务 | 无 |
379
+ | 存储格式 | 数据库表 | JSON 文件 |
380
+ | 查询语言 | SQL | 描述符对象 |
381
+ | 安装部署 | 需安装 MySQL | 零配置 |
382
+ | 性能 | 高并发优秀 | 小规模优秀 |
383
+ | 人类可读 | 否 | 是(直接看 JSON) |
384
+ | 适用场景 | 生产环境 | 原型/小项目/游戏 |
385
+ | API 兼容 | ✅ | ✅(完全兼容) |
386
+
387
+ ## 开发
388
+
389
+ ```bash
390
+ # 安装依赖
391
+ npm install
392
+
393
+ # 运行测试
394
+ npm test
395
+
396
+ # 运行 RPG 游戏示例测试
397
+ node rpg_test.js
398
+ ```
399
+
400
+ ## License
401
+
402
+ ISC
@@ -0,0 +1,235 @@
1
+ 'use strict';
2
+
3
+ const namingConventionPlugin = require('mm_eslint');
4
+
5
+ /**
6
+ * 基于个人规则的ESLint配置
7
+ * 符合最新ESLint Flat Config格式
8
+ */
9
+
10
+ module.exports = [
11
+ {
12
+ files: ['**/*.js'],
13
+ plugins: {
14
+ 'naming-convention': namingConventionPlugin,
15
+ jsdoc: require('eslint-plugin-jsdoc'),
16
+ },
17
+ languageOptions: {
18
+ ecmaVersion: 'latest',
19
+ sourceType: 'module',
20
+ parserOptions: {
21
+ ecmaVersion: 'latest',
22
+ sourceType: 'module',
23
+ },
24
+ },
25
+
26
+ // 命名规范规则 - 由自定义插件处理
27
+ rules: {
28
+ // 自定义命名规范插件规则(优先使用)
29
+ 'naming-convention/class-name': 'error',
30
+ 'naming-convention/function-name': 'error',
31
+ 'naming-convention/method-name': 'error',
32
+ 'naming-convention/variable-name': 'warn',
33
+ 'naming-convention/constant-name': 'error',
34
+ 'naming-convention/private-method-naming': 'warn',
35
+ 'naming-convention/private-variable-naming': 'warn',
36
+ 'naming-convention/param-name': 'warn',
37
+ 'naming-convention/property-name': 'warn',
38
+ 'naming-convention/instance-property': 'warn',
39
+ 'naming-convention/prototype-method': 'error',
40
+
41
+ // 禁用与命名规范插件冲突的默认规则
42
+ camelcase: 'off',
43
+ 'id-match': 'off',
44
+ 'new-cap': 'off',
45
+ 'id-length': 'off',
46
+ 'id-denylist': 'off',
47
+ 'id-blacklist': 'off',
48
+ },
49
+ },
50
+
51
+ {
52
+ // 代码风格规则
53
+ rules: {
54
+ // 最大行长度100字符
55
+ 'max-len': [
56
+ 'error',
57
+ {
58
+ code: 100,
59
+ ignoreComments: true,
60
+ ignoreUrls: true,
61
+ ignoreStrings: true,
62
+ ignoreTemplateLiterals: true,
63
+ },
64
+ ],
65
+
66
+ // 缩进2空格
67
+ indent: [
68
+ 'error',
69
+ 2,
70
+ {
71
+ SwitchCase: 1,
72
+ VariableDeclarator: 1,
73
+ outerIIFEBody: 1,
74
+ MemberExpression: 1,
75
+ FunctionDeclaration: { parameters: 1, body: 1 },
76
+ FunctionExpression: { parameters: 1, body: 1 },
77
+ CallExpression: { arguments: 1 },
78
+ ArrayExpression: 1,
79
+ ObjectExpression: 1,
80
+ ImportDeclaration: 1,
81
+ flatTernaryExpressions: false,
82
+ ignoreComments: false,
83
+ },
84
+ ],
85
+
86
+ // 单引号
87
+ quotes: [
88
+ 'error',
89
+ 'single',
90
+ {
91
+ avoidEscape: true,
92
+ allowTemplateLiterals: true,
93
+ },
94
+ ],
95
+
96
+ // 禁止制表符
97
+ 'no-tabs': 'error',
98
+
99
+ // 行尾分号
100
+ semi: ['error', 'always'],
101
+
102
+ // 逗号风格
103
+ 'comma-style': ['error', 'last'],
104
+
105
+ // 逗号间距
106
+ 'comma-spacing': [
107
+ 'error',
108
+ {
109
+ before: false,
110
+ after: true,
111
+ },
112
+ ],
113
+ },
114
+ },
115
+
116
+ {
117
+ // 错误处理规则
118
+ rules: {
119
+ // 必须进行参数校验
120
+ 'no-unused-vars': [
121
+ 'error',
122
+ {
123
+ args: 'all',
124
+ caughtErrors: 'all',
125
+ ignoreRestSiblings: true,
126
+ },
127
+ ],
128
+
129
+ // 建议使用try-catch
130
+ 'no-unsafe-finally': 'warn',
131
+
132
+ // 禁止直接抛出字符串
133
+ 'no-throw-literal': 'error',
134
+ },
135
+ },
136
+
137
+ {
138
+ // 最佳实践规则
139
+ rules: {
140
+ // 优先使用async/await
141
+ 'prefer-promise-reject-errors': 'error',
142
+
143
+ // 避免副作用
144
+ 'no-param-reassign': [
145
+ 'error',
146
+ {
147
+ props: true,
148
+ ignorePropertyModificationsFor: [
149
+ 'acc', // for reduce accumulators
150
+ 'accumulator', // for reduce accumulators
151
+ 'e', // for e.returnvalue
152
+ 'ctx', // for Koa routing
153
+ 'context', // for Koa routing
154
+ 'req', // for Express requests
155
+ 'request', // for Express requests
156
+ 'res', // for Express responses
157
+ 'response', // for Express responses
158
+ '$scope', // for Angular 1 scopes
159
+ 'staticContext', // for ReactRouter context
160
+ ],
161
+ },
162
+ ],
163
+
164
+ // 优先链式编程
165
+ 'prefer-object-spread': 'error',
166
+
167
+ // 优先函数式编程
168
+ 'prefer-arrow-callback': 'error',
169
+
170
+ // 方法长度限制
171
+ 'max-lines-per-function': [
172
+ 'warn',
173
+ {
174
+ max: 40,
175
+ skipBlankLines: true,
176
+ skipComments: true,
177
+ },
178
+ ],
179
+
180
+ // 复杂度限制
181
+ complexity: ['warn', 10],
182
+ },
183
+ },
184
+
185
+ {
186
+ // JSDoc规则
187
+ rules: {
188
+ // 要求公开方法有JSDoc注释
189
+ 'jsdoc/require-jsdoc': [
190
+ 'warn',
191
+ {
192
+ publicOnly: true,
193
+ require: {
194
+ FunctionDeclaration: true,
195
+ MethodDefinition: true,
196
+ ClassDeclaration: true,
197
+ ArrowFunctionExpression: false,
198
+ FunctionExpression: false,
199
+ },
200
+ },
201
+ ],
202
+
203
+ // 检查JSDoc语法
204
+ 'jsdoc/check-alignment': 'warn',
205
+ 'jsdoc/check-indentation': 'warn',
206
+ 'jsdoc/check-param-names': 'warn',
207
+ 'jsdoc/check-syntax': 'warn',
208
+ 'jsdoc/check-tag-names': 'warn',
209
+ 'jsdoc/check-types': 'warn',
210
+ 'jsdoc/no-undefined-types': 'warn',
211
+ 'jsdoc/require-description': 'warn',
212
+ 'jsdoc/require-param': 'warn',
213
+ 'jsdoc/require-param-description': 'warn',
214
+ 'jsdoc/require-param-name': 'warn',
215
+ 'jsdoc/require-param-type': 'warn',
216
+ 'jsdoc/require-returns': 'warn',
217
+ 'jsdoc/require-returns-check': 'warn',
218
+ 'jsdoc/require-returns-description': 'warn',
219
+ 'jsdoc/require-returns-type': 'warn',
220
+ 'jsdoc/valid-types': 'warn',
221
+ },
222
+ },
223
+
224
+ {
225
+ // 忽略规则
226
+ ignores: [
227
+ 'node_modules/**',
228
+ 'dist/**',
229
+ 'build/**',
230
+ 'coverage/**',
231
+ '*.min.js',
232
+ 'eslint.config.js'
233
+ ],
234
+ },
235
+ ];
package/index.js ADDED
@@ -0,0 +1,16 @@
1
+ const Admin = require('mm_corejs').Admin;
2
+ const { JsonDB } = require('./lib/json_db');
3
+
4
+ const admin = new Admin();
5
+ admin.setClass(JsonDB);
6
+
7
+ module.exports = {
8
+ JsonDB,
9
+ admin,
10
+ jsondbAdmin(scope, config) {
11
+ if (!scope) {
12
+ scope = 'os';
13
+ }
14
+ return admin.get(scope, config);
15
+ }
16
+ };