132 lines
3.6 KiB
TypeScript
132 lines
3.6 KiB
TypeScript
/**
|
|
* metona-sqlark Connection Manager — 数据库实例连接池
|
|
* @module connection-manager
|
|
*
|
|
* v0.1.13: 避免重复创建同名数据库实例,通过 connect() 复用已有连接。
|
|
* 管理实例生命周期,防止重复 open IndexedDB。
|
|
*/
|
|
|
|
import { MetonaSqlark } from './core';
|
|
import type { DatabaseConfig } from './constants';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ConnectionManager
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class ConnectionManager {
|
|
/** 活跃连接:dbName → MetonaSqlark */
|
|
private connections = new Map<string, MetonaSqlark>();
|
|
|
|
/** 连接引用计数:dbName → count */
|
|
private refCount = new Map<string, number>();
|
|
|
|
/**
|
|
* 获取或创建数据库实例
|
|
*
|
|
* 如果同名数据库已打开,复用已有实例并增加引用计数。
|
|
* 否则创建新实例。
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const db = await MetonaSqlark.connect({ name: 'my-app', mode: 'hybrid' });
|
|
* // ... use db
|
|
* await db.disconnect(); // 引用计数 -1,归零时自动关闭
|
|
* ```
|
|
*/
|
|
async connect(config: DatabaseConfig): Promise<MetonaSqlark> {
|
|
const name = config.name;
|
|
|
|
// 已有连接 → 复用
|
|
const existing = this.connections.get(name);
|
|
if (existing && existing.isReady()) {
|
|
const count = (this.refCount.get(name) ?? 0) + 1;
|
|
this.refCount.set(name, count);
|
|
return existing;
|
|
}
|
|
|
|
// 创建新连接
|
|
const db = new MetonaSqlark(config);
|
|
await db.init();
|
|
|
|
this.connections.set(name, db);
|
|
this.refCount.set(name, 1);
|
|
|
|
// 注入 disconnect 方法
|
|
(db as MetonaSqlark & { disconnect: () => Promise<void> }).disconnect = async () => {
|
|
await this.release(name);
|
|
};
|
|
|
|
return db;
|
|
}
|
|
|
|
/**
|
|
* 释放连接引用。引用计数归零时自动关闭数据库。
|
|
*/
|
|
async release(dbName: string): Promise<void> {
|
|
const count = (this.refCount.get(dbName) ?? 1) - 1;
|
|
if (count <= 0) {
|
|
const db = this.connections.get(dbName);
|
|
if (db) {
|
|
await db.close();
|
|
this.connections.delete(dbName);
|
|
}
|
|
this.refCount.delete(dbName);
|
|
} else {
|
|
this.refCount.set(dbName, count);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 强制关闭指定数据库(忽略引用计数)
|
|
*/
|
|
async forceClose(dbName: string): Promise<void> {
|
|
const db = this.connections.get(dbName);
|
|
if (db) {
|
|
await db.close();
|
|
this.connections.delete(dbName);
|
|
}
|
|
this.refCount.delete(dbName);
|
|
}
|
|
|
|
/**
|
|
* 强制关闭所有连接
|
|
*/
|
|
async closeAll(): Promise<void> {
|
|
for (const [, db] of this.connections) {
|
|
try { await db.close(); } catch { /* ignore */ }
|
|
}
|
|
this.connections.clear();
|
|
this.refCount.clear();
|
|
}
|
|
|
|
/**
|
|
* 获取所有活跃连接名
|
|
*/
|
|
getActiveConnections(): string[] {
|
|
return Array.from(this.connections.keys());
|
|
}
|
|
|
|
/**
|
|
* 获取连接的引用计数
|
|
*/
|
|
getRefCount(dbName: string): number {
|
|
return this.refCount.get(dbName) ?? 0;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 全局单例
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const manager = new ConnectionManager();
|
|
|
|
// 挂载到 MetonaSqlark 静态方法(通过 any 绕过 TS 类型检查)
|
|
const M = MetonaSqlark as unknown as Record<string, unknown>;
|
|
M.connect = (config: DatabaseConfig) => manager.connect(config);
|
|
M.disconnect = (dbName: string) => manager.release(dbName);
|
|
M.disconnectAll = () => manager.closeAll();
|
|
M.getActiveConnections = () => manager.getActiveConnections();
|
|
|
|
export { manager as connectionManager };
|
|
export default manager;
|