hile-core
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHile
Hile
Hile 是一个轻量级异步服务容器。本文档是面向 AI 编码模型和人类开发者的 代码生成规范,阅读后应能正确地使用本库编写符合架构规则的代码。
Hile is a lightweight asynchronous service container. This document is a code generation specification for AI coding models and human developers. After reading it, you should be able to correctly use this library to write code that complies with architectural rules.
1. 架构总览
1. Architecture Overview
Hile 的核心是 (服务容器)。所有服务都必须经过 定义 → 加载 两个步骤才能使用。容器保证:
Container- 每个服务函数只执行一次(单例)
- 并发请求同一服务时自动合并(不重复执行)
- 服务启动失败时自动执行已注册的清理回调(销毁机制)
模块默认导出了一个全局容器实例,并提供 / 两个便捷函数。
defineServiceloadServiceThe core of Hile is the (service container). All services must go through two steps: Define → Load before they can be used. The container guarantees:
Container- Each service function is executed only once (Singleton)
- Concurrent requests for the same service are automatically merged (No duplicate execution)
- When a service fails to start, registered cleanup callbacks are automatically executed (Destruction mechanism)
The module exports a global container instance by default, and provides two convenient functions: / .
defineServiceloadService2. 类型签名
2. Type Signatures
生成代码时,必须严格遵循以下类型:
typescript
// 销毁回调:无参数,可返回 Promise
type ServiceCutDownFunction = () => unknown | Promise<unknown>;
// 销毁注册器:在服务函数内部调用,将清理回调注册到容器
type ServiceCutDownHandler = (fn: ServiceCutDownFunction) => void;
// 服务函数:第一个参数固定为销毁注册器,返回值为同步值或 Promise
type ServiceFunction<R> = (shutdown: ServiceCutDownHandler) => R | Promise<R>;
// 内部服务标识(Symbol,不可外部构造)
const sericeFlag = Symbol('service');
// 服务注册信息:由 defineService/register 返回,作为 loadService/resolve 的入参
interface ServiceRegisterProps<R> {
id: number;
fn: ServiceFunction<R>;
flag: typeof sericeFlag;
}When generating code, you must strictly follow the following types:
typescript
// 销毁回调:无参数,可返回 Promise
type ServiceCutDownFunction = () => unknown | Promise<unknown>;
// 销毁注册器:在服务函数内部调用,将清理回调注册到容器
type ServiceCutDownHandler = (fn: ServiceCutDownFunction) => void;
// 服务函数:第一个参数固定为销毁注册器,返回值为同步值或 Promise
type ServiceFunction<R> = (shutdown: ServiceCutDownHandler) => R | Promise<R>;
// 内部服务标识(Symbol,不可外部构造)
const sericeFlag = Symbol('service');
// 服务注册信息:由 defineService/register 返回,作为 loadService/resolve 的入参
interface ServiceRegisterProps<R> {
id: number;
fn: ServiceFunction<R>;
flag: typeof sericeFlag;
}3. 代码生成模板
3. Code Generation Templates
3.1 定义服务(必须遵循的模式)
3.1 Define Service (Mandatory Pattern)
模板:
typescript
import { defineService } from '@hile/core'
export const xxxService = defineService(async (shutdown) => {
// 1. 初始化资源
const resource = await createResource()
// 2. 注册销毁回调(每创建一个资源就注册一个对应的清理)
shutdown(() => resource.close())
// 3. 返回服务实例
return resource
})规则:
- 服务函数的第一个参数 必须 命名为 ,类型为
shutdownServiceCutDownHandler - 服务函数 应当 使用 声明(确保销毁机制在失败时正确触发)
async - 的返回值 必须 赋值给一个模块级常量并
defineServiceexport - 常量命名 必须 以 结尾(如
Service、databaseService)cacheService - 每个服务定义 应当 放在独立的文件中
Template:
typescript
import { defineService } from '@hile/core'
export const xxxService = defineService(async (shutdown) => {
// 1. 初始化资源
const resource = await createResource()
// 2. 注册销毁回调(每创建一个资源就注册一个对应的清理)
shutdown(() => resource.close())
// 3. 返回服务实例
return resource
})Rules:
- The first parameter of the service function must be named , with the type
shutdownServiceCutDownHandler - The service function should be declared with (to ensure the destruction mechanism is triggered correctly on failure)
async - The return value of must be assigned to a module-level constant and
defineServiceedexport - The constant name must end with (e.g.,
Service,databaseService)cacheService - Each service definition should be placed in an independent file
3.2 加载服务
3.2 Load Service
模板:
typescript
import { loadService } from '@hile/core'
import { databaseService } from './services/database'
const db = await loadService(databaseService)规则:
- 永远 使用 获取服务实例,不要 直接调用服务函数
loadService() - 返回
loadService,必须Promiseawait - 可以在任何地方多次调用 ,容器保证只执行一次
loadService(同一个服务)
Template:
typescript
import { loadService } from '@hile/core'
import { databaseService } from './services/database'
const db = await loadService(databaseService)Rules:
- Always use to get service instances, never call the service function directly
loadService() - returns a
loadService, must usePromiseawait - You can call multiple times anywhere, the container guarantees it will only execute once
loadService(the same service)
3.3 服务间依赖
3.3 Inter-service Dependencies
当一个服务依赖另一个服务时,在服务函数内部通过 加载依赖:
loadServicetypescript
import { defineService, loadService } from '@hile/core'
import { databaseService } from './database'
import { configService } from './config'
export const userService = defineService(async (shutdown) => {
// 加载依赖的服务(若已完成则直接返回缓存,否则等待)
const config = await loadService(configService)
const db = await loadService(databaseService)
const repo = new UserRepository(db, config)
shutdown(() => repo.dispose())
return repo
})规则:
- 依赖的服务通过 引入其
import,然后在函数体内ServiceRegisterProps加载loadService - 不要 将 的结果缓存到模块作用域变量中
loadService - 不要 在服务函数外部调用 来获取另一个服务并传入——应在服务函数内部加载
loadService
When one service depends on another, load the dependency via inside the service function:
loadServicetypescript
import { defineService, loadService } from '@hile/core'
import { databaseService } from './database'
import { configService } from './config'
export const userService = defineService(async (shutdown) => {
// 加载依赖的服务(若已完成则直接返回缓存,否则等待)
const config = await loadService(configService)
const db = await loadService(databaseService)
const repo = new UserRepository(db, config)
shutdown(() => repo.dispose())
return repo
})Rules:
- Import the of the dependent service via
ServiceRegisterProps, then load it withimportinside the function bodyloadService - Do not cache the result of in a module-scoped variable
loadService - Do not call outside the service function to get another service and pass it in — load it inside the service function instead
loadService
3.4 注册销毁回调
3.4 Register Shutdown Callback
模板:
typescript
export const connectionService = defineService(async (shutdown) => {
const primary = await connectPrimary()
shutdown(() => primary.disconnect()) // 注册第 1 个 → 最后执行
const replica = await connectReplica()
shutdown(() => replica.disconnect()) // 注册第 2 个
const cache = await initCache()
shutdown(() => cache.flush()) // 注册第 3 个 → 最先执行
return { primary, replica, cache }
})规则:
- 每初始化一个需要清理的资源后,立即 调用 注册对应的清理函数
shutdown() - 不要把所有清理逻辑放在一个 shutdown 里,每个资源对应一个 shutdown 调用
- 销毁函数按 逆序(LIFO) 执行:后注册的先执行,先注册的后执行
- 销毁函数可以是 ,容器会依次
asyncawait - 同一个函数引用多次传给 只会注册一次
shutdown()
Template:
typescript
export const connectionService = defineService(async (shutdown) => {
const primary = await connectPrimary()
shutdown(() => primary.disconnect()) // 注册第 1 个 → 最后执行
const replica = await connectReplica()
shutdown(() => replica.disconnect()) // 注册第 2 个
const cache = await initCache()
shutdown(() => cache.flush()) // 注册第 3 个 → 最先执行
return { primary, replica, cache }
})Rules:
- Immediately after initializing a resource that needs cleanup, call to register the corresponding cleanup function
shutdown() - Do not put all cleanup logic in a single shutdown call, each resource corresponds to one shutdown call
- Destruction functions are executed in reverse order (LIFO): later registered functions execute first, earlier registered ones execute last
- Destruction functions can be , the container will
asyncthem sequentiallyawait - Passing the same function reference to multiple times will only register it once
shutdown()
3.5 手动销毁所有服务
3.5 Manually Destroy All Services
在应用退出或需要优雅关闭时,调用 手动触发所有已注册的销毁回调:
container.shutdown()模板:
typescript
import container from '@hile/core'
process.on('SIGTERM', async () => {
await container.shutdown()
process.exit(0)
})规则:
- 返回
shutdown(),必须Promiseawait - 销毁按 服务注册逆序 执行:后注册的服务先销毁
- 每个服务内部的销毁回调同样按 逆序(LIFO) 执行
- 执行完毕后,再次调用不会重复执行(销毁队列已被清空)
shutdown()
When the application exits or needs to shut down gracefully, call to manually trigger all registered destruction callbacks:
container.shutdown()Template:
typescript
import container from '@hile/core'
process.on('SIGTERM', async () => {
await container.shutdown()
process.exit(0)
})Rules:
- returns a
shutdown(), must usePromiseawait - Destruction is executed in reverse order of service registration: later registered services are destroyed first
- The destruction callbacks inside each service are also executed in reverse order (LIFO)
- After completes, calling it again will not execute repeatedly (the destruction queue has been cleared)
shutdown()
4. 强制规则(生成代码时必须遵守)
4. Mandatory Rules (Must Follow When Generating Code)
| # | 规则 | 原因 |
|---|---|---|
| 1 | 服务函数必须使用 | 同步 |
| 2 | 服务函数第一个参数必须是 | 这是容器注入的销毁注册器,即使不使用也要声明 |
| 3 | | 服务基于函数引用去重,引用必须稳定 |
| 4 | 不要在 | 每次调用会创建新引用,导致重复注册 |
| 5 | 不要手动构造 | 必须通过 |
| 6 | 不要缓存 | 服务可能尚未初始化,应在需要时 |
| 7 | 每个外部资源初始化后立即注册 | 确保初始化中途失败时已创建的资源能被正确清理 |
| 8 | 一个文件只定义一个服务 | 保持服务职责单一、依赖清晰 |
| # | Rule | Reason |
|---|---|---|
| 1 | Service functions must be declared with | Synchronous |
| 2 | The first parameter of the service function must be | This is the destruction registrar injected by the container, declare it even if not used |
| 3 | The result of | Services are deduplicated based on function references, so references must be stable |
| 4 | Do not directly write anonymous functions inside | A new reference is created each call, leading to duplicate registration |
| 5 | Do not manually construct | Must obtain via |
| 6 | Do not cache the result of | The service may not be initialized yet, should |
| 7 | Immediately register | Ensure that resources created during initialization can be cleaned up correctly if initialization fails midway |
| 8 | Define only one service per file | Keep service responsibilities single and dependencies clear |
5. 完整示例:项目结构
5. Complete Example: Project Structure
src/
├── services/
│ ├── config.ts # 配置服务
│ ├── database.ts # 数据库服务(依赖 config)
│ ├── cache.ts # 缓存服务(依赖 config)
│ └── user.ts # 用户服务(依赖 database, cache)
└── main.ts # 入口src/
├── services/
│ ├── config.ts # 配置服务
│ ├── database.ts # 数据库服务(依赖 config)
│ ├── cache.ts # 缓存服务(依赖 config)
│ └── user.ts # 用户服务(依赖 database, cache)
└── main.ts # 入口services/config.ts
services/config.ts
typescript
import { defineService } from '@hile/core'
interface AppConfig {
dbUrl: string
cacheHost: string
}
export const configService = defineService(async (shutdown) => {
const config: AppConfig = {
dbUrl: process.env.DB_URL ?? 'postgres://localhost:5432/app',
cacheHost: process.env.CACHE_HOST ?? 'localhost',
}
return config
})typescript
import { defineService } from '@hile/core'
interface AppConfig {
dbUrl: string
cacheHost: string
}
export const configService = defineService(async (shutdown) => {
const config: AppConfig = {
dbUrl: process.env.DB_URL ?? 'postgres://localhost:5432/app',
cacheHost: process.env.CACHE_HOST ?? 'localhost',
}
return config
})services/database.ts
services/database.ts
typescript
import { defineService, loadService } from '@hile/core'
import { configService } from './config'
export const databaseService = defineService(async (shutdown) => {
const config = await loadService(configService)
const pool = await createPool(config.dbUrl)
shutdown(() => pool.end())
return pool
})typescript
import { defineService, loadService } from '@hile/core'
import { configService } from './config'
export const databaseService = defineService(async (shutdown) => {
const config = await loadService(configService)
const pool = await createPool(config.dbUrl)
shutdown(() => pool.end())
return pool
})services/cache.ts
services/cache.ts
typescript
import { defineService, loadService } from '@hile/core'
import { configService } from './config'
export const cacheService = defineService(async (shutdown) => {
const config = await loadService(configService)
const client = await createRedisClient(config.cacheHost)
shutdown(() => client.quit())
return client
})typescript
import { defineService, loadService } from '@hile/core'
import { configService } from './config'
export const cacheService = defineService(async (shutdown) => {
const config = await loadService(configService)
const client = await createRedisClient(config.cacheHost)
shutdown(() => client.quit())
return client
})services/user.ts
services/user.ts
typescript
import { defineService, loadService } from '@hile/core'
import { databaseService } from './database'
import { cacheService } from './cache'
interface User {
id: number
name: string
}
export const userService = defineService(async (shutdown) => {
const db = await loadService(databaseService)
const cache = await loadService(cacheService)
return {
async getById(id: number): Promise<User> {
const cached = await cache.get(`user:${id}`)
if (cached) return JSON.parse(cached)
const user = await db.query('SELECT * FROM users WHERE id = $1', [id])
await cache.set(`user:${id}`, JSON.stringify(user))
return user
}
}
})typescript
import { defineService, loadService } from '@hile/core'
import { databaseService } from './database'
import { cacheService } from './cache'
interface User {
id: number
name: string
}
export const userService = defineService(async (shutdown) => {
const db = await loadService(databaseService)
const cache = await loadService(cacheService)
return {
async getById(id: number): Promise<User> {
const cached = await cache.get(`user:${id}`)
if (cached) return JSON.parse(cached)
const user = await db.query('SELECT * FROM users WHERE id = $1', [id])
await cache.set(`user:${id}`, JSON.stringify(user))
return user
}
}
})main.ts
main.ts
typescript
import { loadService } from '@hile/core'
import { userService } from './services/user'
async function main() {
const users = await loadService(userService)
const user = await users.getById(1)
console.log(user)
}
main()typescript
import { loadService } from '@hile/core'
import { userService } from './services/user'
async function main() {
const users = await loadService(userService)
const user = await users.getById(1)
console.log(user)
}
main()6. 反模式(生成代码时必须避免)
6. Anti-patterns (Must Avoid When Generating Code)
6.1 不要使用同步 throw
6.1 Do Not Use Synchronous Throw
typescript
// ✗ 错误:同步 throw 不会触发 shutdown 销毁机制
export const badService = defineService((shutdown) => {
const res = createResourceSync()
shutdown(() => res.close())
if (!res.isValid()) throw new Error('invalid')
return res
})
// ✓ 正确:使用 async 函数
export const goodService = defineService(async (shutdown) => {
const res = await createResource()
shutdown(() => res.close())
if (!res.isValid()) throw new Error('invalid')
return res
})typescript
// ✗ 错误:同步 throw 不会触发 shutdown 销毁机制
export const badService = defineService((shutdown) => {
const res = createResourceSync()
shutdown(() => res.close())
if (!res.isValid()) throw new Error('invalid')
return res
})
// ✓ 正确:使用 async 函数
export const goodService = defineService(async (shutdown) => {
const res = await createResource()
shutdown(() => res.close())
if (!res.isValid()) throw new Error('invalid')
return res
})6.2 不要在模块顶层缓存服务结果
6.2 Do Not Cache Service Results at Module Top Level
typescript
// ✗ 错误:模块加载时服务可能尚未就绪
const db = await loadService(databaseService)
export function query(sql: string) {
return db.query(sql)
}
// ✓ 正确:每次在函数内部加载
export async function query(sql: string) {
const db = await loadService(databaseService)
return db.query(sql)
}typescript
// ✗ 错误:模块加载时服务可能尚未就绪
const db = await loadService(databaseService)
export function query(sql: string) {
return db.query(sql)
}
// ✓ 正确:每次在函数内部加载
export async function query(sql: string) {
const db = await loadService(databaseService)
return db.query(sql)
}6.3 不要内联定义服务函数
6.3 Do Not Inline Define Service Functions
typescript
// ✗ 错误:每次调用 getService() 都创建新函数引用,无法去重
function getService() {
return defineService(async (shutdown) => { ... })
}
// ✓ 正确:模块级常量
export const myService = defineService(async (shutdown) => { ... })typescript
// ✗ 错误:每次调用 getService() 都创建新函数引用,无法去重
function getService() {
return defineService(async (shutdown) => { ... })
}
// ✓ 正确:模块级常量
export const myService = defineService(async (shutdown) => { ... })6.4 不要延迟注册 shutdown
6.4 Do Not Delay Registering Shutdown
typescript
// ✗ 错误:如果 doSomething 抛错,resourceA 不会被清理
export const badService = defineService(async (shutdown) => {
const a = await createResourceA()
const b = await doSomething(a)
shutdown(() => a.close()) // 太晚了!
shutdown(() => b.close())
return b
})
// ✓ 正确:创建后立即注册
export const goodService = defineService(async (shutdown) => {
const a = await createResourceA()
shutdown(() => a.close()) // 立即注册
const b = await doSomething(a)
shutdown(() => b.close())
return b
})typescript
// ✗ 错误:如果 doSomething 抛错,resourceA 不会被清理
export const badService = defineService(async (shutdown) => {
const a = await createResourceA()
const b = await doSomething(a)
shutdown(() => a.close()) // 太晚了!
shutdown(() => b.close())
return b
})
// ✓ 正确:创建后立即注册
export const goodService = defineService(async (shutdown) => {
const a = await createResourceA()
shutdown(() => a.close()) // 立即注册
const b = await doSomething(a)
shutdown(() => b.close())
return b
})7. API 速查
7. API Quick Reference
便捷函数(操作默认容器)
Convenience Functions (Operate on Default Container)
| 函数 | 签名 | 说明 |
|---|---|---|
| | 注册服务,返回注册信息 |
| | 加载服务,返回服务实例 |
| | 判断对象是否为合法的服务注册信息(通过内部 Symbol 校验) |
| Function | Signature | Description |
|---|---|---|
| | Register a service, return registration information |
| | Load a service, return service instance |
| | Determine if an object is valid service registration information (verified via internal Symbol) |
Container 类(用于创建隔离容器)
Container Class (For Creating Isolated Containers)
| 方法 | 签名 | 说明 |
|---|---|---|
| | 注册服务。同一函数引用只注册一次 |
| | 解析服务(状态机见下方) |
| | 检查服务是否已注册 |
| | 检查服务是否已运行过 |
| | 根据函数引用查 ID |
| | 根据 ID 查运行时元数据 |
| | 手动销毁所有服务,按服务注册逆序执行所有销毁回调 |
| Method | Signature | Description |
|---|---|---|
| | Register a service. The same function reference is only registered once |
| | Resolve a service (see state machine below) |
| | Check if a service has been registered |
| | Check if a service has been run |
| | Query service ID by function reference |
| | Query runtime metadata by service ID |
| | Manually destroy all services, execute all destruction callbacks in reverse order of service registration |
resolve 状态机
resolve State Machine
resolve(props)
│
├─ paddings 中无记录 → 首次运行
│ → run(id, fn, callback)
│ → 创建 Paddings { status: 0 }
│ ├─ fn 成功 → status = 1, value = 返回值, 通知 queue 所有等待者
│ └─ fn 失败 → status = -1, error = 错误
│ → 先逆序执行 shutdown 回调
│ → 再通知 queue 所有等待者
│
├─ status = 0 (运行中) → 加入 queue 等待
├─ status = 1 (已成功) → 直接 resolve(缓存值)
└─ status = -1 (已失败) → 直接 reject(缓存错误)resolve(props)
│
├─ No record in paddings → First run
│ → run(id, fn, callback)
│ → Create Paddings { status: 0 }
│ ├─ fn succeeds → status = 1, value = return value, notify all waiters in queue
│ └─ fn fails → status = -1, error = error
│ → First execute shutdown callbacks in reverse order
│ → Then notify all waiters in queue
│
├─ status = 0 (Running) → Join queue and wait
├─ status = 1 (Succeeded) → Directly resolve(cached value)
└─ status = -1 (Failed) → Directly reject(cached error)8. 内部机制(供理解,不供直接调用)
8. Internal Mechanisms (For Understanding, Not For Direct Calling)
数据结构
Data Structures
| 字段 | 类型 | 说明 |
|---|---|---|
| | 函数引用 → 服务 ID |
| | 服务 ID → 运行时状态 |
| | 服务 ID → 销毁回调数组 |
| | 注册了销毁回调的服务 ID 队列(有序) |
| Field | Type | Description |
|---|---|---|
| | Function reference → Service ID |
| | Service ID → Runtime status |
| | Service ID → Destruction callback array |
| | Queue of service IDs that have registered destruction callbacks (ordered) |
Paddings 结构
Paddings Structure
| 字段 | 类型 | 说明 |
|---|---|---|
| | -1 失败 / 0 运行中 / 1 成功 |
| | 成功时的返回值 |
| | 失败时的错误 |
| | 等待中的 Promise 回调 |
| Field | Type | Description |
|---|---|---|
| | -1 Failed / 0 Running / 1 Succeeded |
| | Return value on success |
| | Error on failure |
| | Promise callbacks waiting |
销毁执行顺序
Destruction Execution Order
- 单个服务内:销毁回调按注册的逆序(LIFO)依次 执行
await - 全局销毁:按服务注册顺序的逆序依次销毁
- 触发时机:
- 服务函数异步失败(reject)时自动触发该服务的销毁回调
- 手动调用 时触发所有已注册服务的销毁回调(按服务注册逆序)
container.shutdown()
- Inside a single service: Destruction callbacks are executed sequentially in reverse registration order (LIFO) with
await - Global destruction: Destroy services in reverse order of their registration
- Trigger timing:
- Automatically trigger the service's destruction callbacks when the service function fails asynchronously (rejects)
- Trigger destruction callbacks of all registered services when is called manually (in reverse order of service registration)
container.shutdown()
服务标识机制(sericeFlag)
Service Identification Mechanism (sericeFlag)
容器内部使用 作为服务注册信息的标识。 返回的对象会携带 字段。 通过检查 以及 和 的类型来判断一个对象是否为合法的服务注册信息。由于 Symbol 不可伪造,外部无法手动构造通过 校验的对象。
const sericeFlag = Symbol('service')registerflag: sericeFlagisService()flag === sericeFlagidfnisServiceThe container internally uses as the identifier for service registration information. The object returned by carries the field. determines if an object is valid service registration information by checking and the types of and . Since Symbols are unforgeable, external parties cannot manually construct an object that passes the check.
const sericeFlag = Symbol('service')registerflag: sericeFlagisService()flag === sericeFlagidfnisService函数去重机制
Function Deduplication Mechanism
容器通过 比较函数引用。两个函数即使代码完全相同,只要引用不同就会被视为不同服务。因此服务必须定义为模块级常量。
===The container compares function references via . Two functions are considered different services even if their code is identical, as long as their references are different. Therefore, services must be defined as module-level constants.
===9. 开发
9. Development
bash
pnpm install
pnpm build # 编译
pnpm dev # 监听模式
pnpm test # 运行测试技术栈: TypeScript, Vitest
bash
pnpm install
pnpm build # 编译
pnpm dev # 监听模式
pnpm test # 运行测试Tech Stack: TypeScript, Vitest
License
License
MIT
MIT