@hile/micro 1.0.5 → 1.0.7

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/README.md CHANGED
@@ -243,6 +243,92 @@ const health = await app.dispatch('/-/health', {});
243
243
 
244
244
  ---
245
245
 
246
+ ## 配置管理
247
+
248
+ ### Registry 工作目录
249
+
250
+ Registry 启动时自动创建 `~/.registry/` 工作目录。YAML 配置文件存放在 `~/.registry/configs/` 下,按 namespace 分文件管理:
251
+
252
+ ```
253
+ ~/.registry/
254
+ └── configs/
255
+ ├── service-a.config.yaml
256
+ ├── service-b.config.yaml
257
+ └── global.config.yaml
258
+ ```
259
+
260
+ ### 配置文件热加载
261
+
262
+ Regsitry 监听 `configs/` 目录的文件变化,新增或修改 `*.config.yaml` 文件时自动加载(兼容 vim 原子写入),无需重启:
263
+
264
+ ```bash
265
+ # 创建或修改 ~/.registry/configs/my-service.config.yaml
266
+ # Registry 自动检测变化并更新内存中的配置
267
+ ```
268
+
269
+ ### 远程读取配置
270
+
271
+ 通过 `/-/env/variables` 端点,已连服务可按 namespace 和字段从 Registry 远程读取配置:
272
+
273
+ ```typescript
274
+ // Application 侧
275
+ const result = await app.getEnvVariables(
276
+ { namespace: 'service-a', fields: ['db.host', 'db.port'] },
277
+ { namespace: 'global' },
278
+ );
279
+ // result = {
280
+ // 'service-a': { 'db.host': '10.0.0.2', 'db.port': 3306 },
281
+ // 'global': { featureFlag: true },
282
+ // }
283
+ ```
284
+
285
+ ---
286
+
287
+ ## CLI
288
+
289
+ ### `hile registry`
290
+
291
+ 启动注册中心:
292
+
293
+ ```bash
294
+ # 使用默认配置
295
+ hile registry
296
+
297
+ # 指定端口
298
+ hile registry --port 8888
299
+
300
+ # 指定宣告地址
301
+ hile registry --host 10.0.0.1
302
+ ```
303
+
304
+ ### `hile registry configs`
305
+
306
+ 管理 `~/.registry/configs/` 下的 YAML 配置文件:
307
+
308
+ ```bash
309
+ # 列出所有 namespace
310
+ hile registry configs
311
+
312
+ # 查看配置(YAML 输出)
313
+ hile registry configs get my-service
314
+
315
+ # 查看配置(JSON 输出)
316
+ hile registry configs get my-service --json
317
+
318
+ # 设置配置项
319
+ hile registry configs set my-service port=8080
320
+ hile registry configs set my-service debug=true
321
+
322
+ # 删除整个 namespace
323
+ hile registry configs del my-service
324
+
325
+ # 删除某个字段(需确认)
326
+ hile registry configs del my-service port
327
+
328
+ # 跳过确认删除
329
+ hile registry configs del my-service -y
330
+ ```
331
+
246
332
  ## 连接协议
247
333
 
248
334
  ### 连接 URL 格式
@@ -308,6 +394,12 @@ class Application extends Server {
308
394
 
309
395
  // 同进程调用路由
310
396
  dispatch(url: string, data: any): Promise<any>;
397
+
398
+ // 远程读取 Registry 的配置(强类型)
399
+ getEnvVariables<
400
+ T extends Record<string, Record<string, any>>,
401
+ const Requests extends readonly EnvRequest<T>[],
402
+ >(...data: Requests): Promise<GetEnvVariablesResult<T, Requests>>;
311
403
  }
312
404
  ```
313
405
 
@@ -318,6 +410,7 @@ class Registry extends Server {
318
410
  constructor(props?: MicroServerProps);
319
411
  listen(port: number): Promise<() => Promise<void>>;
320
412
  onFind(): void; // 幂等地挂载 /-/find 路由
413
+ watchEnvFile(): fs.FSWatcher | undefined; // 监听 ~/.registry/configs/ 目录内的 *.config.yaml 文件变化
321
414
  }
322
415
  ```
323
416
 
package/SKILL.md CHANGED
@@ -29,8 +29,8 @@ description: Code generation and contribution rules for @hile/micro. Use when ed
29
29
  |----|------|------|---------|
30
30
  | `Server` | `server.ts` | WebSocketServer 生命周期, 出入站连接, Client Map | 不感知 Registry |
31
31
  | `Client` | `client.ts` | 远端 Server 的 WebSocket 会话代理 | `dispose()` 必须关闭底层 socket |
32
- | `Registry` | `registry.ts` | namespace → Set\<host:port\>, 心跳检测, /-/find 随机返回 | `namespace` 固定 `'registry'` |
33
- | `Application` | `application.ts` | 注册发现 + 熔断 + 重试 + 追踪 + 心跳 | `listen()` 后自动连 Registry |
32
+ | `Registry` | `registry.ts` | namespace → Set\<host:port\>, 心跳检测, /-/find 随机返回, 环境变量管理 | 自动创建 `~/.registry/` 工作目录 |
33
+ | `Application` | `application.ts` | 注册发现 + 熔断 + 重试 + 追踪 + 心跳 + 远程环境变量读取 | `listen()` 后自动连 Registry |
34
34
 
35
35
  ### 应用模型
36
36
 
@@ -95,17 +95,28 @@ export interface RegistryFindData {
95
95
 
96
96
  export function parseAddressKey(key: string): RegistryAddress | undefined;
97
97
  export function selectRandomRegistryAddress(keys: Iterable<string>): RegistryAddress | undefined;
98
+ // 配置文件路径工具:
99
+ export function getRegistryConfigsDir(): string; // 返回 ~/.registry/configs/
100
+ export function namespaceToConfigFile(ns: string): string; // 返回 ~/.registry/configs/{ns}.config.yaml
101
+ export function parseConfigFilename(filename: string): string | null; // 解析 ".config.yaml" 后缀,提取 namespace
98
102
 
99
103
  export class Registry extends Server {
100
104
  // heartbeat 常量:
101
105
  // HEARTBEAT_INTERVAL = 1000 (1s 轮询)
102
106
  // HEARTBEAT_TIMEOUT = 20000 (20s 未收到心跳则剔除)
107
+ // 工作目录: ~/.registry/ (自动创建)
108
+ // - configs/ 目录存放 *.config.yaml 配置文件
109
+ // - watchEnvFile() 监听 configs/ 目录,兼容 vim 原子写入
110
+ // - configs Map<string, any> 按 namespace 存储解析后的 YAML 内容
111
+ // 内部路由:
112
+ // /-/find — 按 namespace 随机返回地址 (支持 exclude)
113
+ // /-/heartbeat — 更新实例心跳时间戳
114
+ // /-/env/variables — 按 namespace + fields 返回配置 (通过 getEnvVariables 调用)
115
+
103
116
  constructor(props?: MicroServerProps);
104
117
  listen(port: number): Promise<() => Promise<void>>;
105
118
  onFind(): void; // 幂等,可重复调用
106
- // 内部路由:
107
- // /-/find — 按 namespace 随机返回地址 (支持 exclude)
108
- // /-/heartbeat — 更新实例心跳时间戳
119
+ watchEnvFile(): fs.FSWatcher | undefined; // 监听 ~/.registry/configs/ 目录
109
120
  }
110
121
  ```
111
122
 
@@ -140,6 +151,12 @@ export class Application extends Server {
140
151
  retries?: number, // 重试次数, 默认 1
141
152
  ): Promise<T>;
142
153
 
154
+ // 远程读取 Registry 的配置(强类型,按 namespace + fields)
155
+ getEnvVariables<
156
+ T extends Record<string, Record<string, any>>,
157
+ const Requests extends readonly EnvRequest<T>[],
158
+ >(...data: Requests): Promise<GetEnvVariablesResult<T, Requests>>;
159
+
143
160
  // 继承自 Server/MessageLoader:
144
161
  // register<T, E>(url, handler): () => void;
145
162
  // dispatch(url, data, extras): Promise<any>;
@@ -359,6 +376,72 @@ listen() teardown 触发:
359
376
 
360
377
  **不处理的情况:** 全新 namespace(无缓存)、缓存 Client 已断连。
361
378
 
379
+ ### 3.10 配置管理
380
+
381
+ **存储结构:**
382
+
383
+ ```
384
+ ~/.registry/
385
+ └── configs/
386
+ ├── service-a.config.yaml
387
+ ├── service-b.config.yaml
388
+ └── global.config.yaml
389
+ ```
390
+
391
+ **Registry 侧:**
392
+
393
+ ```
394
+ Registry 构造:
395
+ 1. 创建 ~/.registry/ 目录 (自动)
396
+
397
+ Registry listen():
398
+ 1. 调用 watchEnvFile()
399
+ └─ 读取 ~/.registry/configs/ 下所有 *.config.yaml → YAML.parse
400
+ └─ 按 namespace 存入 this.configs Map
401
+ └─ 监听 configs/ 目录 (兼容 vim 原子写入)
402
+ └─ 文件变化 → 重新 YAML.parse 并更新 this.configs
403
+
404
+ /-/env/variables 端点:
405
+ register('/-/env/variables', async ({ data }) => {
406
+ data.map(({ namespace, fields }) => {
407
+ if (!this.configs.has(namespace))
408
+ return { namespace, value: null }
409
+ if (!fields?.length)
410
+ return { namespace, value: this.configs.get(namespace) }
411
+ // 按 fields 过滤
412
+ return { namespace, value: filteredConfig }
413
+ })
414
+ })
415
+ ```
416
+
417
+ **Application 侧:**
418
+
419
+ ```typescript
420
+ // 强类型查询
421
+ type EnvRequest<T> = {
422
+ [N in keyof T]: { namespace: N; fields?: readonly (keyof T[N])[] };
423
+ }[keyof T];
424
+
425
+ type GetEnvVariablesResult<T, Requests> = UnionToIntersection<...>;
426
+
427
+ getEnvVariables(...data: EnvRequest<T>[]): Promise<GetEnvVariablesResult<T, Requests>>
428
+ ```
429
+
430
+ **CLI 管理配置:**
431
+
432
+ 通过 `hile registry configs` 子命令直接管理 `~/.registry/configs/` 下的 YAML 文件:
433
+
434
+ ```
435
+ hile registry configs # 列出所有 namespace
436
+ hile registry configs get <namespace> # 查看配置(YAML/--json)
437
+ hile registry configs set <namespace> <key>=<value> # 设置配置项
438
+ hile registry configs del <namespace> [key] # 删除(带确认,-y 跳过)
439
+ ```
440
+
441
+ - CLI 直接读写 YAML 文件,运行中的 Registry 通过 `fs.watch` 自动感知
442
+ - 值类型自动推断:`true/false` → boolean, `null` → null, 纯数字 → number, 以 `{`/`[` 开头 → JSON 解析为 object/array, 其余 → string
443
+ - 实现代码在 `packages/cli/src/configs.ts`,工具函数在 `packages/micro/src/registry.ts`
444
+
362
445
  ---
363
446
 
364
447
  ## 4. 代码生成模板
@@ -470,6 +553,19 @@ pnpm --filter @hile/micro test # 必须全部通过
470
553
  | 超时 reject | `request timeout > rejects when request exceeds the timeout` |
471
554
  | 超时充足则成功 | `request timeout > succeeds when timeout is long enough` |
472
555
  | 缓存降级 | `cache degradation > uses cached client when registry lookup fails due to exclusion` |
556
+ | YAML 配置加载 | `config file loading > loads yaml config files on watchEnvFile` |
557
+ | YAML 配置热加载 | `config file loading > reloads config when yaml file changes` |
558
+ | configs 目录不存在 | `config file loading > does not crash when configs directory does not exist` |
559
+ | /-/env/variables 按字段过滤 | `/-/env/variables endpoint > returns requested config by namespace and fields` |
560
+ | /-/env/variables 全字段 | `/-/env/variables endpoint > returns all config when fields not specified` |
561
+ | 不存在的 namespace | `/-/env/variables endpoint > returns null value for non-existent namespace` |
562
+ | /-/env/variables 空列表 | `/-/env/variables endpoint > handles empty data list` |
563
+ | getEnvVariables 集成 | `Application.getEnvVariables > fetches config from Registry` |
564
+ | getEnvVariables 不存在 namespace | `Application.getEnvVariables > returns null when namespace config does not exist` |
565
+ | getRegistryConfigsDir | `config file utilities > getRegistryConfigsDir returns path ending with .registry/configs` |
566
+ | namespaceToConfigFile | `config file utilities > namespaceToConfigFile returns path with .config.yaml suffix` |
567
+ | parseConfigFilename 正例 | `config file utilities > parseConfigFilename extracts namespace from valid filename` |
568
+ | parseConfigFilename 反例 | `config file utilities > parseConfigFilename returns null for non-config file` |
473
569
 
474
570
  ### 5.3 测试规范(必须遵守)
475
571
 
@@ -490,7 +586,7 @@ pnpm --filter @hile/micro test # 必须全部通过
490
586
  4. **不要假设 `host:port` 可无损表达 IPv6** — 使用 `[IPv6]:port` 格式,`parseAddressKey` 按最后一个 `:` 切分
491
587
  5. **不要传错 Registry 端口** — 丢失 Registry 连接时依赖 `reconnectToRegistry`,不要在外部缓存 registry Client
492
588
  6. **不要在 call() 中修改原始 data 对象** — 必须使用浅拷贝 `{ ...data, _correlationId }`
493
- 7. **不要在其他文件中重复 `selectRandomRegistryAddress` `parseAddressKey`**这些是 Registry 的内部 helper
589
+ 7. **不要在其他文件中重复 Registry helper 函数** `selectRandomRegistryAddress`、`parseAddressKey`、`getRegistryConfigsDir`、`namespaceToConfigFile`、`parseConfigFilename` 都在 `registry.ts` 中导出复用
494
590
  8. **不要给 call() 增加非可选参数** — `timeout` 和 `retries` 都在尾部且保持可选,不影响现有调用
495
591
 
496
592
  ---
@@ -500,13 +596,18 @@ pnpm --filter @hile/micro test # 必须全部通过
500
596
  | 文件 | 可修改 | 说明 |
501
597
  |------|--------|------|
502
598
  | `packages/micro/src/application.ts` | ✅ | 核心业务逻辑 |
503
- | `packages/micro/src/index.test.ts` | ✅ | 测试 |
599
+ | `packages/micro/src/index.test.ts` | ✅ | 测试(主测试文件) |
600
+ | `packages/micro/src/env-config.test.ts` | ✅ | 测试(环境变量配置测试) |
504
601
  | `packages/micro/src/server.ts` | ❌ | 底层协议,不动 |
505
602
  | `packages/micro/src/client.ts` | ❌ | 底层协议,不动 |
506
- | `packages/micro/src/registry.ts` | | 注册中心,不动 |
603
+ | `packages/micro/src/registry.ts` | | 注册中心(配置管理、路径工具函数) |
507
604
  | `packages/micro/src/utils.ts` | ❌ | 工具函数,不动 |
508
605
  | `packages/micro/README.md` | ✅ | 用户文档 |
509
606
  | `packages/micro/SKILL.md` | ✅ | AI 参考文档 |
607
+ | `packages/cli/src/index.ts` | ✅ | CLI 入口(registry configs 子命令组) |
608
+ | `packages/cli/src/configs.ts` | ✅ | CLI 配置管理 handler(list/get/set/del) |
609
+ | `packages/cli/src/start.ts` | ❌ | 启动逻辑,不动 |
610
+ | `packages/cli/src/exitHook.ts` | ❌ | 退出钩子,不动 |
510
611
 
511
612
  ---
512
613
 
@@ -514,7 +615,9 @@ pnpm --filter @hile/micro test # 必须全部通过
514
615
 
515
616
  | 文件 | 用途 |
516
617
  |------|------|
517
- | `packages/micro/src/application.ts` | 完整实现(共 ~310 行) |
518
- | `packages/micro/src/index.test.ts` | 28 个测试用例 |
519
- | `docs/superpowers/specs/2026-05-14-micro-improvements-design.md` | 四种改进的设计规约 |
520
- | `docs/superpowers/plans/2026-05-14-micro-improvements.md` | 实施计划 |
618
+ | `packages/micro/src/registry.ts` | 注册中心(含配置管理、路径工具函数) |
619
+ | `packages/micro/src/application.ts` | 应用服务(含 getEnvVariables) |
620
+ | `packages/micro/src/index.test.ts` | 主测试文件(28 个用例) |
621
+ | `packages/micro/src/env-config.test.ts` | 配置管理测试文件(13 个用例) |
622
+ | `packages/cli/src/index.ts` | CLI 入口(含 registry configs 子命令组) |
623
+ | `packages/cli/src/configs.ts` | CLI 配置管理 handler(list/get/set/del) |
@@ -1,6 +1,21 @@
1
1
  import { Client } from './client.js';
2
2
  import { Server, type MicroServerProps } from './server.js';
3
3
  import { RegistryAddress } from './registry.js';
4
+ type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
5
+ type EnvRequest<T extends Record<string, Record<string, any>>> = {
6
+ [N in keyof T]: {
7
+ namespace: N;
8
+ fields?: readonly (keyof T[N])[];
9
+ };
10
+ }[keyof T];
11
+ type EnvFieldsForRequest<T extends Record<string, Record<string, any>>, N extends keyof T, F> = F extends readonly (infer K extends keyof T[N])[] ? Pick<T[N], K> : T[N];
12
+ type EnvRequestResult<T extends Record<string, Record<string, any>>, R> = R extends {
13
+ namespace: infer N extends keyof T;
14
+ fields?: infer F;
15
+ } ? {
16
+ [K in N]: EnvFieldsForRequest<T, N, F>;
17
+ } : never;
18
+ export type GetEnvVariablesResult<T extends Record<string, Record<string, any>>, Requests extends readonly EnvRequest<T>[]> = UnionToIntersection<EnvRequestResult<T, Requests[number]>>;
4
19
  export type ApplicationProps = {
5
20
  namespace: string;
6
21
  registry: RegistryAddress;
@@ -35,4 +50,6 @@ export declare class Application extends Server {
35
50
  private findFromRegistry;
36
51
  get(namespace: string, exclude?: string[]): Promise<Client>;
37
52
  call<T = any>(namespace: string, url: string, data: any, timeout?: number, retries?: number): Promise<T>;
53
+ getEnvVariables<T extends Record<string, Record<string, any>>, const Requests extends readonly EnvRequest<T>[] = readonly EnvRequest<T>[]>(...data: Requests): Promise<GetEnvVariablesResult<T, Requests>>;
38
54
  }
55
+ export {};
@@ -293,4 +293,15 @@ export class Application extends Server {
293
293
  throw err;
294
294
  }
295
295
  }
296
+ async getEnvVariables(...data) {
297
+ if (!this.registry)
298
+ throw new Error('Registry not found');
299
+ const { response } = this.registry.request('/-/env/variables', data);
300
+ const configs = await response();
301
+ const out = {};
302
+ for (const { namespace, value } of configs) {
303
+ out[namespace] = value;
304
+ }
305
+ return out;
306
+ }
296
307
  }
@@ -10,15 +10,23 @@ export interface RegistryAddress {
10
10
  /** 将 `host:port` 或 `[ipv6]:port` 形式的 key 解析为地址(端口取最后一个 `:` 之后) */
11
11
  export declare function parseAddressKey(key: string): RegistryAddress | undefined;
12
12
  export declare function selectRandomRegistryAddress(keys: Iterable<string>): RegistryAddress | undefined;
13
+ export declare function getRegistryConfigsDir(): string;
14
+ export declare function namespaceToConfigFile(ns: string): string;
15
+ export declare function parseConfigFilename(filename: string): string | null;
13
16
  export declare class Registry extends Server {
14
17
  private readonly namespaces;
15
18
  private unregisterFind?;
16
19
  private static readonly HEARTBEAT_INTERVAL;
17
20
  private static readonly HEARTBEAT_TIMEOUT;
18
21
  private readonly heartbeats;
22
+ private readonly workspace;
23
+ private readonly configFileSuffix;
24
+ private readonly configs;
19
25
  constructor(props?: MicroServerProps);
26
+ watchEnvFile(): import("node:fs").FSWatcher | undefined;
20
27
  listen(port?: number): Promise<() => Promise<void>>;
21
28
  /** 幂等:重复调用会先注销上一条 `/-/find` 再注册,避免叠多条路由 */
22
29
  onFind(): void;
23
30
  private mountFindHandler;
31
+ private registerEnvVariables;
24
32
  }
package/dist/registry.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { Server } from './server.js';
2
+ import { homedir } from 'node:os';
3
+ import { resolve, join } from 'node:path';
4
+ import { existsSync, mkdirSync, readdirSync, readFileSync, watch } from 'node:fs';
5
+ import YAML from 'yaml';
2
6
  /** 将 `host:port` 或 `[ipv6]:port` 形式的 key 解析为地址(端口取最后一个 `:` 之后) */
3
7
  export function parseAddressKey(key) {
4
8
  const i = key.lastIndexOf(':');
@@ -24,14 +28,33 @@ export function selectRandomRegistryAddress(keys) {
24
28
  const index = Math.floor(Math.random() * addresses.length);
25
29
  return addresses[index];
26
30
  }
31
+ export function getRegistryConfigsDir() {
32
+ return resolve(homedir(), '.registry', 'configs');
33
+ }
34
+ export function namespaceToConfigFile(ns) {
35
+ return join(getRegistryConfigsDir(), `${ns}.config.yaml`);
36
+ }
37
+ export function parseConfigFilename(filename) {
38
+ if (!filename.endsWith('.config.yaml'))
39
+ return null;
40
+ return filename.slice(0, -'.config.yaml'.length);
41
+ }
27
42
  export class Registry extends Server {
28
43
  namespaces = new Map();
29
44
  unregisterFind;
30
45
  static HEARTBEAT_INTERVAL = 1000;
31
46
  static HEARTBEAT_TIMEOUT = 20000;
32
47
  heartbeats = new Map();
33
- constructor(props) {
34
- super('registry', props ?? {});
48
+ workspace;
49
+ configFileSuffix = '.config.yaml';
50
+ configs = new Map();
51
+ constructor(props = {}) {
52
+ const workspace = resolve(homedir(), '.registry');
53
+ if (!existsSync(workspace)) {
54
+ mkdirSync(workspace, { recursive: true });
55
+ }
56
+ super('registry', props);
57
+ this.workspace = workspace;
35
58
  this.events.on('connect', (client, extras) => {
36
59
  const key = client.host + ':' + client.port;
37
60
  this.heartbeats.set(key, Date.now());
@@ -56,6 +79,7 @@ export class Registry extends Server {
56
79
  }
57
80
  });
58
81
  this.mountFindHandler();
82
+ this.registerEnvVariables();
59
83
  this.register('/-/heartbeat', async ({ client }) => {
60
84
  if (!client)
61
85
  return;
@@ -63,8 +87,38 @@ export class Registry extends Server {
63
87
  this.heartbeats.set(key, Date.now());
64
88
  });
65
89
  }
90
+ watchEnvFile() {
91
+ const configFile = resolve(this.workspace, 'configs');
92
+ if (!existsSync(configFile))
93
+ return;
94
+ const configFiles = readdirSync(configFile).filter(filename => filename.endsWith(this.configFileSuffix));
95
+ for (const filename of configFiles) {
96
+ try {
97
+ const config = YAML.parse(readFileSync(resolve(configFile, filename), 'utf8'));
98
+ if (typeof config !== 'object' || config === null)
99
+ continue;
100
+ this.configs.set(parseConfigFilename(filename), config);
101
+ }
102
+ catch { }
103
+ }
104
+ return watch(configFile, (_, filename) => {
105
+ if (!filename?.endsWith(this.configFileSuffix))
106
+ return;
107
+ try {
108
+ const config = YAML.parse(readFileSync(resolve(configFile, filename), 'utf8'));
109
+ if (typeof config !== 'object' || config === null)
110
+ return;
111
+ this.configs.set(parseConfigFilename(filename), config);
112
+ }
113
+ catch { /* vim 替换文件时的中间态读错误,忽略 */ }
114
+ });
115
+ }
66
116
  async listen(port = 0) {
67
- const teardown = await super.listen(port);
117
+ const registry_port = process.env.REGISTRY_PORT ? Number(process.env.REGISTRY_PORT) : 0;
118
+ const _port = port || registry_port;
119
+ if (!_port || _port <= 0)
120
+ throw new Error('Unable to resolve registry port: pass `port` in constructor options, or ensure process.env.REGISTRY_PORT is set.');
121
+ const teardown = await super.listen(_port);
68
122
  const timer = setInterval(() => {
69
123
  const now = Date.now();
70
124
  for (const [key, lastTime] of this.heartbeats) {
@@ -77,7 +131,10 @@ export class Registry extends Server {
77
131
  }
78
132
  }
79
133
  }, Registry.HEARTBEAT_INTERVAL);
134
+ const watcher = this.watchEnvFile();
80
135
  return async () => {
136
+ if (watcher)
137
+ watcher.close();
81
138
  clearInterval(timer);
82
139
  await teardown();
83
140
  };
@@ -106,4 +163,21 @@ export class Registry extends Server {
106
163
  return selectRandomRegistryAddress(keys);
107
164
  });
108
165
  }
166
+ registerEnvVariables() {
167
+ this.register('/-/env/variables', async ({ data }) => {
168
+ return data.map(({ namespace, fields }) => {
169
+ if (!this.configs.has(namespace)) {
170
+ return { namespace, value: null };
171
+ }
172
+ if (!fields?.length)
173
+ return { namespace, value: this.configs.get(namespace) };
174
+ const config = this.configs.get(namespace);
175
+ const value = {};
176
+ for (const field of fields) {
177
+ value[field] = config[field];
178
+ }
179
+ return { namespace, value };
180
+ });
181
+ });
182
+ }
109
183
  }
package/dist/server.d.ts CHANGED
@@ -18,6 +18,7 @@ export declare class Server extends MessageLoader {
18
18
  protected readonly clients: Map<string, Client>;
19
19
  private readonly announceHost;
20
20
  readonly events: EventEmitter<any>;
21
+ get host(): string;
21
22
  constructor(namespace: string, props?: MicroServerProps);
22
23
  private upstream;
23
24
  private createClient;
package/dist/server.js CHANGED
@@ -12,6 +12,9 @@ export class Server extends MessageLoader {
12
12
  clients = new Map();
13
13
  announceHost;
14
14
  events = new EventEmitter();
15
+ get host() {
16
+ return this.announceHost;
17
+ }
15
18
  constructor(namespace, props = {}) {
16
19
  const { advertiseHost, ...loaderProps } = props;
17
20
  super(loaderProps);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hile/micro",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -26,7 +26,8 @@
26
26
  "@hile/message-loader": "^1.0.8",
27
27
  "@hile/message-ws": "^1.0.6",
28
28
  "internal-ip": "^9.0.0",
29
- "ws": "^8.19.0"
29
+ "ws": "^8.19.0",
30
+ "yaml": "^2.9.0"
30
31
  },
31
- "gitHead": "ddc82850295d26358cbffbb695e642cdb748339d"
32
+ "gitHead": "fdf2a5159fef97ca1f8258d6d11c62a061fd9ef4"
32
33
  }