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.
- package/CHANGELOG.md +56 -60
- package/LICENSE +201 -21
- package/README.md +537 -1828
- package/changelogs/README.md +160 -0
- package/changelogs/v2.0.0.md +222 -0
- package/dist/cjs/index.cjs +10600 -0
- package/dist/cjs/mongodb/common/transaction-aware.cjs +10 -0
- package/dist/cjs/transaction/CacheLockManager.cjs +100 -0
- package/dist/cjs/transaction/Transaction.cjs +158 -0
- package/dist/cjs/transaction/TransactionManager.cjs +298 -0
- package/dist/esm/index.mjs +10650 -0
- package/dist/types/base.d.ts +81 -0
- package/dist/types/collection.d.ts +1031 -0
- package/dist/types/expression.d.ts +115 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/lock.d.ts +74 -0
- package/dist/types/model.d.ts +526 -0
- package/dist/types/mongodb.d.ts +49 -0
- package/dist/types/monsqlize.d.ts +491 -0
- package/dist/types/pool.d.ts +84 -0
- package/dist/types/runtime.d.ts +362 -0
- package/dist/types/saga.d.ts +143 -0
- package/dist/types/slow-query-log.d.ts +126 -0
- package/dist/types/sync.d.ts +103 -0
- package/dist/types/transaction.d.ts +132 -0
- package/package.json +67 -69
- package/index.d.ts +0 -206
- package/index.mjs +0 -52
- package/lib/cache-invalidation.js +0 -279
- package/lib/cache.js +0 -530
- package/lib/common/cursor.js +0 -59
- package/lib/common/docs-urls.js +0 -73
- package/lib/common/index-options.js +0 -223
- package/lib/common/log.js +0 -61
- package/lib/common/namespace.js +0 -22
- package/lib/common/normalize.js +0 -34
- package/lib/common/page-result.js +0 -43
- package/lib/common/runner.js +0 -57
- package/lib/common/server-features.js +0 -232
- package/lib/common/shape-builders.js +0 -27
- package/lib/common/validation.js +0 -113
- package/lib/connect.js +0 -99
- package/lib/constants.js +0 -55
- package/lib/count-queue.js +0 -188
- package/lib/distributed-cache-invalidator.js +0 -260
- package/lib/errors.js +0 -168
- package/lib/expression/cache/ExpressionCache.js +0 -114
- package/lib/expression/compiler/ExpressionCompiler.js +0 -1090
- package/lib/expression/compiler/ExpressionCompilerExtensions.js +0 -531
- package/lib/expression/detector.js +0 -84
- package/lib/expression/factory.js +0 -29
- package/lib/expression/index.js +0 -19
- package/lib/function-cache.js +0 -533
- package/lib/index.js +0 -1251
- package/lib/infrastructure/ConnectionPoolManager.js +0 -464
- package/lib/infrastructure/HealthChecker.js +0 -281
- package/lib/infrastructure/PoolConfig.js +0 -199
- package/lib/infrastructure/PoolSelector.js +0 -225
- package/lib/infrastructure/PoolStats.js +0 -244
- package/lib/infrastructure/ssh-tunnel-ssh2.js +0 -212
- package/lib/infrastructure/ssh-tunnel.js +0 -41
- package/lib/infrastructure/uri-parser.js +0 -36
- package/lib/lock/Lock.js +0 -67
- package/lib/lock/errors.js +0 -28
- package/lib/lock/index.js +0 -13
- package/lib/logger.js +0 -225
- package/lib/model/examples/test.js +0 -311
- package/lib/model/features/defaults.js +0 -161
- package/lib/model/features/populate.js +0 -568
- package/lib/model/features/relations.js +0 -120
- package/lib/model/features/soft-delete.js +0 -349
- package/lib/model/features/version.js +0 -157
- package/lib/model/features/virtuals.js +0 -219
- package/lib/model/index.js +0 -1265
- package/lib/mongodb/common/accessor-helpers.js +0 -59
- package/lib/mongodb/common/agg-pipeline.js +0 -36
- package/lib/mongodb/common/aggregation-validator.js +0 -127
- package/lib/mongodb/common/iid.js +0 -28
- package/lib/mongodb/common/lexicographic-expr.js +0 -53
- package/lib/mongodb/common/shape.js +0 -32
- package/lib/mongodb/common/sort.js +0 -39
- package/lib/mongodb/common/transaction-aware.js +0 -25
- package/lib/mongodb/connect.js +0 -234
- package/lib/mongodb/index.js +0 -639
- package/lib/mongodb/management/admin-ops.js +0 -200
- package/lib/mongodb/management/bookmark-ops.js +0 -167
- package/lib/mongodb/management/cache-ops.js +0 -50
- package/lib/mongodb/management/collection-ops.js +0 -387
- package/lib/mongodb/management/database-ops.js +0 -202
- package/lib/mongodb/management/index-ops.js +0 -475
- package/lib/mongodb/management/index.js +0 -17
- package/lib/mongodb/management/namespace.js +0 -31
- package/lib/mongodb/management/validation-ops.js +0 -268
- package/lib/mongodb/queries/aggregate.js +0 -172
- package/lib/mongodb/queries/chain.js +0 -631
- package/lib/mongodb/queries/count.js +0 -99
- package/lib/mongodb/queries/distinct.js +0 -78
- package/lib/mongodb/queries/find-and-count.js +0 -193
- package/lib/mongodb/queries/find-by-ids.js +0 -236
- package/lib/mongodb/queries/find-one-by-id.js +0 -171
- package/lib/mongodb/queries/find-one.js +0 -71
- package/lib/mongodb/queries/find-page.js +0 -618
- package/lib/mongodb/queries/find.js +0 -171
- package/lib/mongodb/queries/index.js +0 -51
- package/lib/mongodb/queries/watch.js +0 -538
- package/lib/mongodb/writes/common/batch-retry.js +0 -65
- package/lib/mongodb/writes/delete-batch.js +0 -323
- package/lib/mongodb/writes/delete-many.js +0 -181
- package/lib/mongodb/writes/delete-one.js +0 -173
- package/lib/mongodb/writes/find-one-and-delete.js +0 -203
- package/lib/mongodb/writes/find-one-and-replace.js +0 -239
- package/lib/mongodb/writes/find-one-and-update.js +0 -240
- package/lib/mongodb/writes/increment-one.js +0 -259
- package/lib/mongodb/writes/index.js +0 -46
- package/lib/mongodb/writes/insert-batch.js +0 -508
- package/lib/mongodb/writes/insert-many.js +0 -223
- package/lib/mongodb/writes/insert-one.js +0 -169
- package/lib/mongodb/writes/replace-one.js +0 -226
- package/lib/mongodb/writes/result-handler.js +0 -237
- package/lib/mongodb/writes/update-batch.js +0 -416
- package/lib/mongodb/writes/update-many.js +0 -275
- package/lib/mongodb/writes/update-one.js +0 -273
- package/lib/mongodb/writes/upsert-one.js +0 -203
- package/lib/multi-level-cache.js +0 -244
- package/lib/operators.js +0 -330
- package/lib/redis-cache-adapter.js +0 -267
- package/lib/saga/SagaContext.js +0 -67
- package/lib/saga/SagaDefinition.js +0 -32
- package/lib/saga/SagaExecutor.js +0 -201
- package/lib/saga/SagaOrchestrator.js +0 -186
- package/lib/saga/index.js +0 -11
- package/lib/slow-query-log/base-storage.js +0 -70
- package/lib/slow-query-log/batch-queue.js +0 -97
- package/lib/slow-query-log/config-manager.js +0 -196
- package/lib/slow-query-log/index.js +0 -238
- package/lib/slow-query-log/mongodb-storage.js +0 -324
- package/lib/slow-query-log/query-hash.js +0 -39
- package/lib/sync/ChangeStreamSyncManager.js +0 -405
- package/lib/sync/ResumeTokenStore.js +0 -192
- package/lib/sync/SyncConfig.js +0 -127
- package/lib/sync/SyncTarget.js +0 -240
- package/lib/sync/index.js +0 -20
- package/lib/transaction/CacheLockManager.js +0 -162
- package/lib/transaction/DistributedCacheLockManager.js +0 -475
- package/lib/transaction/Transaction.js +0 -315
- package/lib/transaction/TransactionManager.js +0 -267
- package/lib/transaction/index.js +0 -11
- package/lib/utils/objectid-converter.js +0 -632
- package/types/README.md +0 -122
- package/types/base.ts +0 -94
- package/types/batch.ts +0 -187
- package/types/cache.ts +0 -71
- package/types/chain.ts +0 -254
- package/types/collection.ts +0 -357
- package/types/expression.ts +0 -109
- package/types/function-cache.d.ts +0 -135
- package/types/lock.ts +0 -95
- package/types/model/definition.ts +0 -152
- package/types/model/index.ts +0 -10
- package/types/model/instance.ts +0 -121
- package/types/model/relations.ts +0 -121
- package/types/model/virtuals.ts +0 -32
- package/types/monsqlize.ts +0 -245
- package/types/options.ts +0 -192
- package/types/pagination.ts +0 -154
- package/types/pool.ts +0 -125
- package/types/query.ts +0 -71
- package/types/saga.ts +0 -125
- package/types/stream.ts +0 -64
- package/types/sync.ts +0 -79
- package/types/transaction.ts +0 -79
- 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;
|