@hile/micro-dynamic-configs 2.0.0 → 2.0.1
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/package.json +4 -4
- package/SKILL.md +0 -174
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hile/micro-dynamic-configs",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"vitest": "^4.0.18"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@hile/ioredis": "^
|
|
26
|
-
"@hile/micro": "^2.0.
|
|
25
|
+
"@hile/ioredis": "^2.0.0",
|
|
26
|
+
"@hile/micro": "^2.0.1",
|
|
27
27
|
"ioredis": "^5.10.0",
|
|
28
28
|
"zod": "^4.4.3"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "8e0fd1f78b5a8abd21218d1f596ada2533a0c8e7"
|
|
31
31
|
}
|
package/SKILL.md
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: micro-dynamic-configs
|
|
3
|
-
description: Code generation and contribution rules for @hile/micro-dynamic-configs. Use when editing this package or when the user asks about dynamic config patterns or API.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# @hile/micro-dynamic-configs
|
|
7
|
-
|
|
8
|
-
面向 AI 编码模型与维护者的代码生成规范。阅读后应能正确使用本库编写符合架构规则的代码。
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 1. 架构总览
|
|
13
|
-
|
|
14
|
-
通过 `@hile/micro` 的 pub/sub 机制推送配置变更,每个 schema 字段一个独立 topic。
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
Publisher Config Server Subscriber
|
|
18
|
-
│ │ │
|
|
19
|
-
│ save({ key: val }) │ │
|
|
20
|
-
├──────────────────────────► │ │
|
|
21
|
-
│ │ publisher.update(newValue) │
|
|
22
|
-
│ │ ──────────────────────────────► │
|
|
23
|
-
│ │ (通过 Registry 广播 topic) │
|
|
24
|
-
│ │ │
|
|
25
|
-
│ │ app.subscribe('ns:key', cb) │
|
|
26
|
-
│ │ ◄────────────────────────────── │
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
- **Config Server** (`MicroDynamicConfigsServer`) — 持有配置数据,校验、持久化,通过 `app.publish` 注册 publisher
|
|
30
|
-
- **Subscriber** — 直接用 `app.subscribe(topic, callback)` 接收推送,无需客户端层
|
|
31
|
-
- **Publisher** — 调用 `server.save()` 修改配置,数据流:validate → Redis 持久化 → 内存更新 → `publisher.update()` → `change:key` 事件
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## 2. 类型签名
|
|
36
|
-
|
|
37
|
-
### MicroDynamicConfigsServer
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
class MicroDynamicConfigsServer<
|
|
41
|
-
T extends Application,
|
|
42
|
-
Z extends ZodObject<ZodRawShape>
|
|
43
|
-
> extends EventEmitter
|
|
44
|
-
|
|
45
|
-
constructor(options: {
|
|
46
|
-
app: T // Application 实例
|
|
47
|
-
redis: Redis // ioredis 实例
|
|
48
|
-
schema: Z // Zod schema 定义配置结构
|
|
49
|
-
redis_key: string // Redis 存储键名
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
// 从 Redis 加载持久化数据,为每个 schema 字段注册 publisher,返回 teardown 函数
|
|
53
|
-
initialize(): Promise<() => Promise<void>>
|
|
54
|
-
|
|
55
|
-
// 持久化并推送变更
|
|
56
|
-
save(value: Partial<z.infer<Z>>): Promise<number>
|
|
57
|
-
|
|
58
|
-
// 当前配置快照(只读)
|
|
59
|
-
get value(): z.infer<Z>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### 事件
|
|
63
|
-
|
|
64
|
-
| 事件 | 参数 | 说明 |
|
|
65
|
-
|------|------|------|
|
|
66
|
-
| `change:{field}` | `(newValue, oldValue)` | 字段变更时触发,每个字段独立事件名 |
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 3. Topic 约定
|
|
71
|
-
|
|
72
|
-
每个 schema 字段一个 topic,格式为:
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
{namespace}:{field}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
例如 namespace `config-svc`、字段 `name`、`port`、`debug`:
|
|
79
|
-
|
|
80
|
-
- `config-svc:name`
|
|
81
|
-
- `config-svc:port`
|
|
82
|
-
- `config-svc:debug`
|
|
83
|
-
|
|
84
|
-
### 订阅
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
const values: Record<string, any> = {};
|
|
88
|
-
const unsub = await app.subscribe('config-svc:name', (v) => values.name = v);
|
|
89
|
-
// unsub() 取消订阅
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## 4. 代码生成强制规则
|
|
95
|
-
|
|
96
|
-
### 4.1 save() 必须分两轮执行
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
// ✓ 正确:先 validate 再 apply
|
|
100
|
-
// Pass 1: validate & diff
|
|
101
|
-
const entries = [];
|
|
102
|
-
for (const key of keys) {
|
|
103
|
-
const parsed = schema.parse(value[key]);
|
|
104
|
-
if (!deepEqual(old, parsed)) entries.push({ key, parsed });
|
|
105
|
-
}
|
|
106
|
-
// Pass 2: persist → memory → publish + emit
|
|
107
|
-
await redis.set(key, JSON.stringify(next));
|
|
108
|
-
update memory;
|
|
109
|
-
emit 'change:{key}' events;
|
|
110
|
-
|
|
111
|
-
// ✗ 禁止:validate 和 mutate 混在一轮
|
|
112
|
-
for (const key of keys) {
|
|
113
|
-
this._value[key] = parsed; // ❌ 后续字段失败时 _value 已脏
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### 4.2 initialize() 中注册 publisher
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
// ✓ 正确:initialize 时调用 app.publish 注册所有字段
|
|
121
|
-
const publisher = await this.app.publish(`${namespace}:${key}`, initialValue);
|
|
122
|
-
this.publishers.set(key, publisher);
|
|
123
|
-
|
|
124
|
-
// save 时通过 publisher.update 推送
|
|
125
|
-
await this.publishers.get(key)!.update(newValue);
|
|
126
|
-
|
|
127
|
-
// teardown 时 unpublish
|
|
128
|
-
await publisher.unpublish();
|
|
129
|
-
|
|
130
|
-
// ✗ 禁止:save 中每次调用 app.publish(应复用 initialize 时注册的 publisher)
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 4.3 teardown 必须清理所有资源
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
// ✓ 正确:teardown 需 unpublish + clear + removeAllListeners
|
|
137
|
-
return async () => {
|
|
138
|
-
for (const publisher of this.publishers.values()) {
|
|
139
|
-
await publisher.unpublish();
|
|
140
|
-
}
|
|
141
|
-
this.publishers.clear();
|
|
142
|
-
this.removeAllListeners();
|
|
143
|
-
};
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### 4.4 deep diff 避免无效推送
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
// ✓ 正确:值未变化时跳过 publish
|
|
150
|
-
if (isDeepStrictEqual(oldValue, parsed)) {
|
|
151
|
-
continue; // 不写入 entries,不触发 publish 和事件
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
## 5. 边界条件清单
|
|
158
|
-
|
|
159
|
-
- [ ] `save()` 传入空对象 `{}` — 返回 0,无操作
|
|
160
|
-
- [ ] `save()` 传入 schema 中不存在的字段 — 跳过,不报错
|
|
161
|
-
- [ ] `save()` 中单个字段校验失败 — 整个操作 reject,`_value` 不变
|
|
162
|
-
- [ ] `save()` 中所有字段值与当前一致 — 返回 0,不写 Redis 不推送
|
|
163
|
-
- [ ] `save()` Redis 写入失败 — throw,`_value` 不变
|
|
164
|
-
- [ ] `initialize()` 时 Redis 无数据 — 使用 schema 默认值
|
|
165
|
-
- [ ] 多个字段同时变更 — 一轮 `save()` 中逐个 `publisher.update()` + `change:key` 事件
|
|
166
|
-
- [ ] Deep diff 检测嵌套对象变化 — 嵌套字段内容不变时跳过 publish
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## 6. 依赖
|
|
171
|
-
|
|
172
|
-
- `@hile/micro` — 提供 `Application`、`app.publish`/`app.subscribe`
|
|
173
|
-
- `ioredis` — Redis 持久化
|
|
174
|
-
- `zod` — Schema 定义与校验
|