Loading...
Loading...
Code generation and usage guidelines for the @hile/core asynchronous service container. Apply this when defining or loading Hile services, configuring lifecycle shutdown logic, or when users inquire about @hile/core, defineService, loadService, or service container patterns.
npx skill4agent add cevio/hile hile-coreContainerdefineServiceloadService// 销毁回调:无参数,可返回 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;
}import { defineService } from '@hile/core'
export const xxxService = defineService(async (shutdown) => {
// 1. 初始化资源
const resource = await createResource()
// 2. 注册销毁回调(每创建一个资源就注册一个对应的清理)
shutdown(() => resource.close())
// 3. 返回服务实例
return resource
})shutdownServiceCutDownHandlerasyncdefineServiceexportServicedatabaseServicecacheServiceimport { loadService } from '@hile/core'
import { databaseService } from './services/database'
const db = await loadService(databaseService)loadService()loadServicePromiseawaitloadService(the same service)loadServiceimport { 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
})ServiceRegisterPropsimportloadServiceloadServiceloadServiceexport 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()asyncawaitshutdown()container.shutdown()import container from '@hile/core'
process.on('SIGTERM', async () => {
await container.shutdown()
process.exit(0)
})shutdown()Promiseawaitshutdown()| # | 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 |
src/
├── services/
│ ├── config.ts # 配置服务
│ ├── database.ts # 数据库服务(依赖 config)
│ ├── cache.ts # 缓存服务(依赖 config)
│ └── user.ts # 用户服务(依赖 database, cache)
└── main.ts # 入口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
})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
})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
})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
}
}
})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()// ✗ 错误:同步 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
})// ✗ 错误:模块加载时服务可能尚未就绪
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)
}// ✗ 错误:每次调用 getService() 都创建新函数引用,无法去重
function getService() {
return defineService(async (shutdown) => { ... })
}
// ✓ 正确:模块级常量
export const myService = defineService(async (shutdown) => { ... })// ✗ 错误:如果 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
})| 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) |
| 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(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)| 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) |
| Field | Type | Description |
|---|---|---|
| | -1 Failed / 0 Running / 1 Succeeded |
| | Return value on success |
| | Error on failure |
| | Promise callbacks waiting |
awaitcontainer.shutdown()const sericeFlag = Symbol('service')registerflag: sericeFlagisService()flag === sericeFlagidfnisService===pnpm install
pnpm build # 编译
pnpm dev # 监听模式
pnpm test # 运行测试