@hile/core 1.0.18 → 1.0.20
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 +3 -3
- package/SKILL.md +63 -131
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -138,7 +138,7 @@ const container = new Container({
|
|
|
138
138
|
订阅事件:
|
|
139
139
|
|
|
140
140
|
```typescript
|
|
141
|
-
const off = container.
|
|
141
|
+
const off = container.on((event) => {
|
|
142
142
|
if (event.type === 'service:ready') {
|
|
143
143
|
console.log(`service#${event.id} ready in ${event.durationMs}ms`)
|
|
144
144
|
}
|
|
@@ -218,8 +218,8 @@ const result = await container.resolve(service)
|
|
|
218
218
|
| `register(fn)` | 注册服务(同函数引用去重) |
|
|
219
219
|
| `resolve(props)` | 加载服务(执行、等待或返回缓存) |
|
|
220
220
|
| `shutdown()` | 销毁所有服务并执行清理回调 |
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
221
|
+
| `on(listener)` | 订阅容器事件,返回取消订阅函数 |
|
|
222
|
+
| `off(listener)` | 取消订阅 |
|
|
223
223
|
| `getLifecycle(id)` | 获取服务生命周期阶段 |
|
|
224
224
|
| `getDependencyGraph()` | 获取依赖图 `{ nodes, edges }` |
|
|
225
225
|
| `getStartupOrder()` | 获取服务启动顺序(首次启动顺序) |
|
package/SKILL.md
CHANGED
|
@@ -1,180 +1,112 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hile-core
|
|
3
|
-
description: @hile/core 的代码生成与使用规范。适用于定义/加载 Hile
|
|
3
|
+
description: "@hile/core 的代码生成与使用规范。适用于定义/加载 Hile 服务、生命周期编排、依赖图与容器事件相关场景。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# @hile/core SKILL
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
本文档面向代码生成器与维护者,目标是确保生成代码严格符合容器语义。
|
|
9
9
|
|
|
10
|
-
## 1.
|
|
10
|
+
## 1. 强约束(必须遵守)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
1. 服务必须使用 `async (shutdown)` 形态定义。
|
|
13
|
+
2. 只能通过 `defineService` / `container.register` 产出服务对象。
|
|
14
|
+
3. 只能通过 `loadService` / `container.resolve` 获取服务实例。
|
|
15
|
+
4. 外部资源创建后必须立即注册 `shutdown`。
|
|
16
|
+
5. 禁止在模块顶层缓存 `await loadService(...)` 结果。
|
|
17
|
+
6. 依赖服务必须在服务函数内部加载。
|
|
18
|
+
7. 多个 teardown 默认按 LIFO 顺序执行。
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
- 并发合并:并发加载同一服务时共享同一初始化过程
|
|
16
|
-
- 失败回收:初始化失败时自动执行已注册的清理回调
|
|
20
|
+
## 2. 生命周期与超时约束
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
容器生命周期:`init -> ready -> stopping -> stopped`。
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
- 启动超时:`new Container({ startTimeoutMs })`
|
|
25
|
+
- 销毁超时:`new Container({ shutdownTimeoutMs })`
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
type ServiceCutDownFunction = () => unknown | Promise<unknown>;
|
|
24
|
-
type ServiceCutDownHandler = (fn: ServiceCutDownFunction) => void;
|
|
25
|
-
type ServiceFunction<R> = (shutdown: ServiceCutDownHandler) => R | Promise<R>;
|
|
26
|
-
|
|
27
|
-
const sericeFlag = Symbol('service');
|
|
27
|
+
生成代码时:
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
fn: ServiceFunction<R>;
|
|
32
|
-
flag: typeof sericeFlag;
|
|
33
|
-
}
|
|
34
|
-
```
|
|
29
|
+
- 不要吞掉启动超时错误。
|
|
30
|
+
- 不要假设 teardown 一定成功;应允许 `service:shutdown:error` 事件出现。
|
|
35
31
|
|
|
36
|
-
## 3.
|
|
32
|
+
## 3. 可观测事件约束
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
允许订阅:`container.on(listener)`。
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
import { defineService } from '@hile/core'
|
|
36
|
+
关键事件:
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
- `service:init`
|
|
39
|
+
- `service:ready`
|
|
40
|
+
- `service:error`
|
|
41
|
+
- `service:shutdown:start`
|
|
42
|
+
- `service:shutdown:done`
|
|
43
|
+
- `service:shutdown:error`
|
|
44
|
+
- `container:shutdown:start`
|
|
45
|
+
- `container:shutdown:done`
|
|
46
|
+
- `container:error`
|
|
49
47
|
|
|
50
48
|
规则:
|
|
51
49
|
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
- `defineService` 结果需使用模块级 `export const` 暴露
|
|
55
|
-
- 命名建议以 `Service` 结尾
|
|
50
|
+
- 订阅后必须在生命周期结束时取消订阅。
|
|
51
|
+
- 记录错误时保留原始 error 对象。
|
|
56
52
|
|
|
57
|
-
|
|
53
|
+
## 4. 依赖图与循环依赖
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
import { loadService } from '@hile/core'
|
|
61
|
-
import { databaseService } from './services/database'
|
|
55
|
+
容器会自动记录服务依赖并检测循环依赖:
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
- `getDependencyGraph()`
|
|
58
|
+
- `getStartupOrder()`
|
|
65
59
|
|
|
66
60
|
规则:
|
|
67
61
|
|
|
68
|
-
-
|
|
69
|
-
- `
|
|
62
|
+
- 不要绕开容器手动构建“隐式全局单例依赖”。
|
|
63
|
+
- 出现 `circular dependency detected` 时应通过拆分服务职责或引入中间层服务解决。
|
|
70
64
|
|
|
71
|
-
|
|
65
|
+
## 5. 反模式(禁止)
|
|
72
66
|
|
|
73
|
-
|
|
74
|
-
import { defineService, loadService } from '@hile/core'
|
|
75
|
-
import { databaseService } from './database'
|
|
76
|
-
|
|
77
|
-
export const userService = defineService(async (shutdown) => {
|
|
78
|
-
const db = await loadService(databaseService)
|
|
79
|
-
return new UserRepository(db)
|
|
80
|
-
})
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
规则:
|
|
84
|
-
|
|
85
|
-
- 在服务函数内部加载依赖
|
|
86
|
-
- 不在模块顶层缓存 `loadService` 结果
|
|
87
|
-
|
|
88
|
-
### 3.4 注册清理回调
|
|
67
|
+
### 5.1 顶层缓存实例
|
|
89
68
|
|
|
90
69
|
```typescript
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
shutdown(() => a.close())
|
|
94
|
-
|
|
95
|
-
const b = await connectB()
|
|
96
|
-
shutdown(() => b.close())
|
|
70
|
+
// ✗
|
|
71
|
+
const db = await loadService(dbService)
|
|
97
72
|
|
|
98
|
-
|
|
99
|
-
|
|
73
|
+
// ✓
|
|
74
|
+
export async function query(sql: string) {
|
|
75
|
+
const db = await loadService(dbService)
|
|
76
|
+
return db.query(sql)
|
|
77
|
+
}
|
|
100
78
|
```
|
|
101
79
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- 资源创建后立即注册清理
|
|
105
|
-
- 回调按 LIFO 执行
|
|
106
|
-
- 支持异步回调
|
|
107
|
-
|
|
108
|
-
### 3.5 全局优雅关闭
|
|
80
|
+
### 5.2 手动伪造服务对象
|
|
109
81
|
|
|
110
82
|
```typescript
|
|
111
|
-
|
|
83
|
+
// ✗
|
|
84
|
+
const fake = { id: 1, fn: async () => 1 }
|
|
112
85
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
process.exit(0)
|
|
116
|
-
})
|
|
86
|
+
// ✓
|
|
87
|
+
const real = defineService(async () => 1)
|
|
117
88
|
```
|
|
118
89
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
1. 服务函数必须使用 `async`,避免同步 `throw` 破坏销毁机制。
|
|
122
|
-
2. 不要手动构造 `ServiceRegisterProps`。
|
|
123
|
-
3. 不要在工厂函数里动态调用 `defineService` 生成新引用。
|
|
124
|
-
4. 不要在模块顶层 `await loadService(...)`。
|
|
125
|
-
5. 每个外部资源都应对应一次 `shutdown` 注册。
|
|
126
|
-
|
|
127
|
-
## 5. 常见反模式
|
|
128
|
-
|
|
129
|
-
### 同步 throw
|
|
90
|
+
### 5.3 不注册资源清理
|
|
130
91
|
|
|
131
92
|
```typescript
|
|
132
93
|
// ✗
|
|
133
|
-
export const bad = defineService((
|
|
134
|
-
|
|
135
|
-
shutdown(() => r.close())
|
|
136
|
-
throw new Error('boom')
|
|
94
|
+
export const bad = defineService(async () => {
|
|
95
|
+
return await createPool()
|
|
137
96
|
})
|
|
138
97
|
|
|
139
98
|
// ✓
|
|
140
99
|
export const good = defineService(async (shutdown) => {
|
|
141
|
-
const
|
|
142
|
-
shutdown(() =>
|
|
143
|
-
|
|
100
|
+
const pool = await createPool()
|
|
101
|
+
shutdown(() => pool.end())
|
|
102
|
+
return pool
|
|
144
103
|
})
|
|
145
104
|
```
|
|
146
105
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
// ✗
|
|
151
|
-
const db = await loadService(databaseService)
|
|
152
|
-
|
|
153
|
-
// ✓
|
|
154
|
-
export async function query(sql: string) {
|
|
155
|
-
const db = await loadService(databaseService)
|
|
156
|
-
return db.query(sql)
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## 6. API 速查
|
|
161
|
-
|
|
162
|
-
### 便捷函数
|
|
163
|
-
|
|
164
|
-
| 函数 | 说明 |
|
|
165
|
-
|---|---|
|
|
166
|
-
| `defineService(fn)` | 注册服务到默认容器 |
|
|
167
|
-
| `loadService(props)` | 加载服务实例 |
|
|
168
|
-
| `isService(props)` | 判断是否为合法服务注册对象 |
|
|
169
|
-
|
|
170
|
-
### Container
|
|
106
|
+
## 6. 边界条件清单
|
|
171
107
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
| `hasMeta(id)` | 检查运行时元数据 |
|
|
178
|
-
| `getIdByService(fn)` | 通过函数获取 ID |
|
|
179
|
-
| `getMetaById(id)` | 通过 ID 获取元数据 |
|
|
180
|
-
| `shutdown()` | 销毁所有服务 |
|
|
108
|
+
- [ ] 服务同步抛错路径是否可观测
|
|
109
|
+
- [ ] 异步 reject 路径是否会触发 teardown
|
|
110
|
+
- [ ] teardown 抛错是否不覆盖原始业务错误
|
|
111
|
+
- [ ] 并发 resolve 同一服务是否只初始化一次
|
|
112
|
+
- [ ] shutdown 重复调用是否幂等
|
package/dist/index.d.ts
CHANGED
|
@@ -73,8 +73,8 @@ export declare class Container {
|
|
|
73
73
|
private getId;
|
|
74
74
|
private hasPath;
|
|
75
75
|
private trackDependency;
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
on(listener: (event: ContainerEvent) => void): () => boolean;
|
|
77
|
+
off(listener: (event: ContainerEvent) => void): void;
|
|
78
78
|
getLifecycle(id: number): ServiceLifecycleStage | undefined;
|
|
79
79
|
getDependencyGraph(): {
|
|
80
80
|
nodes: number[];
|
package/dist/index.js
CHANGED
|
@@ -83,11 +83,11 @@ export class Container {
|
|
|
83
83
|
this.dependents.get(childId).add(parentId);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
-
|
|
86
|
+
on(listener) {
|
|
87
87
|
this.listeners.add(listener);
|
|
88
88
|
return () => this.listeners.delete(listener);
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
off(listener) {
|
|
91
91
|
this.listeners.delete(listener);
|
|
92
92
|
}
|
|
93
93
|
getLifecycle(id) {
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hile/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Hile core - lightweight async service container",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsc -b",
|
|
8
|
+
"build": "tsc -b && fix-esm-import-path --preserve-import-type ./dist",
|
|
9
9
|
"dev": "tsc -b --watch",
|
|
10
10
|
"test": "vitest run"
|
|
11
11
|
},
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^25.3.1",
|
|
24
|
+
"fix-esm-import-path": "^1.10.3",
|
|
24
25
|
"vitest": "^4.0.18"
|
|
25
26
|
},
|
|
26
|
-
"gitHead": "
|
|
27
|
+
"gitHead": "07067534cbd2e1c4011c72f74d16d18506081aac"
|
|
27
28
|
}
|