@hile/core 1.0.16 → 1.0.17

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.
Files changed (3) hide show
  1. package/README.md +45 -53
  2. package/SKILL.md +75 -351
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Hile
1
+ # @hile/core
2
2
 
3
- 轻量级异步服务容器,提供单例管理、并发请求合并和资源生命周期销毁。纯 TypeScript 实现,零运行时依赖。
3
+ 轻量级异步服务容器,提供单例管理、并发请求合并与生命周期销毁能力。纯 TypeScript 实现,零运行时依赖。
4
4
 
5
5
  ## 安装
6
6
 
@@ -13,7 +13,6 @@ pnpm add @hile/core
13
13
  ```typescript
14
14
  import { defineService, loadService } from '@hile/core'
15
15
 
16
- // 定义服务
17
16
  const greeterService = defineService(async (shutdown) => {
18
17
  return {
19
18
  hello(name: string) {
@@ -22,16 +21,15 @@ const greeterService = defineService(async (shutdown) => {
22
21
  }
23
22
  })
24
23
 
25
- // 加载并使用
26
24
  const greeter = await loadService(greeterService)
27
- greeter.hello('World') // "Hello, World!"
25
+ greeter.hello('World') // Hello, World!
28
26
  ```
29
27
 
30
28
  ## 核心概念
31
29
 
32
- ### 定义服务
30
+ ### 1) 定义服务
33
31
 
34
- 使用 `defineService` 注册一个服务。服务函数接收一个 `shutdown` 参数,用于注册资源清理回调。
32
+ 通过 `defineService` 注册服务。服务函数接收 `shutdown` 注册器,用于登记资源清理回调。
35
33
 
36
34
  ```typescript
37
35
  import { defineService } from '@hile/core'
@@ -43,9 +41,9 @@ export const databaseService = defineService(async (shutdown) => {
43
41
  })
44
42
  ```
45
43
 
46
- ### 加载服务
44
+ ### 2) 加载服务
47
45
 
48
- 使用 `loadService` 获取服务实例。容器保证服务函数只执行一次,后续调用直接返回缓存结果。
46
+ 通过 `loadService` 获取服务实例。容器保证同一服务函数只执行一次。
49
47
 
50
48
  ```typescript
51
49
  import { loadService } from '@hile/core'
@@ -55,23 +53,23 @@ const db = await loadService(databaseService)
55
53
  const users = await db.query('SELECT * FROM users')
56
54
  ```
57
55
 
58
- ### 并发请求合并
56
+ ### 3) 并发请求合并
59
57
 
60
- 多个地方同时请求同一服务时,服务函数不会重复执行,所有调用者共享同一结果。
58
+ 并发加载同一服务时,初始化只执行一次,调用方共享结果。
61
59
 
62
60
  ```typescript
63
- // 三个并发调用,服务函数只执行一次
64
61
  const [r1, r2, r3] = await Promise.all([
65
62
  loadService(heavyService),
66
63
  loadService(heavyService),
67
64
  loadService(heavyService),
68
65
  ])
66
+
69
67
  // r1 === r2 === r3
70
68
  ```
71
69
 
72
- ### 服务间依赖
70
+ ### 4) 服务间依赖
73
71
 
74
- 在服务函数内部通过 `loadService` 加载所依赖的其他服务:
72
+ 服务内部可通过 `loadService` 继续加载依赖服务。
75
73
 
76
74
  ```typescript
77
75
  import { defineService, loadService } from '@hile/core'
@@ -94,37 +92,35 @@ export const userService = defineService(async (shutdown) => {
94
92
  })
95
93
  ```
96
94
 
97
- ### 资源销毁(Shutdown)
95
+ ### 5) 资源销毁(Shutdown)
98
96
 
99
- 服务函数的第一个参数 `shutdown` 是一个注册器,用于在初始化过程中注册清理回调。当服务启动失败时,容器会自动执行已注册的清理回调。
97
+ 当服务初始化失败或手动执行全局关闭时,容器会按规则执行已注册的清理回调。
100
98
 
101
99
  ```typescript
102
100
  export const connectionService = defineService(async (shutdown) => {
103
101
  const primary = await connectPrimary()
104
- shutdown(() => primary.disconnect()) // 最后执行
102
+ shutdown(() => primary.disconnect())
105
103
 
106
104
  const replica = await connectReplica()
107
- shutdown(() => replica.disconnect()) // 第 2 个执行
105
+ shutdown(() => replica.disconnect())
108
106
 
109
107
  const cache = await initCache()
110
- shutdown(() => cache.flush()) // 最先执行
108
+ shutdown(() => cache.flush())
111
109
 
112
110
  return { primary, replica, cache }
113
111
  })
114
112
  ```
115
113
 
116
- **关键特性:**
114
+ 特性:
117
115
 
118
- - 销毁回调按 **逆序(LIFO)** 执行——后注册的先执行
116
+ - 清理回调按逆序(LIFO)执行
119
117
  - 支持异步清理函数
120
- - 同一函数引用多次注册只会执行一次
121
- - 清理函数自身的错误不会影响原始错误的传播
122
-
123
- > **注意:** 请使用 `async` 函数定义服务。同步函数中直接 `throw` 的错误无法触发销毁机制。
118
+ - 同一函数引用重复注册只执行一次
119
+ - 清理函数错误不会覆盖原始业务错误
124
120
 
125
- ### 手动销毁(Graceful Shutdown)
121
+ > 建议始终使用 `async` 服务函数,确保异常路径可正确触发销毁机制。
126
122
 
127
- 调用 `container.shutdown()` 可手动触发所有已注册服务的销毁回调,适用于进程退出时优雅关闭资源:
123
+ ### 6) 手动销毁(Graceful Shutdown)
128
124
 
129
125
  ```typescript
130
126
  import container from '@hile/core'
@@ -135,24 +131,20 @@ process.on('SIGTERM', async () => {
135
131
  })
136
132
  ```
137
133
 
138
- 销毁按 **服务注册逆序** 执行:后注册的服务先销毁。调用后销毁队列清空,重复调用不会再次执行。
139
-
140
- ### 服务校验(isService)
141
-
142
- 使用 `isService` 判断一个对象是否为合法的服务注册信息。内部通过不可伪造的 Symbol 标识校验,确保只有通过 `defineService` / `container.register` 创建的对象才会返回 `true`:
134
+ ### 7) 服务校验(isService)
143
135
 
144
136
  ```typescript
145
137
  import { defineService, isService } from '@hile/core'
146
138
 
147
139
  const myService = defineService(async (shutdown) => 'hello')
148
140
 
149
- isService(myService) // true
150
- isService({ id: 1, fn: () => {} } as any) // false
141
+ isService(myService) // true
142
+ isService({ id: 1, fn: () => {} } as any) // false
151
143
  ```
152
144
 
153
145
  ## 隔离容器
154
146
 
155
- 除默认容器外,可以创建独立的 `Container` 实例,实现服务作用域隔离:
147
+ 除了默认容器,也可以手动创建独立容器以实现作用域隔离。
156
148
 
157
149
  ```typescript
158
150
  import { Container } from '@hile/core'
@@ -168,31 +160,31 @@ const result = await container.resolve(service)
168
160
 
169
161
  ## API
170
162
 
171
- ### 便捷函数
163
+ ### 顶层函数
172
164
 
173
165
  | 函数 | 说明 |
174
166
  |------|------|
175
- | `defineService(fn)` | 注册服务到默认容器,返回 `ServiceRegisterProps` |
176
- | `loadService(props)` | 从默认容器加载服务,返回 `Promise<R>` |
177
- | `isService(props)` | 判断对象是否为合法的服务注册信息(通过内部 Symbol 校验) |
167
+ | `defineService(fn)` | 注册服务到默认容器 |
168
+ | `loadService(props)` | 从默认容器加载服务 |
169
+ | `isService(props)` | 判断对象是否为合法服务注册信息 |
178
170
 
179
- ### Container
171
+ ### `Container`
180
172
 
181
173
  | 方法 | 说明 |
182
174
  |------|------|
183
- | `register(fn)` | 注册服务。同一函数引用只注册一次,返回 `ServiceRegisterProps` |
184
- | `resolve(props)` | 加载服务。根据当前状态决定执行、等待或返回缓存 |
185
- | `hasService(fn)` | 检查服务函数是否已注册 |
186
- | `hasMeta(id)` | 检查服务是否已运行(存在运行时元数据) |
187
- | `getIdByService(fn)` | 根据函数引用获取服务 ID |
188
- | `getMetaById(id)` | 根据服务 ID 获取运行时元数据 |
189
- | `shutdown()` | 手动销毁所有服务,返回 `Promise<void>`。按服务注册逆序执行所有销毁回调 |
175
+ | `register(fn)` | 注册服务(同函数引用去重) |
176
+ | `resolve(props)` | 加载服务(执行、等待或返回缓存) |
177
+ | `hasService(fn)` | 检查函数是否已注册 |
178
+ | `hasMeta(id)` | 检查服务是否已有运行时元数据 |
179
+ | `getIdByService(fn)` | 通过函数获取服务 ID |
180
+ | `getMetaById(id)` | 通过 ID 获取运行时元数据 |
181
+ | `shutdown()` | 销毁所有服务并执行清理回调 |
190
182
 
191
183
  ### 服务状态
192
184
 
193
- | 状态 | 值 | `resolve` 的行为 |
194
- |------|---|-----------------|
195
- | 从未运行 | — | 执行服务函数 |
185
+ | 状态 | 值 | `resolve` 行为 |
186
+ |------|---|---------------|
187
+ | 未运行 | — | 执行服务函数 |
196
188
  | 运行中 | `0` | 加入等待队列 |
197
189
  | 已成功 | `1` | 返回缓存值 |
198
190
  | 已失败 | `-1` | 返回缓存错误 |
@@ -201,9 +193,9 @@ const result = await container.resolve(service)
201
193
 
202
194
  ```bash
203
195
  pnpm install
204
- pnpm build # 编译
205
- pnpm dev # 监听模式
206
- pnpm test # 运行测试
196
+ pnpm build
197
+ pnpm dev
198
+ pnpm test
207
199
  ```
208
200
 
209
201
  ## License
package/SKILL.md CHANGED
@@ -1,44 +1,31 @@
1
1
  ---
2
2
  name: hile-core
3
- description: Code generation and usage rules for @hile/core async service container. Use when defining or loading Hile services, wiring lifecycle shutdown, or when the user asks about @hile/core, defineService, loadService, or service container patterns.
3
+ description: @hile/core 的代码生成与使用规范。适用于定义/加载 Hile 服务、生命周期 shutdown 编排、或涉及 defineServiceloadService、Container 等话题。
4
4
  ---
5
5
 
6
- # Hile
6
+ # @hile/core SKILL
7
7
 
8
- Hile 是一个轻量级异步服务容器。本文档是面向 AI 编码模型和人类开发者的 **代码生成规范**,阅读后应能正确地使用本库编写符合架构规则的代码。
8
+ 本文档用于约束 AI 与开发者在使用 `@hile/core` 时的代码生成方式,目标是保证服务定义、依赖加载与资源销毁行为一致且可维护。
9
9
 
10
- ---
11
-
12
- ## 1. 架构总览
13
-
14
- Hile 的核心是 `Container`(服务容器)。所有服务都必须经过 **定义 → 加载** 两个步骤才能使用。容器保证:
15
-
16
- - 每个服务函数只执行一次(**单例**)
17
- - 并发请求同一服务时自动合并(**不重复执行**)
18
- - 服务启动失败时自动执行已注册的清理回调(**销毁机制**)
10
+ ## 1. 架构概览
19
11
 
20
- 模块默认导出了一个全局容器实例,并提供 `defineService` / `loadService` 两个便捷函数。
12
+ Hile `Container` 为核心,服务遵循“定义 加载”两阶段:
21
13
 
22
- ---
14
+ - 单例:同一服务函数仅初始化一次
15
+ - 并发合并:并发加载同一服务时共享同一初始化过程
16
+ - 失败回收:初始化失败时自动执行已注册的清理回调
23
17
 
24
- ## 2. 类型签名
18
+ 模块默认导出全局容器,并提供 `defineService` / `loadService` 便捷函数。
25
19
 
26
- 生成代码时,必须严格遵循以下类型:
20
+ ## 2. 关键类型
27
21
 
28
22
  ```typescript
29
- // 销毁回调:无参数,可返回 Promise
30
23
  type ServiceCutDownFunction = () => unknown | Promise<unknown>;
31
-
32
- // 销毁注册器:在服务函数内部调用,将清理回调注册到容器
33
24
  type ServiceCutDownHandler = (fn: ServiceCutDownFunction) => void;
34
-
35
- // 服务函数:第一个参数固定为销毁注册器,返回值为同步值或 Promise
36
25
  type ServiceFunction<R> = (shutdown: ServiceCutDownHandler) => R | Promise<R>;
37
26
 
38
- // 内部服务标识(Symbol,不可外部构造)
39
27
  const sericeFlag = Symbol('service');
40
28
 
41
- // 服务注册信息:由 defineService/register 返回,作为 loadService/resolve 的入参
42
29
  interface ServiceRegisterProps<R> {
43
30
  id: number;
44
31
  fn: ServiceFunction<R>;
@@ -46,39 +33,28 @@ interface ServiceRegisterProps<R> {
46
33
  }
47
34
  ```
48
35
 
49
- ---
36
+ ## 3. 标准模板
50
37
 
51
- ## 3. 代码生成模板
52
-
53
- ### 3.1 定义服务(必须遵循的模式)
54
-
55
- **模板:**
38
+ ### 3.1 定义服务
56
39
 
57
40
  ```typescript
58
41
  import { defineService } from '@hile/core'
59
42
 
60
43
  export const xxxService = defineService(async (shutdown) => {
61
- // 1. 初始化资源
62
44
  const resource = await createResource()
63
-
64
- // 2. 注册销毁回调(每创建一个资源就注册一个对应的清理)
65
45
  shutdown(() => resource.close())
66
-
67
- // 3. 返回服务实例
68
46
  return resource
69
47
  })
70
48
  ```
71
49
 
72
- **规则:**
73
- - 服务函数的第一个参数 **必须** 命名为 `shutdown`,类型为 `ServiceCutDownHandler`
74
- - 服务函数 **应当** 使用 `async` 声明(确保销毁机制在失败时正确触发)
75
- - `defineService` 的返回值 **必须** 赋值给一个模块级常量并 `export`
76
- - 常量命名 **必须** 以 `Service` 结尾(如 `databaseService`、`cacheService`)
77
- - 每个服务定义 **应当** 放在独立的文件中
50
+ 规则:
78
51
 
79
- ### 3.2 加载服务
52
+ - 服务函数第一个参数固定为 `shutdown`
53
+ - 推荐必须使用 `async`
54
+ - `defineService` 结果需使用模块级 `export const` 暴露
55
+ - 命名建议以 `Service` 结尾
80
56
 
81
- **模板:**
57
+ ### 3.2 加载服务
82
58
 
83
59
  ```typescript
84
60
  import { loadService } from '@hile/core'
@@ -87,68 +63,49 @@ import { databaseService } from './services/database'
87
63
  const db = await loadService(databaseService)
88
64
  ```
89
65
 
90
- **规则:**
91
- - **永远** 使用 `loadService()` 获取服务实例,**不要** 直接调用服务函数
92
- - `loadService` 返回 `Promise`,**必须** `await`
93
- - 可以在任何地方多次调用 `loadService(同一个服务)`,容器保证只执行一次
66
+ 规则:
94
67
 
95
- ### 3.3 服务间依赖
68
+ - 始终通过 `loadService` 获取实例
69
+ - `loadService` 返回 Promise,必须 `await`
96
70
 
97
- 当一个服务依赖另一个服务时,在服务函数内部通过 `loadService` 加载依赖:
71
+ ### 3.3 服务依赖服务
98
72
 
99
73
  ```typescript
100
74
  import { defineService, loadService } from '@hile/core'
101
75
  import { databaseService } from './database'
102
- import { configService } from './config'
103
76
 
104
77
  export const userService = defineService(async (shutdown) => {
105
- // 加载依赖的服务(若已完成则直接返回缓存,否则等待)
106
- const config = await loadService(configService)
107
78
  const db = await loadService(databaseService)
108
-
109
- const repo = new UserRepository(db, config)
110
- shutdown(() => repo.dispose())
111
-
112
- return repo
79
+ return new UserRepository(db)
113
80
  })
114
81
  ```
115
82
 
116
- **规则:**
117
- - 依赖的服务通过 `import` 引入其 `ServiceRegisterProps`,然后在函数体内 `loadService` 加载
118
- - **不要** 将 `loadService` 的结果缓存到模块作用域变量中
119
- - **不要** 在服务函数外部调用 `loadService` 来获取另一个服务并传入——应在服务函数内部加载
83
+ 规则:
120
84
 
121
- ### 3.4 注册销毁回调
85
+ - 在服务函数内部加载依赖
86
+ - 不在模块顶层缓存 `loadService` 结果
122
87
 
123
- **模板:**
88
+ ### 3.4 注册清理回调
124
89
 
125
90
  ```typescript
126
91
  export const connectionService = defineService(async (shutdown) => {
127
- const primary = await connectPrimary()
128
- shutdown(() => primary.disconnect()) // 注册第 1 个 → 最后执行
129
-
130
- const replica = await connectReplica()
131
- shutdown(() => replica.disconnect()) // 注册第 2 个
92
+ const a = await connectA()
93
+ shutdown(() => a.close())
132
94
 
133
- const cache = await initCache()
134
- shutdown(() => cache.flush()) // 注册第 3 个 → 最先执行
95
+ const b = await connectB()
96
+ shutdown(() => b.close())
135
97
 
136
- return { primary, replica, cache }
98
+ return { a, b }
137
99
  })
138
100
  ```
139
101
 
140
- **规则:**
141
- - 每初始化一个需要清理的资源后,**立即** 调用 `shutdown()` 注册对应的清理函数
142
- - 不要把所有清理逻辑放在一个 shutdown 里,**每个资源对应一个 shutdown 调用**
143
- - 销毁函数按 **逆序(LIFO)** 执行:后注册的先执行,先注册的后执行
144
- - 销毁函数可以是 `async`,容器会依次 `await`
145
- - 同一个函数引用多次传给 `shutdown()` 只会注册一次
146
-
147
- ### 3.5 手动销毁所有服务
102
+ 规则:
148
103
 
149
- 在应用退出或需要优雅关闭时,调用 `container.shutdown()` 手动触发所有已注册的销毁回调:
104
+ - 资源创建后立即注册清理
105
+ - 回调按 LIFO 执行
106
+ - 支持异步回调
150
107
 
151
- **模板:**
108
+ ### 3.5 全局优雅关闭
152
109
 
153
110
  ```typescript
154
111
  import container from '@hile/core'
@@ -159,298 +116,65 @@ process.on('SIGTERM', async () => {
159
116
  })
160
117
  ```
161
118
 
162
- **规则:**
163
- - `shutdown()` 返回 `Promise`,**必须** `await`
164
- - 销毁按 **服务注册逆序** 执行:后注册的服务先销毁
165
- - 每个服务内部的销毁回调同样按 **逆序(LIFO)** 执行
166
- - `shutdown()` 执行完毕后,再次调用不会重复执行(销毁队列已被清空)
119
+ ## 4. 强制规则
167
120
 
168
- ---
169
-
170
- ## 4. 强制规则(生成代码时必须遵守)
121
+ 1. 服务函数必须使用 `async`,避免同步 `throw` 破坏销毁机制。
122
+ 2. 不要手动构造 `ServiceRegisterProps`。
123
+ 3. 不要在工厂函数里动态调用 `defineService` 生成新引用。
124
+ 4. 不要在模块顶层 `await loadService(...)`。
125
+ 5. 每个外部资源都应对应一次 `shutdown` 注册。
171
126
 
172
- | # | 规则 | 原因 |
173
- |---|------|------|
174
- | 1 | 服务函数**必须**使用 `async` 声明 | 同步 `throw` 不会触发销毁机制,只有异步 reject 才会触发 |
175
- | 2 | 服务函数第一个参数**必须**是 `shutdown` | 这是容器注入的销毁注册器,即使不使用也要声明 |
176
- | 3 | `defineService` 的结果**必须**赋给模块级 `export const` | 服务基于函数引用去重,引用必须稳定 |
177
- | 4 | **不要**在 `defineService` 内直接写匿名函数再传给另一个函数 | 每次调用会创建新引用,导致重复注册 |
178
- | 5 | **不要**手动构造 `ServiceRegisterProps` 对象 | 必须通过 `defineService` 或 `container.register` 获取,内部 `flag` 为不可伪造的 Symbol |
179
- | 6 | **不要**缓存 `loadService` 的结果到模块顶层变量 | 服务可能尚未初始化,应在需要时 `await loadService()` |
180
- | 7 | 每个外部资源初始化后**立即**注册 `shutdown` | 确保初始化中途失败时已创建的资源能被正确清理 |
181
- | 8 | 一个文件只定义一个服务 | 保持服务职责单一、依赖清晰 |
182
-
183
- ---
127
+ ## 5. 常见反模式
184
128
 
185
- ## 5. 完整示例:项目结构
186
-
187
- ```
188
- src/
189
- ├── services/
190
- │ ├── config.ts # 配置服务
191
- │ ├── database.ts # 数据库服务(依赖 config)
192
- │ ├── cache.ts # 缓存服务(依赖 config)
193
- │ └── user.ts # 用户服务(依赖 database, cache)
194
- └── main.ts # 入口
195
- ```
196
-
197
- ### services/config.ts
129
+ ### 同步 throw
198
130
 
199
131
  ```typescript
200
- import { defineService } from '@hile/core'
201
-
202
- interface AppConfig {
203
- dbUrl: string
204
- cacheHost: string
205
- }
206
-
207
- export const configService = defineService(async (shutdown) => {
208
- const config: AppConfig = {
209
- dbUrl: process.env.DB_URL ?? 'postgres://localhost:5432/app',
210
- cacheHost: process.env.CACHE_HOST ?? 'localhost',
211
- }
212
- return config
132
+ //
133
+ export const bad = defineService((shutdown) => {
134
+ const r = createSync()
135
+ shutdown(() => r.close())
136
+ throw new Error('boom')
213
137
  })
214
- ```
215
-
216
- ### services/database.ts
217
-
218
- ```typescript
219
- import { defineService, loadService } from '@hile/core'
220
- import { configService } from './config'
221
138
 
222
- export const databaseService = defineService(async (shutdown) => {
223
- const config = await loadService(configService)
224
- const pool = await createPool(config.dbUrl)
225
- shutdown(() => pool.end())
226
- return pool
139
+ //
140
+ export const good = defineService(async (shutdown) => {
141
+ const r = await createAsync()
142
+ shutdown(() => r.close())
143
+ throw new Error('boom')
227
144
  })
228
145
  ```
229
146
 
230
- ### services/cache.ts
147
+ ### 顶层缓存服务实例
231
148
 
232
149
  ```typescript
233
- import { defineService, loadService } from '@hile/core'
234
- import { configService } from './config'
235
-
236
- export const cacheService = defineService(async (shutdown) => {
237
- const config = await loadService(configService)
238
- const client = await createRedisClient(config.cacheHost)
239
- shutdown(() => client.quit())
240
- return client
241
- })
242
- ```
243
-
244
- ### services/user.ts
245
-
246
- ```typescript
247
- import { defineService, loadService } from '@hile/core'
248
- import { databaseService } from './database'
249
- import { cacheService } from './cache'
250
-
251
- interface User {
252
- id: number
253
- name: string
254
- }
255
-
256
- export const userService = defineService(async (shutdown) => {
257
- const db = await loadService(databaseService)
258
- const cache = await loadService(cacheService)
259
-
260
- return {
261
- async getById(id: number): Promise<User> {
262
- const cached = await cache.get(`user:${id}`)
263
- if (cached) return JSON.parse(cached)
264
- const user = await db.query('SELECT * FROM users WHERE id = $1', [id])
265
- await cache.set(`user:${id}`, JSON.stringify(user))
266
- return user
267
- }
268
- }
269
- })
270
- ```
271
-
272
- ### main.ts
273
-
274
- ```typescript
275
- import { loadService } from '@hile/core'
276
- import { userService } from './services/user'
277
-
278
- async function main() {
279
- const users = await loadService(userService)
280
- const user = await users.getById(1)
281
- console.log(user)
282
- }
283
-
284
- main()
285
- ```
286
-
287
- ---
288
-
289
- ## 6. 反模式(生成代码时必须避免)
290
-
291
- ### 6.1 不要使用同步 throw
292
-
293
- ```typescript
294
- // ✗ 错误:同步 throw 不会触发 shutdown 销毁机制
295
- export const badService = defineService((shutdown) => {
296
- const res = createResourceSync()
297
- shutdown(() => res.close())
298
- if (!res.isValid()) throw new Error('invalid')
299
- return res
300
- })
301
-
302
- // ✓ 正确:使用 async 函数
303
- export const goodService = defineService(async (shutdown) => {
304
- const res = await createResource()
305
- shutdown(() => res.close())
306
- if (!res.isValid()) throw new Error('invalid')
307
- return res
308
- })
309
- ```
310
-
311
- ### 6.2 不要在模块顶层缓存服务结果
312
-
313
- ```typescript
314
- // ✗ 错误:模块加载时服务可能尚未就绪
150
+ //
315
151
  const db = await loadService(databaseService)
316
- export function query(sql: string) {
317
- return db.query(sql)
318
- }
319
152
 
320
- // ✓ 正确:每次在函数内部加载
153
+ // ✓
321
154
  export async function query(sql: string) {
322
155
  const db = await loadService(databaseService)
323
156
  return db.query(sql)
324
157
  }
325
158
  ```
326
159
 
327
- ### 6.3 不要内联定义服务函数
328
-
329
- ```typescript
330
- // ✗ 错误:每次调用 getService() 都创建新函数引用,无法去重
331
- function getService() {
332
- return defineService(async (shutdown) => { ... })
333
- }
334
-
335
- // ✓ 正确:模块级常量
336
- export const myService = defineService(async (shutdown) => { ... })
337
- ```
338
-
339
- ### 6.4 不要延迟注册 shutdown
340
-
341
- ```typescript
342
- // ✗ 错误:如果 doSomething 抛错,resourceA 不会被清理
343
- export const badService = defineService(async (shutdown) => {
344
- const a = await createResourceA()
345
- const b = await doSomething(a)
346
- shutdown(() => a.close()) // 太晚了!
347
- shutdown(() => b.close())
348
- return b
349
- })
350
-
351
- // ✓ 正确:创建后立即注册
352
- export const goodService = defineService(async (shutdown) => {
353
- const a = await createResourceA()
354
- shutdown(() => a.close()) // 立即注册
355
- const b = await doSomething(a)
356
- shutdown(() => b.close())
357
- return b
358
- })
359
- ```
360
-
361
- ---
362
-
363
- ## 7. API 速查
364
-
365
- ### 便捷函数(操作默认容器)
366
-
367
- | 函数 | 签名 | 说明 |
368
- |------|------|------|
369
- | `defineService` | `<R>(fn: ServiceFunction<R>) => ServiceRegisterProps<R>` | 注册服务,返回注册信息 |
370
- | `loadService` | `<R>(props: ServiceRegisterProps<R>) => Promise<R>` | 加载服务,返回服务实例 |
371
- | `isService` | `<R>(props: ServiceRegisterProps<R>) => boolean` | 判断对象是否为合法的服务注册信息(通过内部 Symbol 校验) |
372
-
373
- ### Container 类(用于创建隔离容器)
374
-
375
- | 方法 | 签名 | 说明 |
376
- |------|------|------|
377
- | `register` | `<R>(fn: ServiceFunction<R>) => ServiceRegisterProps<R>` | 注册服务。同一函数引用只注册一次 |
378
- | `resolve` | `<R>(props: ServiceRegisterProps<R>) => Promise<R>` | 解析服务(状态机见下方) |
379
- | `hasService` | `<R>(fn: ServiceFunction<R>) => boolean` | 检查服务是否已注册 |
380
- | `hasMeta` | `(id: number) => boolean` | 检查服务是否已运行过 |
381
- | `getIdByService` | `<R>(fn: ServiceFunction<R>) => number \| undefined` | 根据函数引用查 ID |
382
- | `getMetaById` | `(id: number) => Paddings \| undefined` | 根据 ID 查运行时元数据 |
383
- | `shutdown` | `() => Promise<void>` | 手动销毁所有服务,按服务注册逆序执行所有销毁回调 |
384
-
385
- ### resolve 状态机
386
-
387
- ```
388
- resolve(props)
389
-
390
- ├─ paddings 中无记录 → 首次运行
391
- │ → run(id, fn, callback)
392
- │ → 创建 Paddings { status: 0 }
393
- │ ├─ fn 成功 → status = 1, value = 返回值, 通知 queue 所有等待者
394
- │ └─ fn 失败 → status = -1, error = 错误
395
- │ → 先逆序执行 shutdown 回调
396
- │ → 再通知 queue 所有等待者
397
-
398
- ├─ status = 0 (运行中) → 加入 queue 等待
399
- ├─ status = 1 (已成功) → 直接 resolve(缓存值)
400
- └─ status = -1 (已失败) → 直接 reject(缓存错误)
401
- ```
402
-
403
- ---
404
-
405
- ## 8. 内部机制(供理解,不供直接调用)
160
+ ## 6. API 速查
406
161
 
407
- ### 数据结构
408
-
409
- | 字段 | 类型 | 说明 |
410
- |------|------|------|
411
- | `packages` | `Map<Function, number>` | 函数引用 → 服务 ID |
412
- | `paddings` | `Map<number, Paddings>` | 服务 ID → 运行时状态 |
413
- | `shutdownFunctions` | `Map<number, ServiceCutDownFunction[]>` | 服务 ID → 销毁回调数组 |
414
- | `shutdownQueues` | `number[]` | 注册了销毁回调的服务 ID 队列(有序) |
415
-
416
- ### Paddings 结构
417
-
418
- | 字段 | 类型 | 说明 |
419
- |------|------|------|
420
- | `status` | `-1 \| 0 \| 1` | -1 失败 / 0 运行中 / 1 成功 |
421
- | `value` | `R` | 成功时的返回值 |
422
- | `error` | `any` | 失败时的错误 |
423
- | `queue` | `Set<{ resolve, reject }>` | 等待中的 Promise 回调 |
424
-
425
- ### 销毁执行顺序
426
-
427
- - **单个服务内**:销毁回调按注册的逆序(LIFO)依次 `await` 执行
428
- - **全局销毁**:按服务注册顺序的逆序依次销毁
429
- - **触发时机**:
430
- - 服务函数异步失败(reject)时自动触发该服务的销毁回调
431
- - 手动调用 `container.shutdown()` 时触发所有已注册服务的销毁回调(按服务注册逆序)
432
-
433
- ### 服务标识机制(sericeFlag)
434
-
435
- 容器内部使用 `const sericeFlag = Symbol('service')` 作为服务注册信息的标识。`register` 返回的对象会携带 `flag: sericeFlag` 字段。`isService()` 通过检查 `flag === sericeFlag` 以及 `id` 和 `fn` 的类型来判断一个对象是否为合法的服务注册信息。由于 Symbol 不可伪造,外部无法手动构造通过 `isService` 校验的对象。
436
-
437
- ### 函数去重机制
438
-
439
- 容器通过 `===` 比较函数引用。两个函数即使代码完全相同,只要引用不同就会被视为不同服务。因此服务必须定义为模块级常量。
440
-
441
- ---
442
-
443
- ## 9. 开发
444
-
445
- ```bash
446
- pnpm install
447
- pnpm build # 编译
448
- pnpm dev # 监听模式
449
- pnpm test # 运行测试
450
- ```
162
+ ### 便捷函数
451
163
 
452
- **技术栈:** TypeScript, Vitest
164
+ | 函数 | 说明 |
165
+ |---|---|
166
+ | `defineService(fn)` | 注册服务到默认容器 |
167
+ | `loadService(props)` | 加载服务实例 |
168
+ | `isService(props)` | 判断是否为合法服务注册对象 |
453
169
 
454
- ## License
170
+ ### Container
455
171
 
456
- MIT
172
+ | 方法 | 说明 |
173
+ |---|---|
174
+ | `register(fn)` | 注册服务 |
175
+ | `resolve(props)` | 解析服务 |
176
+ | `hasService(fn)` | 检查函数是否已注册 |
177
+ | `hasMeta(id)` | 检查运行时元数据 |
178
+ | `getIdByService(fn)` | 通过函数获取 ID |
179
+ | `getMetaById(id)` | 通过 ID 获取元数据 |
180
+ | `shutdown()` | 销毁所有服务 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hile/core",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "Hile core - lightweight async service container",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,5 +23,5 @@
23
23
  "@types/node": "^25.3.1",
24
24
  "vitest": "^4.0.18"
25
25
  },
26
- "gitHead": "a6eab4e7803f0df7d4bca9a0bdeca76692dbc883"
26
+ "gitHead": "6672fc4cdc551c4265912cc85ee2e96fc44bc4c9"
27
27
  }