monsqlize 1.3.0 → 2.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 (172) hide show
  1. package/CHANGELOG.md +56 -60
  2. package/LICENSE +201 -21
  3. package/README.md +537 -1828
  4. package/changelogs/README.md +160 -0
  5. package/changelogs/v2.0.0.md +222 -0
  6. package/dist/cjs/index.cjs +10600 -0
  7. package/dist/cjs/mongodb/common/transaction-aware.cjs +10 -0
  8. package/dist/cjs/transaction/CacheLockManager.cjs +100 -0
  9. package/dist/cjs/transaction/Transaction.cjs +158 -0
  10. package/dist/cjs/transaction/TransactionManager.cjs +298 -0
  11. package/dist/esm/index.mjs +10650 -0
  12. package/dist/types/base.d.ts +81 -0
  13. package/dist/types/collection.d.ts +1031 -0
  14. package/dist/types/expression.d.ts +115 -0
  15. package/dist/types/index.d.ts +23 -0
  16. package/dist/types/lock.d.ts +74 -0
  17. package/dist/types/model.d.ts +526 -0
  18. package/dist/types/mongodb.d.ts +49 -0
  19. package/dist/types/monsqlize.d.ts +491 -0
  20. package/dist/types/pool.d.ts +84 -0
  21. package/dist/types/runtime.d.ts +362 -0
  22. package/dist/types/saga.d.ts +143 -0
  23. package/dist/types/slow-query-log.d.ts +126 -0
  24. package/dist/types/sync.d.ts +103 -0
  25. package/dist/types/transaction.d.ts +132 -0
  26. package/package.json +67 -69
  27. package/index.d.ts +0 -206
  28. package/index.mjs +0 -52
  29. package/lib/cache-invalidation.js +0 -279
  30. package/lib/cache.js +0 -530
  31. package/lib/common/cursor.js +0 -59
  32. package/lib/common/docs-urls.js +0 -73
  33. package/lib/common/index-options.js +0 -223
  34. package/lib/common/log.js +0 -61
  35. package/lib/common/namespace.js +0 -22
  36. package/lib/common/normalize.js +0 -34
  37. package/lib/common/page-result.js +0 -43
  38. package/lib/common/runner.js +0 -57
  39. package/lib/common/server-features.js +0 -232
  40. package/lib/common/shape-builders.js +0 -27
  41. package/lib/common/validation.js +0 -113
  42. package/lib/connect.js +0 -99
  43. package/lib/constants.js +0 -55
  44. package/lib/count-queue.js +0 -188
  45. package/lib/distributed-cache-invalidator.js +0 -260
  46. package/lib/errors.js +0 -168
  47. package/lib/expression/cache/ExpressionCache.js +0 -114
  48. package/lib/expression/compiler/ExpressionCompiler.js +0 -1090
  49. package/lib/expression/compiler/ExpressionCompilerExtensions.js +0 -531
  50. package/lib/expression/detector.js +0 -84
  51. package/lib/expression/factory.js +0 -29
  52. package/lib/expression/index.js +0 -19
  53. package/lib/function-cache.js +0 -533
  54. package/lib/index.js +0 -1251
  55. package/lib/infrastructure/ConnectionPoolManager.js +0 -464
  56. package/lib/infrastructure/HealthChecker.js +0 -281
  57. package/lib/infrastructure/PoolConfig.js +0 -199
  58. package/lib/infrastructure/PoolSelector.js +0 -225
  59. package/lib/infrastructure/PoolStats.js +0 -244
  60. package/lib/infrastructure/ssh-tunnel-ssh2.js +0 -212
  61. package/lib/infrastructure/ssh-tunnel.js +0 -41
  62. package/lib/infrastructure/uri-parser.js +0 -36
  63. package/lib/lock/Lock.js +0 -67
  64. package/lib/lock/errors.js +0 -28
  65. package/lib/lock/index.js +0 -13
  66. package/lib/logger.js +0 -225
  67. package/lib/model/examples/test.js +0 -311
  68. package/lib/model/features/defaults.js +0 -161
  69. package/lib/model/features/populate.js +0 -568
  70. package/lib/model/features/relations.js +0 -120
  71. package/lib/model/features/soft-delete.js +0 -349
  72. package/lib/model/features/version.js +0 -157
  73. package/lib/model/features/virtuals.js +0 -219
  74. package/lib/model/index.js +0 -1265
  75. package/lib/mongodb/common/accessor-helpers.js +0 -59
  76. package/lib/mongodb/common/agg-pipeline.js +0 -36
  77. package/lib/mongodb/common/aggregation-validator.js +0 -127
  78. package/lib/mongodb/common/iid.js +0 -28
  79. package/lib/mongodb/common/lexicographic-expr.js +0 -53
  80. package/lib/mongodb/common/shape.js +0 -32
  81. package/lib/mongodb/common/sort.js +0 -39
  82. package/lib/mongodb/common/transaction-aware.js +0 -25
  83. package/lib/mongodb/connect.js +0 -234
  84. package/lib/mongodb/index.js +0 -639
  85. package/lib/mongodb/management/admin-ops.js +0 -200
  86. package/lib/mongodb/management/bookmark-ops.js +0 -167
  87. package/lib/mongodb/management/cache-ops.js +0 -50
  88. package/lib/mongodb/management/collection-ops.js +0 -387
  89. package/lib/mongodb/management/database-ops.js +0 -202
  90. package/lib/mongodb/management/index-ops.js +0 -475
  91. package/lib/mongodb/management/index.js +0 -17
  92. package/lib/mongodb/management/namespace.js +0 -31
  93. package/lib/mongodb/management/validation-ops.js +0 -268
  94. package/lib/mongodb/queries/aggregate.js +0 -172
  95. package/lib/mongodb/queries/chain.js +0 -631
  96. package/lib/mongodb/queries/count.js +0 -99
  97. package/lib/mongodb/queries/distinct.js +0 -78
  98. package/lib/mongodb/queries/find-and-count.js +0 -193
  99. package/lib/mongodb/queries/find-by-ids.js +0 -236
  100. package/lib/mongodb/queries/find-one-by-id.js +0 -171
  101. package/lib/mongodb/queries/find-one.js +0 -71
  102. package/lib/mongodb/queries/find-page.js +0 -618
  103. package/lib/mongodb/queries/find.js +0 -171
  104. package/lib/mongodb/queries/index.js +0 -51
  105. package/lib/mongodb/queries/watch.js +0 -538
  106. package/lib/mongodb/writes/common/batch-retry.js +0 -65
  107. package/lib/mongodb/writes/delete-batch.js +0 -323
  108. package/lib/mongodb/writes/delete-many.js +0 -181
  109. package/lib/mongodb/writes/delete-one.js +0 -173
  110. package/lib/mongodb/writes/find-one-and-delete.js +0 -203
  111. package/lib/mongodb/writes/find-one-and-replace.js +0 -239
  112. package/lib/mongodb/writes/find-one-and-update.js +0 -240
  113. package/lib/mongodb/writes/increment-one.js +0 -259
  114. package/lib/mongodb/writes/index.js +0 -46
  115. package/lib/mongodb/writes/insert-batch.js +0 -508
  116. package/lib/mongodb/writes/insert-many.js +0 -223
  117. package/lib/mongodb/writes/insert-one.js +0 -169
  118. package/lib/mongodb/writes/replace-one.js +0 -226
  119. package/lib/mongodb/writes/result-handler.js +0 -237
  120. package/lib/mongodb/writes/update-batch.js +0 -416
  121. package/lib/mongodb/writes/update-many.js +0 -275
  122. package/lib/mongodb/writes/update-one.js +0 -273
  123. package/lib/mongodb/writes/upsert-one.js +0 -203
  124. package/lib/multi-level-cache.js +0 -244
  125. package/lib/operators.js +0 -330
  126. package/lib/redis-cache-adapter.js +0 -267
  127. package/lib/saga/SagaContext.js +0 -67
  128. package/lib/saga/SagaDefinition.js +0 -32
  129. package/lib/saga/SagaExecutor.js +0 -201
  130. package/lib/saga/SagaOrchestrator.js +0 -186
  131. package/lib/saga/index.js +0 -11
  132. package/lib/slow-query-log/base-storage.js +0 -70
  133. package/lib/slow-query-log/batch-queue.js +0 -97
  134. package/lib/slow-query-log/config-manager.js +0 -196
  135. package/lib/slow-query-log/index.js +0 -238
  136. package/lib/slow-query-log/mongodb-storage.js +0 -324
  137. package/lib/slow-query-log/query-hash.js +0 -39
  138. package/lib/sync/ChangeStreamSyncManager.js +0 -405
  139. package/lib/sync/ResumeTokenStore.js +0 -192
  140. package/lib/sync/SyncConfig.js +0 -127
  141. package/lib/sync/SyncTarget.js +0 -240
  142. package/lib/sync/index.js +0 -20
  143. package/lib/transaction/CacheLockManager.js +0 -162
  144. package/lib/transaction/DistributedCacheLockManager.js +0 -475
  145. package/lib/transaction/Transaction.js +0 -315
  146. package/lib/transaction/TransactionManager.js +0 -267
  147. package/lib/transaction/index.js +0 -11
  148. package/lib/utils/objectid-converter.js +0 -632
  149. package/types/README.md +0 -122
  150. package/types/base.ts +0 -94
  151. package/types/batch.ts +0 -187
  152. package/types/cache.ts +0 -71
  153. package/types/chain.ts +0 -254
  154. package/types/collection.ts +0 -357
  155. package/types/expression.ts +0 -109
  156. package/types/function-cache.d.ts +0 -135
  157. package/types/lock.ts +0 -95
  158. package/types/model/definition.ts +0 -152
  159. package/types/model/index.ts +0 -10
  160. package/types/model/instance.ts +0 -121
  161. package/types/model/relations.ts +0 -121
  162. package/types/model/virtuals.ts +0 -32
  163. package/types/monsqlize.ts +0 -245
  164. package/types/options.ts +0 -192
  165. package/types/pagination.ts +0 -154
  166. package/types/pool.ts +0 -125
  167. package/types/query.ts +0 -71
  168. package/types/saga.ts +0 -125
  169. package/types/stream.ts +0 -64
  170. package/types/sync.ts +0 -79
  171. package/types/transaction.ts +0 -79
  172. package/types/write.ts +0 -77
package/lib/index.js DELETED
@@ -1,1251 +0,0 @@
1
- const Logger = require("./logger");
2
- const ConnectionManager = require("./connect");
3
- const MemoryCache = require("./cache");
4
- const { createRedisCacheAdapter } = require("./redis-cache-adapter");
5
- const TransactionManager = require("./transaction/TransactionManager");
6
- const CacheLockManager = require("./transaction/CacheLockManager");
7
- const DistributedCacheInvalidator = require("./distributed-cache-invalidator");
8
- const { validateRange } = require("./common/validation");
9
- const ConnectionPoolManager = require("./infrastructure/ConnectionPoolManager");
10
- const SagaOrchestrator = require("./saga/SagaOrchestrator");
11
- const { ChangeStreamSyncManager } = require("./sync");
12
-
13
- module.exports = class {
14
- /**
15
- * 初始化数据库连接配置
16
- * @param {Object} options - 数据库连接配置选项
17
- * @param {string} options.type - 数据库类型,支持 mongodb
18
- * @param {Object} options.config - 数据库连接配置
19
- * @param {Object} [options.cache] - 缓存配置选项
20
- * @param {Object} [options.logger] - 日志记录器
21
- * @param {number} [options.maxTimeMS] - 全局默认查询超时时间(毫秒)
22
- * @param {{instanceId?: string}} [options.namespace] - 命名空间设置(用于缓存隔离)
23
- * @throws {Error} 如果数据库类型无效则抛出错误
24
- */
25
- constructor(options) {
26
- if (!options.type || !["mongodb"].includes(options.type)) {
27
- throw new Error("Invalid database type. Supported types are: mongodb");
28
- }
29
- const { type = "mongodb", databaseName, config, cache, logger } = options;
30
- this.type = type;
31
- this.databaseName = databaseName;
32
- this.config = config;
33
-
34
- // ✅ v1.3.0: 自动 ObjectId 转换配置
35
- this.autoConvertConfig = this._initAutoConvertConfig(
36
- options.autoConvertObjectId,
37
- options.type,
38
- );
39
-
40
- // 🔧 修复:保存 distributed 配置到单独的变量
41
- this._cacheConfig = cache;
42
-
43
- // 🆕 v1.0.8: 多连接池配置
44
- this._poolsConfig = options.pools;
45
- this._poolStrategy = options.poolStrategy || "auto";
46
- this._poolFallback = options.poolFallback;
47
- this._maxPoolsCount = options.maxPoolsCount;
48
- this._poolManager = null; // 连接时初始化
49
-
50
- // Count 队列配置(高并发控制,避免压垮数据库)
51
- // 默认值:
52
- // - enabled: true (默认启用)
53
- // - concurrency: CPU 核心数(最少 4,最多 16)
54
- // - maxQueueSize: 10000
55
- // - timeout: 60000ms (1分钟)
56
- this.countQueue =
57
- options.countQueue !== undefined
58
- ? options.countQueue
59
- : {
60
- enabled: true, // 默认启用
61
- concurrency: undefined, // undefined 则使用 CPU 核心数
62
- maxQueueSize: 10000, // 队列最大 10000
63
- timeout: 60000, // 超时 60 秒
64
- };
65
-
66
- // 使用缓存工厂获取有效的缓存实例
67
- this.cache = MemoryCache.getOrCreateCache(cache);
68
-
69
- // 🆕 v1.1.6: 精准缓存失效配置
70
- const cacheConfig = cache || {};
71
- this.cacheAutoInvalidate =
72
- cacheConfig.autoInvalidate !== undefined
73
- ? cacheConfig.autoInvalidate
74
- : false; // 默认 false(不自动失效)
75
-
76
- // 使用 Logger 工具类创建日志记录器
77
- this.logger = Logger.create(logger);
78
-
79
- // 🔒 参数验证:防止 DoS 攻击(允许null值用于显式禁用)
80
- if (options.maxTimeMS !== undefined && options.maxTimeMS !== null) {
81
- validateRange(options.maxTimeMS, 1, 300000, "maxTimeMS");
82
- }
83
- if (options.findLimit !== undefined && options.findLimit !== null) {
84
- validateRange(options.findLimit, 1, 10000, "findLimit");
85
- }
86
- if (
87
- options.findPageMaxLimit !== undefined &&
88
- options.findPageMaxLimit !== null
89
- ) {
90
- validateRange(options.findPageMaxLimit, 1, 10000, "findPageMaxLimit");
91
- }
92
- if (
93
- options.slowQueryMs !== undefined &&
94
- options.slowQueryMs !== null &&
95
- options.slowQueryMs !== -1
96
- ) {
97
- validateRange(options.slowQueryMs, 0, 60000, "slowQueryMs");
98
- }
99
-
100
- // 集中默认配置(库内默认 + 用户覆盖)
101
- const DEFAULTS = {
102
- maxTimeMS: 2000,
103
- findLimit: 10,
104
- slowQueryMs: 500,
105
- namespace: { scope: "database" },
106
- // 深分页/聚合相关
107
- findPageMaxLimit: 500,
108
- cursorSecret: undefined,
109
- // 慢日志扩展
110
- log: { slowQueryTag: { event: "slow_query", code: "SLOW_QUERY" } },
111
- };
112
- const deepMerge = (base, patch) => {
113
- const out = { ...base };
114
- for (const k of Object.keys(patch || {})) {
115
- const v = patch[k];
116
- if (v && typeof v === "object" && !Array.isArray(v)) {
117
- out[k] = deepMerge(base[k] || {}, v);
118
- } else if (v !== undefined) {
119
- out[k] = v;
120
- }
121
- }
122
- return out;
123
- };
124
- this.defaults = deepMerge(DEFAULTS, {
125
- maxTimeMS: options.maxTimeMS,
126
- findLimit: options.findLimit,
127
- namespace: options.namespace,
128
- slowQueryMs: options.slowQueryMs,
129
- // 新增可选项
130
- findPageMaxLimit: options.findPageMaxLimit,
131
- cursorSecret: options.cursorSecret,
132
- log: options.log,
133
- // 🔴 v1.3.1: 慢查询日志持久化存储配置
134
- slowQueryLog: options.slowQueryLog,
135
- // 🆕 v1.1.6: 精准缓存失效配置
136
- cacheAutoInvalidate: this.cacheAutoInvalidate,
137
- });
138
- // 冻结默认配置,避免运行期被意外修改
139
- this.defaults = Object.freeze(this.defaults);
140
-
141
- // 🆕 v1.4.0: 保存 Model 自动加载配置
142
- this._modelsConfig = options.models;
143
-
144
- // 🆕 v1.1.0: 初始化 Saga 协调器
145
- this._sagaOrchestrator = new SagaOrchestrator({
146
- cache: this.cache,
147
- logger: this.logger,
148
- });
149
-
150
- // 🆕 v1.0.9: 保存同步配置
151
- this._syncConfig = options.sync;
152
- this._syncManager = null;
153
- }
154
-
155
- /**
156
- * 连接数据库并返回访问集合/表的对象
157
- * @returns {{collection: Function, db: Function}} 返回包含 collection 与 db 方法的对象
158
- * @throws {Error} 当连接失败时抛出错误
159
- */
160
- async connect() {
161
- // 如果已经有连接,直接返回访问对象
162
- if (this.dbInstance) {
163
- return this.dbInstance;
164
- }
165
-
166
- // 防止并发连接:使用连接锁
167
- if (this._connecting) {
168
- return this._connecting;
169
- }
170
-
171
- try {
172
- this._connecting = (async () => {
173
- // 🆕 v1.0.8: 如果配置了多连接池,先初始化 ConnectionPoolManager
174
- if (
175
- this._poolsConfig &&
176
- Array.isArray(this._poolsConfig) &&
177
- this._poolsConfig.length > 0
178
- ) {
179
- this._poolManager = new ConnectionPoolManager({
180
- pools: [], // 先创建空管理器
181
- poolStrategy: this._poolStrategy,
182
- poolFallback: this._poolFallback,
183
- maxPoolsCount: this._maxPoolsCount,
184
- logger: this.logger,
185
- });
186
-
187
- // 添加所有配置的连接池
188
- for (const poolConfig of this._poolsConfig) {
189
- await this._poolManager.addPool(poolConfig);
190
- }
191
-
192
- // 启动健康检查
193
- this._poolManager.startHealthCheck();
194
-
195
- this.logger.info("[MonSQLize] 多连接池已初始化", {
196
- poolCount: this._poolsConfig.length,
197
- strategy: this._poolStrategy,
198
- });
199
- }
200
-
201
- // 使用 ConnectionManager 建立连接(单连接池模式或默认连接池)
202
- const { collection, db, instance } = await ConnectionManager.connect(
203
- this.type,
204
- this.databaseName,
205
- this.config,
206
- this.cache,
207
- this.logger,
208
- this.defaults,
209
- this._poolManager, // 传递 poolManager
210
- );
211
-
212
- // 保存连接状态(关键:缓存对象,保证多次调用幂等返回同一形态/引用)
213
- this.dbInstance = { collection, db };
214
- this._adapter = instance;
215
-
216
- // 初始化分布式缓存失效器(如果配置了)
217
- // 🔧 修复:使用 _cacheConfig 读取 distributed 配置
218
- if (
219
- this._cacheConfig &&
220
- typeof this._cacheConfig.distributed === "object" &&
221
- this._cacheConfig.distributed.enabled !== false
222
- ) {
223
- try {
224
- // 🆕 自动从 cache.remote 提取 Redis 实例(如果未配置)
225
- let redis = this._cacheConfig.distributed.redis;
226
- if (!redis && !this._cacheConfig.distributed.redisUrl) {
227
- // 尝试从 remote 缓存适配器中获取 Redis 实例
228
- if (
229
- this.cache.remote &&
230
- typeof this.cache.remote.getRedisInstance === "function"
231
- ) {
232
- redis = this.cache.remote.getRedisInstance();
233
- if (this.logger) {
234
- this.logger.info(
235
- "[DistributedCache] Auto-detected Redis from cache.remote",
236
- );
237
- }
238
- }
239
- }
240
-
241
- this._cacheInvalidator = new DistributedCacheInvalidator({
242
- redisUrl: this._cacheConfig.distributed.redisUrl,
243
- redis,
244
- channel: this._cacheConfig.distributed.channel,
245
- instanceId: this._cacheConfig.distributed.instanceId,
246
- cache: this.cache,
247
- logger: this.logger,
248
- });
249
-
250
- // 🆕 关键:将 invalidate 方法注入到 MultiLevelCache
251
- if (this.cache && typeof this.cache.setPublish === "function") {
252
- this.cache.setPublish((msg) => {
253
- if (msg && msg.type === "invalidate" && msg.pattern) {
254
- this._cacheInvalidator
255
- .invalidate(msg.pattern)
256
- .catch((err) => {
257
- this.logger.error(
258
- "❌ Broadcast invalidation failed:",
259
- err.message,
260
- );
261
- });
262
- }
263
- });
264
- this.logger.info("✅ Distributed cache invalidator initialized", {
265
- channel: this._cacheInvalidator.channel,
266
- integrated: true,
267
- });
268
- } else {
269
- this.logger.warn(
270
- "⚠️ Cache does not support setPublish, distributed invalidation disabled",
271
- );
272
- }
273
- } catch (error) {
274
- this.logger.error(
275
- "❌ Failed to initialize distributed cache invalidator:",
276
- error.message,
277
- );
278
- }
279
- }
280
-
281
- // 初始化事务管理器和缓存锁管理器
282
- if (this.type === "mongodb" && instance.client) {
283
- // 检查是否配置了分布式事务锁
284
- const useDistributedLock =
285
- this._cacheConfig &&
286
- typeof this._cacheConfig.transaction === "object" &&
287
- this._cacheConfig.transaction.distributedLock &&
288
- this._cacheConfig.transaction.distributedLock.redis;
289
-
290
- if (useDistributedLock) {
291
- // 使用分布式缓存锁管理器
292
- const DistributedCacheLockManager = require("./transaction/DistributedCacheLockManager");
293
- this._lockManager = new DistributedCacheLockManager({
294
- redis: this._cacheConfig.transaction.distributedLock.redis,
295
- lockKeyPrefix:
296
- this._cacheConfig.transaction.distributedLock.keyPrefix ||
297
- "monsqlize:cache:lock:",
298
- maxDuration: 300000,
299
- logger: this.logger,
300
- });
301
- this.logger.info("✅ Distributed cache lock manager initialized");
302
- } else {
303
- // 使用本地缓存锁管理器
304
- this._lockManager = new CacheLockManager({
305
- logger: this.logger,
306
- maxDuration: 300000, // 锁最长持续5分钟
307
- cleanupInterval: 10000, // 每10秒清理一次
308
- });
309
- }
310
-
311
- // 将锁管理器注入到缓存
312
- if (this.cache && typeof this.cache.setLockManager === "function") {
313
- this.cache.setLockManager(this._lockManager);
314
- }
315
-
316
- // 创建事务管理器
317
- this._transactionManager = new TransactionManager(
318
- instance, // 传入完整的 adapter 实例
319
- this.cache,
320
- this.logger,
321
- {
322
- lockManager: this._lockManager,
323
- },
324
- );
325
-
326
- this.logger.info("✅ Transaction manager initialized", {
327
- hasClient: !!instance.client,
328
- hasLockManager: !!this._lockManager,
329
- isDistributed: useDistributedLock,
330
- });
331
-
332
- // 🆕 v1.4.0: 挂载业务锁 API(仅在使用分布式锁时可用)
333
- if (
334
- this._lockManager &&
335
- typeof this._lockManager.withLock === "function"
336
- ) {
337
- this.dbInstance.withLock = (key, callback, opts) =>
338
- this._lockManager.withLock(key, callback, opts);
339
- this.dbInstance.acquireLock = (key, opts) =>
340
- this._lockManager.acquireLock(key, opts);
341
- this.dbInstance.tryAcquireLock = (key, opts) =>
342
- this._lockManager.tryAcquireLock(key, opts);
343
- this.dbInstance.getLockStats = () => this._lockManager.getStats();
344
-
345
- this.logger.info("✅ Business lock API initialized", {
346
- isDistributed: useDistributedLock,
347
- });
348
- } else {
349
- this.logger.warn(
350
- "⚠️ Business lock API not available (Redis required)",
351
- {
352
- hasLockManager: !!this._lockManager,
353
- isDistributed: useDistributedLock,
354
- },
355
- );
356
- }
357
- } else {
358
- this.logger.warn("⚠️ Transaction manager not initialized", {
359
- type: this.type,
360
- hasClient: !!instance.client,
361
- });
362
- }
363
-
364
- // 🆕 v1.4.0: 自动加载 Model
365
- if (this._modelsConfig) {
366
- await this._loadModels();
367
- }
368
-
369
- // 🆕 v1.0.9: 启动 Change Stream 同步
370
- if (this._syncConfig && this._syncConfig.enabled) {
371
- try {
372
- this._syncManager = new ChangeStreamSyncManager({
373
- db: instance.getNativeDb(), // 获取原生 MongoDB DB 对象
374
- poolManager: this._poolManager,
375
- config: this._syncConfig,
376
- logger: this.logger,
377
- });
378
-
379
- await this._syncManager.start();
380
-
381
- this.logger.info("[MonSQLize] Change Stream 同步已启动", {
382
- targets: this._syncConfig.targets.length,
383
- });
384
- } catch (error) {
385
- this.logger.error("[MonSQLize] Change Stream 同步启动失败", {
386
- error: error.message,
387
- });
388
- // 同步启动失败不影响主库连接
389
- }
390
- }
391
-
392
- return this.dbInstance;
393
- })();
394
-
395
- const result = await this._connecting;
396
- this._connecting = null;
397
- return result;
398
- } catch (err) {
399
- this._connecting = null;
400
- throw err;
401
- }
402
- }
403
-
404
- /**
405
- * 获取底层缓存实例(用于查看统计或手动失效)
406
- * @returns {Object} 缓存实例
407
- */
408
- getCache() {
409
- return this.cache;
410
- }
411
-
412
- /**
413
- * 获取当前实例的默认配置(只读视图)
414
- * @returns {{maxTimeMS?:number, findLimit?:number, namespace?:object, slowQueryMs?:number}}
415
- */
416
- getDefaults() {
417
- return { ...this.defaults };
418
- }
419
-
420
- /**
421
- * 关闭底层数据库连接(释放资源)
422
- */
423
- async close() {
424
- // 清理分布式缓存失效器
425
- if (
426
- this._cacheInvalidator &&
427
- typeof this._cacheInvalidator.close === "function"
428
- ) {
429
- await this._cacheInvalidator.close();
430
- this._cacheInvalidator = null;
431
- }
432
-
433
- // 清理事务管理器
434
- if (
435
- this._transactionManager &&
436
- typeof this._transactionManager.destroy === "function"
437
- ) {
438
- await this._transactionManager.destroy();
439
- this._transactionManager = null;
440
- }
441
-
442
- // 清理锁管理器
443
- if (this._lockManager && typeof this._lockManager.destroy === "function") {
444
- this._lockManager.destroy();
445
- this._lockManager = null;
446
- }
447
-
448
- // 🆕 v1.0.8: 关闭多连接池
449
- if (this._poolManager) {
450
- await this._poolManager.close();
451
- this._poolManager = null;
452
- }
453
-
454
- // 关闭数据库连接
455
- if (this._adapter && typeof this._adapter.close === "function") {
456
- await this._adapter.close();
457
- }
458
-
459
- // 清理状态
460
- this.dbInstance = null;
461
- this._adapter = null;
462
- this._connecting = null;
463
- }
464
-
465
- /**
466
- * 健康检查(适配器透传)
467
- */
468
- async health() {
469
- if (this._adapter && typeof this._adapter.health === "function") {
470
- return this._adapter.health();
471
- }
472
- return { status: "down", connected: false };
473
- }
474
-
475
- /**
476
- * 查询慢查询日志(v1.3.1+)
477
- * @param {Object} filter - 查询条件 { db, collection, operation }
478
- * @param {Object} options - 查询选项 { sort, limit, skip }
479
- * @returns {Promise<Array>} 慢查询日志列表
480
- */
481
- async getSlowQueryLogs(filter, options) {
482
- if (this._adapter && typeof this._adapter.getSlowQueryLogs === "function") {
483
- return this._adapter.getSlowQueryLogs(filter, options);
484
- }
485
- throw new Error("Slow query log feature is not enabled or not supported");
486
- }
487
-
488
- /**
489
- * 事件订阅(适配器透传)
490
- * @param {'connected'|'closed'|'error'|'slow-query'} event
491
- * @param {(payload:any)=>void} handler
492
- */
493
- on(event, handler) {
494
- if (this._adapter && typeof this._adapter.on === "function") {
495
- this._adapter.on(event, handler);
496
- }
497
- }
498
-
499
- /**
500
- * 启动一个事务会话(手动管理)
501
- * @param {Object} options - 事务选项
502
- * @param {Object} [options.readConcern] - 读关注级别 { level: 'majority' | 'local' | 'snapshot' }
503
- * @param {string} [options.readPreference] - 读偏好
504
- * @param {boolean} [options.causalConsistency=true] - 因果一致性
505
- * @param {number} [options.timeout=30000] - 事务超时时间(毫秒)
506
- * @returns {Promise<Transaction>}
507
- */
508
- async startSession(options = {}) {
509
- if (!this._transactionManager) {
510
- throw new Error("Connection not established. Call connect() first.");
511
- }
512
- return this._transactionManager.startSession(options);
513
- }
514
-
515
- /**
516
- * 使用事务执行操作(自动管理,推荐)
517
- * @param {Function} callback - 事务回调函数,接收 Transaction 对象作为参数
518
- * @param {Object} options - 事务选项(同 startSession)
519
- * @param {number} [options.maxRetries=3] - 最大重试次数
520
- * @param {number} [options.retryDelay=100] - 重试延迟(毫秒)
521
- * @param {number} [options.retryBackoff=2] - 重试退避系数
522
- * @param {string} [options.pool] - 指定连接池名称(v1.0.8+,修复问题10)
523
- * @returns {Promise<any>} 返回 callback 的返回值
524
- */
525
- async withTransaction(callback, options = {}) {
526
- if (!this._transactionManager) {
527
- throw new Error("Connection not established. Call connect() first.");
528
- }
529
-
530
- // 🔴 修复问题10:事务需要确保在同一连接池执行
531
- const poolName = options.pool || "primary";
532
-
533
- // 如果使用了多连接池,需要锁定到指定连接池
534
- if (this._poolManager) {
535
- const pool = this._poolManager._getPool(poolName);
536
- if (!pool) {
537
- throw new Error(`Transaction pool '${poolName}' not found`);
538
- }
539
-
540
- this.logger.info("[Transaction] 使用连接池执行事务", { pool: poolName });
541
-
542
- // 传递连接池信息给 TransactionManager
543
- return this._transactionManager.withTransaction(callback, {
544
- ...options,
545
- _poolClient: pool,
546
- });
547
- }
548
-
549
- // 单连接池模式,正常执行
550
- return this._transactionManager.withTransaction(callback, options);
551
- }
552
-
553
- // ========== 多连接池 API(v1.0.8+)==========
554
-
555
- /**
556
- * 添加连接池
557
- * @param {Object} config - 连接池配置
558
- * @returns {Promise<void>}
559
- */
560
- async addPool(config) {
561
- if (!this._poolManager) {
562
- throw new Error(
563
- "Multi-pool is not enabled. Configure pools in constructor options.",
564
- );
565
- }
566
- return this._poolManager.addPool(config);
567
- }
568
-
569
- /**
570
- * 移除连接池
571
- * @param {string} name - 连接池名称
572
- * @returns {Promise<void>}
573
- */
574
- async removePool(name) {
575
- if (!this._poolManager) {
576
- throw new Error("Multi-pool is not enabled.");
577
- }
578
- return this._poolManager.removePool(name);
579
- }
580
-
581
- /**
582
- * 获取所有连接池名称
583
- * @returns {string[]}
584
- */
585
- getPoolNames() {
586
- if (!this._poolManager) {
587
- return [];
588
- }
589
- return this._poolManager.getPoolNames();
590
- }
591
-
592
- /**
593
- * 获取连接池统计信息
594
- * @returns {Object}
595
- */
596
- getPoolStats() {
597
- if (!this._poolManager) {
598
- return {};
599
- }
600
- return this._poolManager.getPoolStats();
601
- }
602
-
603
- /**
604
- * 获取连接池健康状态
605
- * @returns {Map<string, HealthStatus>}
606
- */
607
- getPoolHealth() {
608
- if (!this._poolManager) {
609
- return new Map();
610
- }
611
- return this._poolManager.getPoolHealth();
612
- }
613
-
614
- /** 取消事件订阅(适配器透传) */
615
- off(event, handler) {
616
- if (this._adapter && typeof this._adapter.off === "function") {
617
- this._adapter.off(event, handler);
618
- }
619
- }
620
-
621
- /**
622
- * 导出工具函数:创建 Redis 缓存适配器
623
- * @static
624
- * @param {import('ioredis').Redis | import('ioredis').Cluster} client - Redis客户端
625
- * @param {Object} [options] - 配置选项
626
- * @returns {import('./cache').CacheLike} Redis缓存适配器
627
- */
628
- static createRedisCacheAdapter(client, options) {
629
- return createRedisCacheAdapter(client, options);
630
- }
631
-
632
- /**
633
- * 初始化 ObjectId 自动转换配置
634
- * @private
635
- * @param {boolean|Object} config - 用户配置
636
- * @param {string} dbType - 数据库类型
637
- * @returns {Object} 配置对象
638
- */
639
- _initAutoConvertConfig(config, dbType) {
640
- // 只在 MongoDB 类型下启用
641
- if (dbType !== "mongodb") {
642
- return { enabled: false };
643
- }
644
-
645
- // 默认配置
646
- const defaults = {
647
- enabled: true,
648
- excludeFields: [],
649
- customFieldPatterns: [],
650
- maxDepth: 10,
651
- logLevel: "warn",
652
- };
653
-
654
- // 用户禁用
655
- if (config === false) {
656
- return { enabled: false };
657
- }
658
-
659
- // 用户自定义配置
660
- if (typeof config === "object" && config !== null) {
661
- return {
662
- enabled: config.enabled !== false,
663
- excludeFields: Array.isArray(config.excludeFields)
664
- ? config.excludeFields
665
- : defaults.excludeFields,
666
- customFieldPatterns: Array.isArray(config.customFieldPatterns)
667
- ? config.customFieldPatterns
668
- : defaults.customFieldPatterns,
669
- maxDepth:
670
- typeof config.maxDepth === "number"
671
- ? config.maxDepth
672
- : defaults.maxDepth,
673
- logLevel: config.logLevel || defaults.logLevel,
674
- };
675
- }
676
-
677
- return defaults;
678
- }
679
-
680
- /**
681
- * 获取 Model 实例(缓存复用)
682
- *
683
- * 同一 collectionName 多次调用返回同一实例。
684
- * Model.redefine() / Model.undefine() 后自动失效,close() 后全部清空。
685
- *
686
- * @param {string} collectionName - 集合名称
687
- * @returns {ModelInstance} Model 实例
688
- * @throws {Error} Model 未定义
689
- * @throws {Error} 数据库未连接
690
- *
691
- * @example
692
- * // 1. 定义 Model
693
- * const { Model } = require('monsqlize');
694
- * Model.define('users', {
695
- * schema: (dsl) => dsl({ username: 'string!' })
696
- * });
697
- *
698
- * // 2. 连接数据库并获取 Model 实例
699
- * const msq = new MonSQLize({ ... });
700
- * await msq.connect();
701
- * const User = msq.model('users');
702
- *
703
- * // 3. 使用 Model
704
- * const result = await User.find({ status: 'active' });
705
- */
706
- model(collectionName) {
707
- // 检查数据库是否已连接
708
- if (!this.dbInstance) {
709
- const err = new Error(
710
- "Database is not connected. Call connect() before accessing models.",
711
- );
712
- err.code = "NOT_CONNECTED";
713
- throw err;
714
- }
715
-
716
- const Model = require("./model");
717
-
718
- // 缓存命中 + redefine 失效检查
719
- if (this._modelInstances && this._modelInstances.has(collectionName)) {
720
- if (!Model._redefinedNames.has(collectionName)) {
721
- return this._modelInstances.get(collectionName);
722
- }
723
- // redefine 后需要重建实例
724
- this._modelInstances.delete(collectionName);
725
- Model._redefinedNames.delete(collectionName);
726
- }
727
-
728
- // 检查 Model 是否已定义
729
- if (!Model.has(collectionName)) {
730
- const err = new Error(
731
- `Model '${collectionName}' is not defined. Call Model.define() first.`,
732
- );
733
- err.code = "MODEL_NOT_DEFINED";
734
- throw err;
735
- }
736
-
737
- // 获取 Model 定义
738
- const modelDef = Model.get(collectionName);
739
-
740
- // [v1.2.3 patch] 实际 MongoDB 集合名可能不同于注册 key
741
- // 优先读 definition.collection > definition.name > key(fallback,向后兼容)
742
- const actualCollectionName =
743
- modelDef.definition.collection ||
744
- modelDef.definition.name ||
745
- collectionName;
746
-
747
- // 获取 collection 实例(v1.2.2+ 支持 connection 路由)
748
- const connection = modelDef.definition.connection;
749
- const collection = (connection && (connection.pool || connection.database))
750
- ? this._resolveModelCollection(actualCollectionName, connection)
751
- : this.dbInstance.collection(actualCollectionName);
752
-
753
- // 创建 ModelInstance 并缓存
754
- const instance = new Model.ModelInstance(collection, modelDef.definition, this);
755
-
756
- if (!this._modelInstances) this._modelInstances = new Map();
757
- this._modelInstances.set(collectionName, instance);
758
-
759
- return instance;
760
- }
761
-
762
- /**
763
- * 解析 Model 绑定的 collection(内部方法)
764
- *
765
- * 根据 Model 定义中的 connection 配置,将请求路由到正确的连接池和数据库。
766
- *
767
- * 四种组合:
768
- * 1. pool + database → 指定池、指定数据库
769
- * 2. pool 只配置 → 指定池、实例默认 databaseName
770
- * 3. database 只配置 → 默认连接池、指定数据库
771
- * 4. 均未配置 → 原逻辑(此方法不会被调用)
772
- *
773
- * @private
774
- * @param {string} collectionName - 集合名称
775
- * @param {{ pool?: string, database?: string }} connection - Model 的 connection 配置
776
- * @returns {Object} monSQLize 包装 collection 对象
777
- * @since v1.2.2
778
- */
779
- _resolveModelCollection(collectionName, connection) {
780
- const poolName = connection.pool;
781
- const dbName = connection.database || this.databaseName;
782
-
783
- if (poolName) {
784
- if (!this._poolManager) {
785
- const err = new Error(
786
- `Model '${collectionName}' requires pool '${poolName}' but no pools are configured. ` +
787
- `Add 'pools' to MonSQLize constructor options.`
788
- );
789
- err.code = 'NO_POOL_MANAGER';
790
- throw err;
791
- }
792
-
793
- const client = this._poolManager._getPool(poolName);
794
- if (!client) {
795
- const availablePools = this._poolManager.getPoolNames().join(', ');
796
- const err = new Error(
797
- `Pool '${poolName}' not found. Available pools: [${availablePools}]`
798
- );
799
- err.code = 'POOL_NOT_FOUND';
800
- throw err;
801
- }
802
-
803
- // 指定池 + 数据库:通过 adapter 的 collectionFromClient 复用包装逻辑
804
- return this._adapter.collectionFromClient(client, dbName, collectionName);
805
- }
806
-
807
- // 只切换数据库,用默认连接
808
- return this.dbInstance.db(dbName).collection(collectionName);
809
- }
810
-
811
- /**
812
- * 获取集合实例(代理方法)
813
- *
814
- * @param {string} collectionName - 集合名称
815
- * @returns {Object} 集合实例
816
- * @throws {Error} 数据库未连接
817
- *
818
- * @example
819
- * const users = msq.collection('users');
820
- * const docs = await users.find({}).toArray();
821
- */
822
- collection(collectionName) {
823
- if (!this.dbInstance) {
824
- const err = new Error(
825
- "Database is not connected. Call connect() before accessing collections.",
826
- );
827
- err.code = "NOT_CONNECTED";
828
- throw err;
829
- }
830
- return this.dbInstance.collection(collectionName);
831
- }
832
-
833
- // ============================================================
834
- // v1.3.0 — scoped access APIs
835
- // ============================================================
836
-
837
- /**
838
- * 获取限定池/库的 collection(公开底层方法)
839
- *
840
- * opts.pool / opts.database 均为可选;均不填时退化为默认路由。
841
- *
842
- * @param {string} collectionName
843
- * @param {{ pool?: string, database?: string }} [opts={}]
844
- * @returns {Object} Collection 实例
845
- * @throws {Error} NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND
846
- * @since v1.3.0
847
- */
848
- scopedCollection(collectionName, opts = {}) {
849
- if (!this.dbInstance) {
850
- const err = new Error('Database is not connected. Call connect() first.');
851
- err.code = 'NOT_CONNECTED';
852
- throw err;
853
- }
854
- const { pool, database } = opts;
855
- if (!pool && !database) {
856
- return this.collection(collectionName);
857
- }
858
- return this._resolveModelCollection(collectionName, { pool, database });
859
- }
860
-
861
- /**
862
- * 获取限定池/库的 Model 实例(公开底层方法,不走 _modelInstances 缓存)
863
- *
864
- * connection 合并语义:opts 字段优先,definition.connection 作 fallback。
865
- * 例:billing/invoice.ts 有 connection:{database:'billing'},
866
- * 调用 scopedModel('BillingInvoice', { pool:'cn' }) →
867
- * merged = { database:'billing', pool:'cn' },路由到 cn 池 billing 库。
868
- *
869
- * @param {string} key - 已注册的 Model key(或 alias)
870
- * @param {{ pool?: string, database?: string }} [opts={}]
871
- * @returns {ModelInstance}
872
- * @throws {Error} NOT_CONNECTED / MODEL_NOT_DEFINED / NO_POOL_MANAGER / POOL_NOT_FOUND
873
- * @since v1.3.0
874
- */
875
- scopedModel(key, opts = {}) {
876
- if (!this.dbInstance) {
877
- const err = new Error('Database is not connected. Call connect() first.');
878
- err.code = 'NOT_CONNECTED';
879
- throw err;
880
- }
881
- const Model = require('./model');
882
- if (!Model.has(key)) {
883
- const err = new Error(`Model '${key}' is not defined. Call Model.define() first.`);
884
- err.code = 'MODEL_NOT_DEFINED';
885
- throw err;
886
- }
887
- const modelDef = Model.get(key);
888
- const actualCollectionName =
889
- modelDef.definition.collection ||
890
- modelDef.definition.name ||
891
- key;
892
- const merged = { ...(modelDef.definition.connection || {}), ...opts };
893
- const { pool, database } = merged;
894
- const collection = (pool || database)
895
- ? this._resolveModelCollection(actualCollectionName, { pool, database })
896
- : this.dbInstance.collection(actualCollectionName);
897
- return new Model.ModelInstance(collection, modelDef.definition, this);
898
- }
899
-
900
- /**
901
- * 获取指定连接池的链式访问器
902
- *
903
- * 前置校验(立即执行):NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND。
904
- *
905
- * @param {string} poolName - 连接池名称
906
- * @returns {{ collection, model, use }} PoolAccessor 纯对象
907
- * @throws {Error} NOT_CONNECTED / NO_POOL_MANAGER / POOL_NOT_FOUND
908
- * @since v1.3.0
909
- */
910
- pool(poolName) {
911
- if (!this.dbInstance) {
912
- const err = new Error('Database is not connected. Call connect() first.');
913
- err.code = 'NOT_CONNECTED';
914
- throw err;
915
- }
916
- if (!this._poolManager) {
917
- const err = new Error('No pool manager configured. Add pools to MonSQLize constructor options.');
918
- err.code = 'NO_POOL_MANAGER';
919
- throw err;
920
- }
921
- if (!this._poolManager._getPool(poolName)) {
922
- const available = this._poolManager.getPoolNames?.() ?? [];
923
- const err = new Error(`Pool '${poolName}' not found. Available pools: [${available.join(', ')}]`);
924
- err.code = 'POOL_NOT_FOUND';
925
- err.available = available;
926
- throw err;
927
- }
928
- return {
929
- collection: (name) =>
930
- this.scopedCollection(name, { pool: poolName }),
931
- model: (key) =>
932
- this.scopedModel(key, { pool: poolName }),
933
- use: (dbName) => ({
934
- collection: (name) =>
935
- this.scopedCollection(name, { pool: poolName, database: dbName }),
936
- model: (key) =>
937
- this.scopedModel(key, { pool: poolName, database: dbName }),
938
- }),
939
- };
940
- }
941
-
942
- /**
943
- * 获取「默认池 + 指定库」访问器
944
- *
945
- * 适用于单连接多库场景(无需配置多连接池)。
946
- * 只校验 NOT_CONNECTED,不涉及 poolManager。
947
- *
948
- * @param {string} dbName - 数据库名称
949
- * @returns {{ collection, model }} ScopedAccessor 纯对象
950
- * @throws {Error} NOT_CONNECTED
951
- * @since v1.3.0
952
- */
953
- use(dbName) {
954
- if (!this.dbInstance) {
955
- const err = new Error('Database is not connected. Call connect() first.');
956
- err.code = 'NOT_CONNECTED';
957
- throw err;
958
- }
959
- return {
960
- collection: (name) =>
961
- this.scopedCollection(name, { database: dbName }),
962
- model: (key) =>
963
- this.scopedModel(key, { database: dbName }),
964
- };
965
- }
966
-
967
- /**
968
- * 自动加载 Model 文件
969
- *
970
- * @private
971
- * @returns {Promise<void>}
972
- *
973
- * @example
974
- * // 配置示例
975
- * {
976
- * models: {
977
- * path: './models', // Model 文件目录(必需)
978
- * pattern: '*.model.js', // 文件名模式(可选,默认 '*.model.{js,ts,mjs,cjs}')
979
- * recursive: true // 是否递归扫描子目录(可选,默认 false)
980
- * }
981
- * }
982
- *
983
- * // 或简化配置(仅指定目录)
984
- * {
985
- * models: './models' // 自动使用默认 pattern
986
- * }
987
- */
988
- async _loadModels(options = {}) {
989
- const { reload = false } = options;
990
- const fs = require("fs");
991
- const path = require("path");
992
-
993
- // 解析配置
994
- let modelsPath, pattern, recursive;
995
-
996
- if (typeof this._modelsConfig === "string") {
997
- // 简化配置:models: './models'
998
- modelsPath = this._modelsConfig;
999
- pattern = "*.model.{js,ts,mjs,cjs}";
1000
- recursive = false;
1001
- } else if (
1002
- typeof this._modelsConfig === "object" &&
1003
- this._modelsConfig.path
1004
- ) {
1005
- // 完整配置
1006
- modelsPath = this._modelsConfig.path;
1007
- pattern = this._modelsConfig.pattern || "*.model.{js,ts,mjs,cjs}";
1008
- recursive = this._modelsConfig.recursive || false;
1009
- } else {
1010
- this.logger.warn("[Model] Invalid models config, skipping auto-load");
1011
- return;
1012
- }
1013
-
1014
- // 解析绝对路径
1015
- const absolutePath = path.isAbsolute(modelsPath)
1016
- ? modelsPath
1017
- : path.resolve(process.cwd(), modelsPath);
1018
-
1019
- // 检查目录是否存在
1020
- if (!fs.existsSync(absolutePath)) {
1021
- this.logger.warn(`[Model] Models directory not found: ${absolutePath}`);
1022
- return;
1023
- }
1024
-
1025
- // 解析 pattern(支持 glob 格式)
1026
- const globPattern = pattern
1027
- .replace(/\./g, "\\.")
1028
- .replace(/\*/g, ".*")
1029
- .replace(/\{([^}]+)\}/g, "($1)")
1030
- .replace(/,/g, "|");
1031
- const regex = new RegExp(`^${globPattern}$`);
1032
-
1033
- // 扫描文件
1034
- const files = [];
1035
- const scanDir = (dir) => {
1036
- const entries = fs.readdirSync(dir, { withFileTypes: true });
1037
-
1038
- for (const entry of entries) {
1039
- const fullPath = path.join(dir, entry.name);
1040
-
1041
- if (entry.isDirectory() && recursive) {
1042
- scanDir(fullPath);
1043
- } else if (entry.isFile() && regex.test(entry.name)) {
1044
- files.push(fullPath);
1045
- }
1046
- }
1047
- };
1048
-
1049
- try {
1050
- scanDir(absolutePath);
1051
- } catch (err) {
1052
- this.logger.error(
1053
- `[Model] Failed to scan directory: ${absolutePath}`,
1054
- err,
1055
- );
1056
- return;
1057
- }
1058
-
1059
- if (this.logger) {
1060
- this.logger.info(`[Model] Scanning models from: ${absolutePath}`, {
1061
- pattern,
1062
- recursive,
1063
- filesFound: files.length,
1064
- });
1065
- }
1066
-
1067
- // 加载每个文件
1068
- const Model = require("./model");
1069
- let successCount = 0;
1070
- let errorCount = 0;
1071
-
1072
- for (const file of files) {
1073
- try {
1074
- // 清除 require 缓存(支持热重载)
1075
- delete require.cache[require.resolve(file)];
1076
-
1077
- // 加载 Model 定义
1078
- const modelDef = require(file);
1079
-
1080
- // 验证 Model 定义
1081
- if (!modelDef) {
1082
- this.logger.warn(
1083
- `[Model] Skipping ${file}: export is null or undefined`,
1084
- );
1085
- errorCount++;
1086
- continue;
1087
- }
1088
-
1089
- if (!modelDef.name) {
1090
- this.logger.warn(`[Model] Skipping ${file}: missing 'name' property`);
1091
- errorCount++;
1092
- continue;
1093
- }
1094
-
1095
- if (typeof modelDef.name !== "string" || modelDef.name.trim() === "") {
1096
- this.logger.warn(
1097
- `[Model] Skipping ${file}: 'name' must be a non-empty string`,
1098
- );
1099
- errorCount++;
1100
- continue;
1101
- }
1102
-
1103
- // 检查是否已注册
1104
- if (Model.has(modelDef.name)) {
1105
- if (reload) {
1106
- // 🆕 v1.1.7: reload 模式 — 替换已有定义
1107
- Model.redefine(modelDef.name, modelDef);
1108
- successCount++;
1109
- if (this.logger) {
1110
- this.logger.debug(
1111
- `[Model] 🔄 Reloaded: ${modelDef.name} from ${path.relative(process.cwd(), file)}`,
1112
- );
1113
- }
1114
- } else {
1115
- // 默认模式:跳过(保持原有行为)
1116
- this.logger.debug(
1117
- `[Model] Model '${modelDef.name}' already registered, skipping ${file}`,
1118
- );
1119
- }
1120
- continue;
1121
- }
1122
-
1123
- // 注册 Model
1124
- Model.define(modelDef.name, modelDef);
1125
- successCount++;
1126
-
1127
- if (this.logger) {
1128
- this.logger.debug(
1129
- `[Model] ✅ Loaded: ${modelDef.name} from ${path.relative(process.cwd(), file)}`,
1130
- );
1131
- }
1132
- } catch (err) {
1133
- errorCount++;
1134
- if (this.logger) {
1135
- this.logger.error(
1136
- `[Model] ❌ Failed to load ${path.relative(process.cwd(), file)}:`,
1137
- err.message,
1138
- );
1139
- }
1140
- }
1141
- }
1142
-
1143
- if (this.logger) {
1144
- this.logger.info(`[Model] Auto-load complete`, {
1145
- scanned: files.length,
1146
- success: successCount,
1147
- errors: errorCount,
1148
- totalModels: Model.list().length,
1149
- reload,
1150
- });
1151
- }
1152
- }
1153
-
1154
- /**
1155
- * 定义 Saga
1156
- * @param {Object} config - Saga 配置
1157
- * @param {string} config.name - Saga 名称
1158
- * @param {Array} config.steps - Saga 步骤列表
1159
- * @returns {SagaDefinition} Saga 定义实例
1160
- */
1161
- defineSaga(config) {
1162
- return this._sagaOrchestrator.defineSaga(config);
1163
- }
1164
-
1165
- /**
1166
- * 执行 Saga
1167
- * @param {string} sagaName - Saga 名称
1168
- * @param {Object} data - 执行数据
1169
- * @returns {Promise<Object>} 执行结果
1170
- */
1171
- async executeSaga(sagaName, data) {
1172
- return await this._sagaOrchestrator.execute(sagaName, data);
1173
- }
1174
-
1175
- /**
1176
- * 列出所有已定义的 Saga
1177
- * @returns {Promise<string[]>} Saga 名称列表
1178
- */
1179
- async listSagas() {
1180
- return await this._sagaOrchestrator.listSagas();
1181
- }
1182
-
1183
- /**
1184
- * 获取 Saga 统计信息
1185
- * @returns {Object} 统计信息
1186
- */
1187
- getSagaStats() {
1188
- return this._sagaOrchestrator.getStats();
1189
- }
1190
-
1191
- /**
1192
- * 关闭数据库连接和同步
1193
- * @returns {Promise<void>}
1194
- */
1195
- async close() {
1196
- // 停止 Change Stream 同步
1197
- if (this._syncManager) {
1198
- await this._syncManager.stop();
1199
- this._syncManager = null;
1200
- this.logger.info("[MonSQLize] Change Stream 同步已停止");
1201
- }
1202
-
1203
- // 关闭多连接池
1204
- if (this._poolManager) {
1205
- await this._poolManager.close();
1206
- this._poolManager = null;
1207
- this.logger.info("[MonSQLize] 连接池已关闭");
1208
- }
1209
-
1210
- // 关闭主连接
1211
- if (this._adapter && typeof this._adapter.close === "function") {
1212
- await this._adapter.close();
1213
- this.logger.info("[MonSQLize] 数据库连接已关闭");
1214
- }
1215
-
1216
- // 清理所有引用,防止内存泄漏
1217
- this._adapter = null;
1218
- this.dbInstance = null;
1219
- this._connecting = null;
1220
-
1221
- // 清理 ModelInstance 缓存
1222
- if (this._modelInstances) {
1223
- this._modelInstances.clear();
1224
- this._modelInstances = null;
1225
- }
1226
-
1227
- return null;
1228
- }
1229
- };
1230
-
1231
- // ========== 导出 Model 类 ==========
1232
- module.exports.Model = require("./model");
1233
-
1234
- // ========== 导出 ConnectionPoolManager ==========
1235
- // 🆕 v1.0.8: 多连接池管理
1236
- module.exports.ConnectionPoolManager = require("./infrastructure/ConnectionPoolManager");
1237
-
1238
- // ========== 导出表达式工厂函数 ==========
1239
- // 🆕 v1.0.9: 统一表达式语法
1240
- const createExpression = require("./expression");
1241
-
1242
- // 导出表达式创建函数
1243
- module.exports.expr = createExpression; // v1.0.9: 统一表达式语法 ⭐
1244
- module.exports.createExpression = createExpression; // 完整版别名
1245
-
1246
- // ========== 导出函数缓存 ==========
1247
- // 🆕 v1.1.4: 通用函数缓存
1248
- const { withCache, FunctionCache } = require("./function-cache");
1249
-
1250
- module.exports.withCache = withCache;
1251
- module.exports.FunctionCache = FunctionCache;