@nexe/config-manager 0.1.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/LICENSE +21 -0
- package/README.md +208 -0
- package/dist/config-manager.d.ts +34 -0
- package/dist/config-manager.d.ts.map +1 -0
- package/dist/config-manager.js +138 -0
- package/dist/config-manager.js.map +1 -0
- package/dist/debug/consul-debug.d.ts +6 -0
- package/dist/debug/consul-debug.d.ts.map +1 -0
- package/dist/debug/consul-debug.js +53 -0
- package/dist/debug/consul-debug.js.map +1 -0
- package/dist/decorators.d.ts +12 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +29 -0
- package/dist/decorators.js.map +1 -0
- package/dist/examples/annotation-example.d.ts +1 -0
- package/dist/examples/annotation-example.d.ts.map +1 -0
- package/dist/examples/annotation-example.js +273 -0
- package/dist/examples/annotation-example.js.map +1 -0
- package/dist/examples/config-example.d.ts +1 -0
- package/dist/examples/config-example.d.ts.map +1 -0
- package/dist/examples/config-example.js +83 -0
- package/dist/examples/config-example.js.map +1 -0
- package/dist/examples/consul-config-example.v2.d.ts +1 -0
- package/dist/examples/consul-config-example.v2.d.ts.map +1 -0
- package/dist/examples/consul-config-example.v2.js +145 -0
- package/dist/examples/consul-config-example.v2.js.map +1 -0
- package/dist/examples/database-config.d.ts +1 -0
- package/dist/examples/database-config.d.ts.map +1 -0
- package/dist/examples/database-config.js +23 -0
- package/dist/examples/database-config.js.map +1 -0
- package/dist/examples/hot-reload-test.d.ts +1 -0
- package/dist/examples/hot-reload-test.d.ts.map +1 -0
- package/dist/examples/hot-reload-test.js +65 -0
- package/dist/examples/hot-reload-test.js.map +1 -0
- package/dist/factory.d.ts +45 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +46 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +100 -0
- package/dist/interfaces.d.ts.map +1 -0
- package/dist/interfaces.js +6 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/sources/consul-config-source.d.ts +79 -0
- package/dist/sources/consul-config-source.d.ts.map +1 -0
- package/dist/sources/consul-config-source.js +332 -0
- package/dist/sources/consul-config-source.js.map +1 -0
- package/dist/sources/environment-config-source.d.ts +34 -0
- package/dist/sources/environment-config-source.d.ts.map +1 -0
- package/dist/sources/environment-config-source.js +77 -0
- package/dist/sources/environment-config-source.js.map +1 -0
- package/dist/sources/index.d.ts +6 -0
- package/dist/sources/index.d.ts.map +1 -0
- package/dist/sources/index.js +6 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/sources/json-config-source.d.ts +57 -0
- package/dist/sources/json-config-source.d.ts.map +1 -0
- package/dist/sources/json-config-source.js +153 -0
- package/dist/sources/json-config-source.js.map +1 -0
- package/dist/sources/remote-api-config-source.d.ts +62 -0
- package/dist/sources/remote-api-config-source.d.ts.map +1 -0
- package/dist/sources/remote-api-config-source.js +169 -0
- package/dist/sources/remote-api-config-source.js.map +1 -0
- package/dist/sources/yaml-config-source.d.ts +57 -0
- package/dist/sources/yaml-config-source.d.ts.map +1 -0
- package/dist/sources/yaml-config-source.js +163 -0
- package/dist/sources/yaml-config-source.js.map +1 -0
- package/package.json +66 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
import { ConfigManager } from './config-manager';
|
2
|
+
export interface ConfigSourceOptions {
|
3
|
+
environment?: {
|
4
|
+
enabled: boolean;
|
5
|
+
prefix?: string;
|
6
|
+
priority?: number;
|
7
|
+
};
|
8
|
+
json?: {
|
9
|
+
enabled: boolean;
|
10
|
+
filePath: string;
|
11
|
+
watchChanges?: boolean;
|
12
|
+
priority?: number;
|
13
|
+
};
|
14
|
+
yaml?: {
|
15
|
+
enabled: boolean;
|
16
|
+
filePath: string;
|
17
|
+
watchChanges?: boolean;
|
18
|
+
priority?: number;
|
19
|
+
};
|
20
|
+
remoteApi?: {
|
21
|
+
enabled: boolean;
|
22
|
+
baseUrl: string;
|
23
|
+
headers?: Record<string, string>;
|
24
|
+
pollingInterval?: number;
|
25
|
+
priority?: number;
|
26
|
+
};
|
27
|
+
consul?: {
|
28
|
+
enabled: boolean;
|
29
|
+
host: string;
|
30
|
+
port: number;
|
31
|
+
secure?: boolean;
|
32
|
+
token?: string;
|
33
|
+
dc?: string;
|
34
|
+
keyPrefix?: string;
|
35
|
+
watchInterval?: number;
|
36
|
+
priority?: number;
|
37
|
+
};
|
38
|
+
}
|
39
|
+
/**
|
40
|
+
* 创建配置管理器的工厂函数
|
41
|
+
* @param options 配置源选项
|
42
|
+
* @returns 配置管理器实例
|
43
|
+
*/
|
44
|
+
export declare function createConfigManager(options: ConfigSourceOptions): ConfigManager;
|
45
|
+
//# sourceMappingURL=factory.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOjD,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,SAAS,CAAC,EAAE;QACV,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,mBAAmB,GAC3B,aAAa,CAgEf"}
|
package/dist/factory.js
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
import { container } from 'tsyringe';
|
2
|
+
import { ConfigManager } from './config-manager';
|
3
|
+
import { ConsulConfigSource } from './sources/consul-config-source';
|
4
|
+
import { EnvironmentConfigSource } from './sources/environment-config-source';
|
5
|
+
import { JsonConfigSource } from './sources/json-config-source';
|
6
|
+
import { RemoteApiConfigSource } from './sources/remote-api-config-source';
|
7
|
+
import { YamlConfigSource } from './sources/yaml-config-source';
|
8
|
+
/**
|
9
|
+
* 创建配置管理器的工厂函数
|
10
|
+
* @param options 配置源选项
|
11
|
+
* @returns 配置管理器实例
|
12
|
+
*/
|
13
|
+
export function createConfigManager(options) {
|
14
|
+
const configManager = new ConfigManager();
|
15
|
+
// 添加环境变量配置源
|
16
|
+
if (options.environment?.enabled) {
|
17
|
+
configManager.addSource(new EnvironmentConfigSource(options.environment.prefix ?? ''), options.environment.priority ?? 100);
|
18
|
+
}
|
19
|
+
// 添加JSON配置源
|
20
|
+
if (options.json?.enabled) {
|
21
|
+
configManager.addSource(new JsonConfigSource(options.json.filePath, options.json.watchChanges ?? false), options.json.priority ?? 20);
|
22
|
+
}
|
23
|
+
// 添加YAML配置源
|
24
|
+
if (options.yaml?.enabled) {
|
25
|
+
configManager.addSource(new YamlConfigSource(options.yaml.filePath, options.yaml.watchChanges ?? false), options.yaml.priority ?? 40);
|
26
|
+
}
|
27
|
+
// 添加远程API配置源
|
28
|
+
if (options.remoteApi?.enabled) {
|
29
|
+
configManager.addSource(new RemoteApiConfigSource(options.remoteApi.baseUrl, options.remoteApi.headers ?? {}, options.remoteApi.pollingInterval), options.remoteApi.priority ?? 60);
|
30
|
+
}
|
31
|
+
// 添加Consul配置源
|
32
|
+
if (options.consul?.enabled) {
|
33
|
+
configManager.addSource(new ConsulConfigSource({
|
34
|
+
host: options.consul.host,
|
35
|
+
port: options.consul.port,
|
36
|
+
secure: options.consul.secure,
|
37
|
+
token: options.consul.token,
|
38
|
+
dc: options.consul.dc,
|
39
|
+
defaultKeyPrefix: options.consul.keyPrefix,
|
40
|
+
}), options.consul.priority ?? 80);
|
41
|
+
}
|
42
|
+
// 注册到容器
|
43
|
+
container.registerInstance(ConfigManager, configManager);
|
44
|
+
return configManager;
|
45
|
+
}
|
46
|
+
//# sourceMappingURL=factory.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAwChE;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAA4B;IAE5B,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;IAE1C,YAAY;IACZ,IAAI,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;QACjC,aAAa,CAAC,SAAS,CACrB,IAAI,uBAAuB,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,IAAI,EAAE,CAAC,EAC7D,OAAO,CAAC,WAAW,CAAC,QAAQ,IAAI,GAAG,CACpC,CAAC;IACJ,CAAC;IAED,YAAY;IACZ,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QAC1B,aAAa,CAAC,SAAS,CACrB,IAAI,gBAAgB,CAClB,OAAO,CAAC,IAAI,CAAC,QAAQ,EACrB,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,KAAK,CACnC,EACD,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAC5B,CAAC;IACJ,CAAC;IAED,YAAY;IACZ,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QAC1B,aAAa,CAAC,SAAS,CACrB,IAAI,gBAAgB,CAClB,OAAO,CAAC,IAAI,CAAC,QAAQ,EACrB,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,KAAK,CACnC,EACD,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAC5B,CAAC;IACJ,CAAC;IAED,aAAa;IACb,IAAI,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;QAC/B,aAAa,CAAC,SAAS,CACrB,IAAI,qBAAqB,CACvB,OAAO,CAAC,SAAS,CAAC,OAAO,EACzB,OAAO,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,EAC/B,OAAO,CAAC,SAAS,CAAC,eAAe,CAClC,EACD,OAAO,CAAC,SAAS,CAAC,QAAQ,IAAI,EAAE,CACjC,CAAC;IACJ,CAAC;IAED,cAAc;IACd,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QAC5B,aAAa,CAAC,SAAS,CACrB,IAAI,kBAAkB,CAAC;YACrB,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;YACzB,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;YACzB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;YAC7B,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK;YAC3B,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE;YACrB,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS;SAC3C,CAAC,EACF,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAC9B,CAAC;IACJ,CAAC;IAED,QAAQ;IACR,SAAS,CAAC,gBAAgB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAEzD,OAAO,aAAa,CAAC;AACvB,CAAC"}
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
|
package/dist/index.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
/**
|
2
|
+
* 配置源接口
|
3
|
+
* 所有配置源必须实现此接口
|
4
|
+
*/
|
5
|
+
export interface IConfigSource<T = unknown> {
|
6
|
+
/**
|
7
|
+
* 加载配置
|
8
|
+
* @param key 可选的配置键,用于获取特定配置项
|
9
|
+
* @param pathPrefix 可选的路径前缀,用于动态指定配置路径
|
10
|
+
*/
|
11
|
+
load: (key?: string, pathPrefix?: string) => Promise<T>;
|
12
|
+
/**
|
13
|
+
* 获取配置源名称
|
14
|
+
*/
|
15
|
+
getName: () => string;
|
16
|
+
/**
|
17
|
+
* 是否支持热更新
|
18
|
+
*/
|
19
|
+
supportsHotReload: () => boolean;
|
20
|
+
/**
|
21
|
+
* 是否支持动态路径
|
22
|
+
*/
|
23
|
+
supportsDynamicPath?: () => boolean;
|
24
|
+
/**
|
25
|
+
* 订阅配置变更(如果支持热更新)
|
26
|
+
* @param key 配置键
|
27
|
+
* @param callback 配置变更时的回调函数
|
28
|
+
* @param pathPrefix 可选的路径前缀
|
29
|
+
*/
|
30
|
+
subscribe?: (key: string, callback: (value: unknown) => void, pathPrefix?: string) => void;
|
31
|
+
/**
|
32
|
+
* 取消订阅配置变更
|
33
|
+
* @param key 配置键
|
34
|
+
* @param pathPrefix 可选的路径前缀
|
35
|
+
*/
|
36
|
+
unsubscribe?: (key: string, pathPrefix?: string) => void;
|
37
|
+
}
|
38
|
+
/**
|
39
|
+
* 配置获取选项
|
40
|
+
*/
|
41
|
+
export interface ConfigOptions {
|
42
|
+
/**
|
43
|
+
* 配置路径前缀,用于指定从哪个路径获取配置
|
44
|
+
*/
|
45
|
+
pathPrefix?: string;
|
46
|
+
/**
|
47
|
+
* 配置源名称,用于指定从哪个配置源获取配置
|
48
|
+
*/
|
49
|
+
sourceName?: string;
|
50
|
+
}
|
51
|
+
/**
|
52
|
+
* 配置管理器接口
|
53
|
+
*/
|
54
|
+
export interface IConfigManager {
|
55
|
+
/**
|
56
|
+
* 添加配置源
|
57
|
+
* @param source 配置源实例
|
58
|
+
* @param priority 优先级,数字越大优先级越高
|
59
|
+
*/
|
60
|
+
addSource: (source: IConfigSource, priority?: number) => void;
|
61
|
+
/**
|
62
|
+
* 获取配置值
|
63
|
+
* @param key 配置键
|
64
|
+
* @param defaultValue 默认值
|
65
|
+
* @param options 配置选项
|
66
|
+
*/
|
67
|
+
get: <T>(key: string, defaultValue?: T, options?: ConfigOptions) => Promise<T>;
|
68
|
+
/**
|
69
|
+
* 获取强类型配置对象
|
70
|
+
* @param configClass 配置类
|
71
|
+
* @param options 配置选项,可指定配置路径前缀
|
72
|
+
*/
|
73
|
+
getConfig: <T>(configClass: new () => T, options?: ConfigOptions) => Promise<T>;
|
74
|
+
/**
|
75
|
+
* 订阅配置变更
|
76
|
+
* @param key 配置键
|
77
|
+
* @param callback 配置变更时的回调函数
|
78
|
+
* @param options 配置选项,可指定配置路径前缀
|
79
|
+
*/
|
80
|
+
subscribe: <T>(key: string, callback: (value: T) => void, options?: ConfigOptions) => void;
|
81
|
+
/**
|
82
|
+
* 取消订阅配置变更
|
83
|
+
* @param key 配置键
|
84
|
+
* @param options 配置选项,可指定配置路径前缀
|
85
|
+
*/
|
86
|
+
unsubscribe: (key: string, options?: ConfigOptions) => void;
|
87
|
+
}
|
88
|
+
/**
|
89
|
+
* 配置装饰器元数据键
|
90
|
+
*/
|
91
|
+
export declare const CONFIG_PROPERTY_METADATA = "config:property";
|
92
|
+
export declare const CONFIG_SECTION_METADATA = "config:section";
|
93
|
+
/**
|
94
|
+
* 配置属性元数据
|
95
|
+
*/
|
96
|
+
export interface ConfigPropertyMetadata {
|
97
|
+
key: string;
|
98
|
+
propertyKey: string;
|
99
|
+
}
|
100
|
+
//# sourceMappingURL=interfaces.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC;;;;OAIG;IACH,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAExD;;OAEG;IACH,OAAO,EAAE,MAAM,MAAM,CAAC;IAEtB;;OAEG;IACH,iBAAiB,EAAE,MAAM,OAAO,CAAC;IAEjC;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC;IAEpC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CACV,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAClC,UAAU,CAAC,EAAE,MAAM,KAChB,IAAI,CAAC;IAEV;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9D;;;;;OAKG;IACH,GAAG,EAAE,CAAC,CAAC,EACL,GAAG,EAAE,MAAM,EACX,YAAY,CAAC,EAAE,CAAC,EAChB,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,CAAC,CAAC,CAAC;IAEhB;;;;OAIG;IACH,SAAS,EAAE,CAAC,CAAC,EACX,WAAW,EAAE,UAAU,CAAC,EACxB,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,CAAC,CAAC,CAAC;IAEhB;;;;;OAKG;IACH,SAAS,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3F;;;;OAIG;IACH,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;CAC7D;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,oBAAoB,CAAC;AAC1D,eAAO,MAAM,uBAAuB,mBAAmB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AA+GA;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,iBAAiB,CAAC;AAC1D,MAAM,CAAC,MAAM,uBAAuB,GAAG,gBAAgB,CAAC"}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import type { IConfigSource } from '../interfaces';
|
2
|
+
interface ConsulOptions {
|
3
|
+
host: string;
|
4
|
+
port: number;
|
5
|
+
secure?: boolean;
|
6
|
+
token?: string;
|
7
|
+
dc?: string;
|
8
|
+
defaultKeyPrefix?: string;
|
9
|
+
}
|
10
|
+
/**
|
11
|
+
* 支持动态路径的 Consul 配置源
|
12
|
+
* 允许在运行时指定不同的配置路径前缀
|
13
|
+
*/
|
14
|
+
export declare class ConsulConfigSource implements IConfigSource {
|
15
|
+
private consul;
|
16
|
+
private options;
|
17
|
+
private pathCaches;
|
18
|
+
private callbacks;
|
19
|
+
private watchers;
|
20
|
+
private recreatingWatchers;
|
21
|
+
/**
|
22
|
+
* @param options Consul配置选项
|
23
|
+
*/
|
24
|
+
constructor(options: ConsulOptions);
|
25
|
+
/**
|
26
|
+
* 从Consul加载配置
|
27
|
+
*/
|
28
|
+
load(key?: string, pathPrefix?: string): Promise<unknown>;
|
29
|
+
/**
|
30
|
+
* 获取指定前缀的所有配置键
|
31
|
+
*/
|
32
|
+
private fetchKeysForPrefix;
|
33
|
+
/**
|
34
|
+
* 获取相对路径
|
35
|
+
*/
|
36
|
+
private getRelativePath;
|
37
|
+
/**
|
38
|
+
* 解码Consul值
|
39
|
+
*/
|
40
|
+
private decodeConsulValue;
|
41
|
+
/**
|
42
|
+
* 获取嵌套值
|
43
|
+
*/
|
44
|
+
private getNestedValue;
|
45
|
+
/**
|
46
|
+
* 设置嵌套值
|
47
|
+
*/
|
48
|
+
private setNestedValue;
|
49
|
+
/**
|
50
|
+
* 获取配置源名称
|
51
|
+
*/
|
52
|
+
getName(): string;
|
53
|
+
/**
|
54
|
+
* 是否支持热更新
|
55
|
+
*/
|
56
|
+
supportsHotReload(): boolean;
|
57
|
+
/**
|
58
|
+
* 是否支持动态路径
|
59
|
+
*/
|
60
|
+
supportsDynamicPath(): boolean;
|
61
|
+
/**
|
62
|
+
* 订阅配置变更
|
63
|
+
*/
|
64
|
+
subscribe(key: string, callback: (value: unknown) => void, pathPrefix?: string): void;
|
65
|
+
/**
|
66
|
+
* 取消订阅配置变更
|
67
|
+
*/
|
68
|
+
unsubscribe(key: string, pathPrefix?: string): void;
|
69
|
+
/**
|
70
|
+
* 创建配置监听器
|
71
|
+
*/
|
72
|
+
private createWatcher;
|
73
|
+
/**
|
74
|
+
* 清理资源
|
75
|
+
*/
|
76
|
+
dispose(): void;
|
77
|
+
}
|
78
|
+
export {};
|
79
|
+
//# sourceMappingURL=consul-config-source.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"consul-config-source.d.ts","sourceRoot":"","sources":["../../src/sources/consul-config-source.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAcD;;;GAGG;AACH,qBACa,kBAAmB,YAAW,aAAa;IAEtD,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAAgB;IAE/B,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,SAAS,CAAmD;IAEpE,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,kBAAkB,CAAqB;IAE/C;;OAEG;gBACS,OAAO,EAAE,aAAa;IAqBlC;;OAEG;IACG,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4B/D;;OAEG;YACW,kBAAkB;IA8DhC;;OAEG;IACH,OAAO,CAAC,eAAe;IAYvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAczB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB;;OAEG;IACH,OAAO,IAAI,MAAM;IAIjB;;OAEG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;OAEG;IACH,mBAAmB,IAAI,OAAO;IAI9B;;OAEG;IACH,SAAS,CACP,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAClC,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI;IAsBP;;OAEG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAyBnD;;OAEG;IACH,OAAO,CAAC,aAAa;IAkFrB;;OAEG;IACH,OAAO,IAAI,IAAI;CAsBhB"}
|
@@ -0,0 +1,332 @@
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
2
|
+
import { createLogger } from '@nexe/logger';
|
3
|
+
import Consul from 'consul';
|
4
|
+
import { injectable } from 'tsyringe';
|
5
|
+
const logger = createLogger('ConsulConfigSource');
|
6
|
+
/**
|
7
|
+
* 支持动态路径的 Consul 配置源
|
8
|
+
* 允许在运行时指定不同的配置路径前缀
|
9
|
+
*/
|
10
|
+
let ConsulConfigSource = class ConsulConfigSource {
|
11
|
+
/**
|
12
|
+
* @param options Consul配置选项
|
13
|
+
*/
|
14
|
+
constructor(options) {
|
15
|
+
// 按路径前缀分别缓存配置
|
16
|
+
this.pathCaches = new Map();
|
17
|
+
this.callbacks = new Map();
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
19
|
+
this.watchers = new Map();
|
20
|
+
this.recreatingWatchers = new Set(); // 防止重复创建
|
21
|
+
this.options = {
|
22
|
+
secure: false,
|
23
|
+
defaultKeyPrefix: '',
|
24
|
+
...options,
|
25
|
+
};
|
26
|
+
// 初始化Consul客户端
|
27
|
+
this.consul = new Consul({
|
28
|
+
host: this.options.host,
|
29
|
+
port: this.options.port,
|
30
|
+
secure: this.options.secure,
|
31
|
+
defaults: {
|
32
|
+
token: this.options.token,
|
33
|
+
dc: this.options.dc,
|
34
|
+
},
|
35
|
+
});
|
36
|
+
logger.info(`🔗 已连接到Consul: ${this.options.host}:${this.options.port}`);
|
37
|
+
}
|
38
|
+
/**
|
39
|
+
* 从Consul加载配置
|
40
|
+
*/
|
41
|
+
async load(key, pathPrefix) {
|
42
|
+
const actualPrefix = pathPrefix || this.options.defaultKeyPrefix || '';
|
43
|
+
try {
|
44
|
+
// 检查是否已缓存该路径的配置
|
45
|
+
if (!this.pathCaches.has(actualPrefix)) {
|
46
|
+
logger.info(`🔄 正在从Consul加载配置路径: ${actualPrefix}`);
|
47
|
+
await this.fetchKeysForPrefix(actualPrefix);
|
48
|
+
}
|
49
|
+
const cache = this.pathCaches.get(actualPrefix) || {};
|
50
|
+
if (!key) {
|
51
|
+
return cache;
|
52
|
+
}
|
53
|
+
const value = this.getNestedValue(cache, key);
|
54
|
+
return value;
|
55
|
+
}
|
56
|
+
catch (error) {
|
57
|
+
logger.error(`从Consul加载配置失败 (路径: ${actualPrefix}, 键: ${key}): ${error instanceof Error ? error.message : String(error)}`);
|
58
|
+
return undefined;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
/**
|
62
|
+
* 获取指定前缀的所有配置键
|
63
|
+
*/
|
64
|
+
async fetchKeysForPrefix(keyPrefix) {
|
65
|
+
try {
|
66
|
+
const cache = {};
|
67
|
+
// 首先尝试获取前缀作为单个JSON对象(兼容原版)
|
68
|
+
try {
|
69
|
+
const singleResult = await this.consul.kv.get({
|
70
|
+
key: keyPrefix,
|
71
|
+
recurse: false,
|
72
|
+
});
|
73
|
+
if (singleResult &&
|
74
|
+
!Array.isArray(singleResult) &&
|
75
|
+
singleResult.Value) {
|
76
|
+
// 如果存在单个JSON对象,解析它
|
77
|
+
const jsonData = this.decodeConsulValue(singleResult.Value);
|
78
|
+
if (jsonData && typeof jsonData === 'object') {
|
79
|
+
// logger.debug(`发现单个JSON配置对象 (${keyPrefix}),使用原版兼容模式`);
|
80
|
+
Object.assign(cache, jsonData);
|
81
|
+
this.pathCaches.set(keyPrefix, cache);
|
82
|
+
// logger.info(
|
83
|
+
// `✅ 已缓存配置路径 ${keyPrefix},包含 ${Object.keys(cache).length} 个配置项`,
|
84
|
+
// );
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
catch (error) {
|
90
|
+
logger.debug(`单个JSON模式失败 (${keyPrefix}),尝试递归模式:`, error);
|
91
|
+
}
|
92
|
+
// 如果单个JSON模式失败,使用递归模式获取所有子键
|
93
|
+
const result = await this.consul.kv.get({
|
94
|
+
key: keyPrefix,
|
95
|
+
recurse: true,
|
96
|
+
});
|
97
|
+
if (Array.isArray(result)) {
|
98
|
+
// 递归模式:处理多个键值对
|
99
|
+
logger.debug(`使用递归模式处理配置路径 (${keyPrefix})`);
|
100
|
+
for (const item of result) {
|
101
|
+
if (item.Key && item.Value !== undefined) {
|
102
|
+
const relativePath = this.getRelativePath(item.Key, keyPrefix);
|
103
|
+
const value = this.decodeConsulValue(item.Value);
|
104
|
+
if (relativePath) {
|
105
|
+
this.setNestedValue(cache, relativePath, value);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
this.pathCaches.set(keyPrefix, cache);
|
111
|
+
logger.info(`✅ 已缓存配置路径 ${keyPrefix},包含 ${Object.keys(cache).length} 个配置项`);
|
112
|
+
}
|
113
|
+
catch (error) {
|
114
|
+
logger.error(`获取Consul配置前缀 ${keyPrefix} 失败:`, error);
|
115
|
+
this.pathCaches.set(keyPrefix, {});
|
116
|
+
}
|
117
|
+
}
|
118
|
+
/**
|
119
|
+
* 获取相对路径
|
120
|
+
*/
|
121
|
+
getRelativePath(fullKey, prefix) {
|
122
|
+
if (!prefix) {
|
123
|
+
return fullKey;
|
124
|
+
}
|
125
|
+
const relativePath = fullKey.startsWith(prefix)
|
126
|
+
? fullKey.slice(prefix.length)
|
127
|
+
: fullKey;
|
128
|
+
return relativePath.replace(/^\//, '');
|
129
|
+
}
|
130
|
+
/**
|
131
|
+
* 解码Consul值
|
132
|
+
*/
|
133
|
+
decodeConsulValue(value) {
|
134
|
+
if (!value) {
|
135
|
+
return null;
|
136
|
+
}
|
137
|
+
try {
|
138
|
+
// 尝试解析为JSON
|
139
|
+
return JSON.parse(value);
|
140
|
+
}
|
141
|
+
catch {
|
142
|
+
// 如果不是JSON,返回原始字符串
|
143
|
+
return value;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
/**
|
147
|
+
* 获取嵌套值
|
148
|
+
*/
|
149
|
+
getNestedValue(obj, path) {
|
150
|
+
return path.split('.').reduce((current, key) => {
|
151
|
+
return current && typeof current === 'object' && !Array.isArray(current)
|
152
|
+
? current[key]
|
153
|
+
: undefined;
|
154
|
+
}, obj);
|
155
|
+
}
|
156
|
+
/**
|
157
|
+
* 设置嵌套值
|
158
|
+
*/
|
159
|
+
setNestedValue(obj, path, value) {
|
160
|
+
const keys = path.split('.');
|
161
|
+
const lastKey = keys.pop();
|
162
|
+
if (!lastKey) {
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
const target = keys.reduce((current, key) => {
|
166
|
+
if (!(key in current)) {
|
167
|
+
current[key] = {};
|
168
|
+
}
|
169
|
+
return current[key];
|
170
|
+
}, obj);
|
171
|
+
target[lastKey] = value;
|
172
|
+
}
|
173
|
+
/**
|
174
|
+
* 获取配置源名称
|
175
|
+
*/
|
176
|
+
getName() {
|
177
|
+
return `ConsulConfigSource(${this.options.host}:${this.options.port})`;
|
178
|
+
}
|
179
|
+
/**
|
180
|
+
* 是否支持热更新
|
181
|
+
*/
|
182
|
+
supportsHotReload() {
|
183
|
+
return true;
|
184
|
+
}
|
185
|
+
/**
|
186
|
+
* 是否支持动态路径
|
187
|
+
*/
|
188
|
+
supportsDynamicPath() {
|
189
|
+
return true;
|
190
|
+
}
|
191
|
+
/**
|
192
|
+
* 订阅配置变更
|
193
|
+
*/
|
194
|
+
subscribe(key, callback, pathPrefix) {
|
195
|
+
const actualPrefix = pathPrefix || this.options.defaultKeyPrefix || '';
|
196
|
+
const watchKey = `${actualPrefix}:${key}`;
|
197
|
+
logger.info(`🔔 订阅配置变更: ${watchKey}`);
|
198
|
+
// 添加回调到列表
|
199
|
+
const callbacks = this.callbacks.get(watchKey) ?? [];
|
200
|
+
callbacks.push(callback);
|
201
|
+
this.callbacks.set(watchKey, callbacks);
|
202
|
+
// 如果还没有为这个路径+键创建watcher且不在重新创建中,则创建一个
|
203
|
+
if (!this.watchers.has(watchKey) && !this.recreatingWatchers.has(watchKey)) {
|
204
|
+
logger.info(`🚀 创建新的 watcher: ${watchKey}`);
|
205
|
+
this.createWatcher(actualPrefix, key);
|
206
|
+
}
|
207
|
+
else if (this.watchers.has(watchKey)) {
|
208
|
+
logger.info(`♻️ watcher 已存在: ${watchKey}`);
|
209
|
+
}
|
210
|
+
else if (this.recreatingWatchers.has(watchKey)) {
|
211
|
+
logger.info(`⏳ watcher 正在重新创建中: ${watchKey}`);
|
212
|
+
}
|
213
|
+
}
|
214
|
+
/**
|
215
|
+
* 取消订阅配置变更
|
216
|
+
*/
|
217
|
+
unsubscribe(key, pathPrefix) {
|
218
|
+
const actualPrefix = pathPrefix || this.options.defaultKeyPrefix || '';
|
219
|
+
const watchKey = `${actualPrefix}:${key}`;
|
220
|
+
logger.info(`🚫 取消订阅配置变更: ${watchKey}`);
|
221
|
+
// 删除回调
|
222
|
+
this.callbacks.delete(watchKey);
|
223
|
+
// 取消重新创建标记
|
224
|
+
this.recreatingWatchers.delete(watchKey);
|
225
|
+
// 关闭对应的watcher
|
226
|
+
const watcher = this.watchers.get(watchKey);
|
227
|
+
if (watcher) {
|
228
|
+
try {
|
229
|
+
watcher.end();
|
230
|
+
logger.info(`✅ 已关闭 watcher: ${watchKey}`);
|
231
|
+
}
|
232
|
+
catch (error) {
|
233
|
+
logger.warn(`⚠️ 关闭 watcher 时出错 (${watchKey}):`, error);
|
234
|
+
}
|
235
|
+
this.watchers.delete(watchKey);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
/**
|
239
|
+
* 创建配置监听器
|
240
|
+
*/
|
241
|
+
createWatcher(pathPrefix, key) {
|
242
|
+
const watchKey = `${pathPrefix}:${key}`;
|
243
|
+
// 双重检查,确保不重复创建
|
244
|
+
if (this.watchers.has(watchKey)) {
|
245
|
+
logger.warn(`⚠️ watcher 已存在,跳过创建: ${watchKey}`);
|
246
|
+
return;
|
247
|
+
}
|
248
|
+
try {
|
249
|
+
logger.info(`🔍 创建watcher: ${watchKey} -> 监听路径: ${pathPrefix}`);
|
250
|
+
const watcher = this.consul.watch({
|
251
|
+
method: this.consul.kv.get,
|
252
|
+
options: {
|
253
|
+
key: pathPrefix,
|
254
|
+
recurse: true,
|
255
|
+
},
|
256
|
+
});
|
257
|
+
// 先注册到 watchers,防止重复创建
|
258
|
+
this.watchers.set(watchKey, watcher);
|
259
|
+
watcher.on('change', async (_data) => {
|
260
|
+
try {
|
261
|
+
// logger.info(`🔄 配置变更检测: ${watchKey}`);
|
262
|
+
// 更新缓存
|
263
|
+
await this.fetchKeysForPrefix(pathPrefix);
|
264
|
+
// 通知订阅者
|
265
|
+
const cache = this.pathCaches.get(pathPrefix) || {};
|
266
|
+
const value = this.getNestedValue(cache, key);
|
267
|
+
// logger.info(`📢 通知订阅者: ${watchKey}, 新值:`, value);
|
268
|
+
const callbacks = this.callbacks.get(watchKey) ?? [];
|
269
|
+
callbacks.forEach(callback => callback(value));
|
270
|
+
}
|
271
|
+
catch (error) {
|
272
|
+
logger.error('处理配置变更失败:', error);
|
273
|
+
}
|
274
|
+
});
|
275
|
+
watcher.on('error', (error) => {
|
276
|
+
logger.error(`Consul watch错误 (${watchKey}):`, error);
|
277
|
+
// 清理当前 watcher
|
278
|
+
this.watchers.delete(watchKey);
|
279
|
+
// 检查是否是限流错误
|
280
|
+
const isRateLimited = error.message.includes('too many requests');
|
281
|
+
if (isRateLimited) {
|
282
|
+
logger.warn(`⚠️ Consul 限流,暂停重新创建 watcher: ${watchKey}`);
|
283
|
+
return; // 不重新创建,避免进一步限流
|
284
|
+
}
|
285
|
+
// 标记为正在重新创建
|
286
|
+
this.recreatingWatchers.add(watchKey);
|
287
|
+
setTimeout(() => {
|
288
|
+
this.recreatingWatchers.delete(watchKey);
|
289
|
+
// 只有在还有回调的情况下才重新创建
|
290
|
+
if (this.callbacks.has(watchKey) && this.callbacks.get(watchKey)?.length) {
|
291
|
+
logger.info(`🔄 重新创建 watcher: ${watchKey}`);
|
292
|
+
this.createWatcher(pathPrefix, key);
|
293
|
+
}
|
294
|
+
}, 10000); // 增加到10秒,避免频繁重试
|
295
|
+
});
|
296
|
+
logger.info(`✅ watcher 创建成功: ${watchKey}`);
|
297
|
+
}
|
298
|
+
catch (error) {
|
299
|
+
logger.error(`创建watcher失败 (${watchKey}):`, error);
|
300
|
+
// 如果创建失败,从 watchers 中移除
|
301
|
+
this.watchers.delete(watchKey);
|
302
|
+
}
|
303
|
+
}
|
304
|
+
/**
|
305
|
+
* 清理资源
|
306
|
+
*/
|
307
|
+
dispose() {
|
308
|
+
logger.info('🧹 开始清理所有资源...');
|
309
|
+
// 清理重新创建标记
|
310
|
+
this.recreatingWatchers.clear();
|
311
|
+
// 关闭所有watchers
|
312
|
+
this.watchers.forEach((watcher, watchKey) => {
|
313
|
+
try {
|
314
|
+
watcher.end();
|
315
|
+
logger.info(`✅ 已关闭 watcher: ${watchKey}`);
|
316
|
+
}
|
317
|
+
catch (error) {
|
318
|
+
logger.warn(`⚠️ 关闭 watcher 时出错 (${watchKey}):`, error);
|
319
|
+
}
|
320
|
+
});
|
321
|
+
this.watchers.clear();
|
322
|
+
this.callbacks.clear();
|
323
|
+
this.pathCaches.clear();
|
324
|
+
logger.info('🧹 已清理所有资源');
|
325
|
+
}
|
326
|
+
};
|
327
|
+
ConsulConfigSource = __decorate([
|
328
|
+
injectable(),
|
329
|
+
__metadata("design:paramtypes", [Object])
|
330
|
+
], ConsulConfigSource);
|
331
|
+
export { ConsulConfigSource };
|
332
|
+
//# sourceMappingURL=consul-config-source.js.map
|