@h-ai/cli 0.1.0-alpha.54 → 0.1.0-alpha.55

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 (64) hide show
  1. package/README.md +4 -0
  2. package/package.json +7 -7
  3. package/templates/base/package.json.hbs +1 -1
  4. package/templates/skills/AGENTS.md +8 -22
  5. package/templates/skills/CLAUDE.md +3 -16
  6. package/templates/skills/bridges/admin/AGENTS.md +3 -16
  7. package/templates/skills/bridges/admin/CLAUDE.md +3 -11
  8. package/templates/skills/bridges/admin/copilot-instructions.md +2 -39
  9. package/templates/skills/bridges/api/AGENTS.md +5 -18
  10. package/templates/skills/bridges/api/CLAUDE.md +3 -11
  11. package/templates/skills/bridges/api/copilot-instructions.md +3 -38
  12. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +24 -59
  13. package/templates/skills/bridges/fullstack/CLAUDE.md.hbs +1 -15
  14. package/templates/skills/bridges/fullstack/copilot-instructions.md.hbs +5 -45
  15. package/templates/skills/bridges/h5/AGENTS.md +4 -17
  16. package/templates/skills/bridges/h5/CLAUDE.md +3 -11
  17. package/templates/skills/bridges/h5/copilot-instructions.md +2 -37
  18. package/templates/skills/bridges/mobile-app/AGENTS.md +5 -18
  19. package/templates/skills/bridges/mobile-app/CLAUDE.md +3 -11
  20. package/templates/skills/bridges/mobile-app/copilot-instructions.md +2 -38
  21. package/templates/skills/bridges/website/AGENTS.md +4 -17
  22. package/templates/skills/bridges/website/CLAUDE.md +3 -11
  23. package/templates/skills/bridges/website/copilot-instructions.md +2 -37
  24. package/templates/skills/copilot-instructions.md +2 -36
  25. package/templates/skills/hai-ai/SKILL.md +35 -389
  26. package/templates/skills/hai-ai/guide.md +383 -0
  27. package/templates/skills/hai-ai/reference.md +1 -1
  28. package/templates/skills/hai-api-client/SKILL.md +6 -6
  29. package/templates/skills/hai-api-contract/SKILL.md +10 -25
  30. package/templates/skills/hai-app-create/SKILL.md +29 -388
  31. package/templates/skills/hai-app-create/reference.md +350 -0
  32. package/templates/skills/hai-app-review/SKILL.md +9 -23
  33. package/templates/skills/hai-app-tests/SKILL.md +29 -494
  34. package/templates/skills/hai-app-tests/reference.md +442 -0
  35. package/templates/skills/hai-audit/SKILL.md +6 -6
  36. package/templates/skills/hai-build/SKILL.md +19 -338
  37. package/templates/skills/hai-build/reference.md +326 -0
  38. package/templates/skills/hai-cache/SKILL.md +27 -32
  39. package/templates/skills/hai-capacitor/SKILL.md +19 -366
  40. package/templates/skills/hai-capacitor/reference.md +352 -0
  41. package/templates/skills/hai-ci/SKILL.md +6 -21
  42. package/templates/skills/hai-core/SKILL.md +25 -566
  43. package/templates/skills/hai-core/reference.md +533 -0
  44. package/templates/skills/hai-crypto/SKILL.md +6 -20
  45. package/templates/skills/hai-datapipe/SKILL.md +9 -23
  46. package/templates/skills/hai-deploy/SKILL.md +6 -24
  47. package/templates/skills/hai-framework-sync/SKILL.md +8 -20
  48. package/templates/skills/hai-iam/SKILL.md +27 -371
  49. package/templates/skills/hai-iam/reference.md +343 -0
  50. package/templates/skills/hai-kit/SKILL.md +6 -6
  51. package/templates/skills/hai-payment/SKILL.md +6 -26
  52. package/templates/skills/hai-pr-review/SKILL.md +6 -22
  53. package/templates/skills/hai-reach/SKILL.md +19 -317
  54. package/templates/skills/hai-reach/reference.md +303 -0
  55. package/templates/skills/hai-reldb/SKILL.md +19 -472
  56. package/templates/skills/hai-reldb/reference.md +452 -0
  57. package/templates/skills/hai-scheduler/SKILL.md +6 -6
  58. package/templates/skills/hai-serv/SKILL.md +27 -759
  59. package/templates/skills/hai-serv/reference.md +736 -0
  60. package/templates/skills/hai-storage/SKILL.md +7 -25
  61. package/templates/skills/hai-ui/SKILL.md +29 -625
  62. package/templates/skills/hai-ui/reference.md +598 -0
  63. package/templates/skills/hai-vecdb/SKILL.md +18 -303
  64. package/templates/skills/hai-vecdb/reference.md +291 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hai-core
3
- description: 使用 @h-ai/core 进行配置加载、日志记录、i18n 国际化、HaiResult 错误处理、Zod 校验错误本地化与模块生命周期管理;当需求涉及 core.init、core.logger、core.config、core.i18n、core.zodValidation、ok/err 或模块初始化模式时使用。
3
+ description: "使用 @h-ai/core 配置、日志、i18n、ID、Zod 错误本地化和 HaiResult。"
4
4
  ---
5
5
 
6
6
  # hai-core
@@ -9,568 +9,27 @@ description: 使用 @h-ai/core 进行配置加载、日志记录、i18n 国际
9
9
 
10
10
  | 项目 | 契约 |
11
11
  | --- | --- |
12
- | 能力 | 使用 @h-ai/core 进行配置加载、日志记录、i18n 国际化、HaiResult 错误处理、Zod 校验错误本地化与模块生命周期管理;当需求涉及 core.init、core.logger、core.config、core.i18n、core.zodValidation、ok/err 或模块初始化模式时使用。 |
13
- | 适用场景 | 当任务与 `hai-core` 的能力描述匹配,并且需要遵循本 Skill 的流程和边界时 |
14
- | 输入 | 模块配置、类型化业务参数、依赖初始化状态和目标运行环境 |
15
- | 输出 | 符合模块公共 API 的实现或示例;业务结果使用 HaiResult,并同步必要测试与文档 |
16
- | 限制 | 遵守 init → use → close 生命周期与运行环境边界;不绕过类型、授权、输入校验或敏感信息保护 |
17
-
18
- > `@h-ai/core` 是 hai-framework 的基础模块,提供配置管理、结构化日志、国际化、HaiResult 错误模型、Zod 校验错误本地化与模块生命周期工具。所有其他模块均依赖 core。
19
-
20
- ---
21
-
22
- ## 运行环境
23
-
24
- **Node.js + 浏览器双端可用。** 构建工具自动选择正确入口(`package.json` exports 条件导出)。
25
-
26
- | 能力 | Node.js | 浏览器 |
27
- |------|---------|--------|
28
- | `core.logger` | ✅ pino(结构化 JSON / pretty) | ✅ loglevel(DevTools console) |
29
- | `core.config` | ✅ 完整(YAML 加载、watch、validate) | ⚠️ 占位 API;文件配置方法返回 `SERVICE_UNAVAILABLE` |
30
- | `core.i18n` | ✅ | ✅ |
31
- | `core.zodValidation` | ✅ | ✅ |
32
- | `core.id` | ✅ | ✅ |
33
- | 工具函数 | ✅ | ✅ |
34
-
35
- ---
36
-
37
- ## 适用场景
38
-
39
- - 项目初始化与配置加载
40
- - 使用 `core.logger` 记录日志
41
- - 使用 `core.config` 读取/校验模块配置
42
- - 使用 `core.i18n` 管理多语言
43
- - 使用 `core.zodValidation` 将 Zod 默认英文错误映射为模块自己的 i18n 文案
44
- - 使用 `ok()` / `err()` 构建 HaiResult 类型返回值
45
- - 创建模块级 logger 或 i18n 消息获取器
46
-
47
- ---
48
-
49
- ## 使用步骤
50
-
51
- ### 1. 初始化
52
-
53
- ```typescript
54
- import { core } from '@h-ai/core'
55
-
56
- // 自动加载 config/ 目录下的所有 _*.yml 和 *.yml
57
- core.init({ configDir: './config' })
58
-
59
- // 也可指定日志级别和配置监听
60
- core.init({ configDir: './config', watchConfig: true })
61
- ```
62
-
63
- ### 2. 使用功能
64
-
65
- ```typescript
66
- // 日志
67
- core.logger.info('Server started', { port: 3000 })
68
-
69
- // 配置
70
- const dbConfig = core.config.get('db')
71
-
72
- // Zod 校验错误本地化
73
- const formErrors = core.zodValidation.mapZodErrorToFormErrors(zodError, getMessage)
74
- ```
75
-
76
- ---
77
-
78
- ## 核心 API
79
-
80
- ### 生命周期
81
-
82
- | 方法 | 签名 | 说明 |
83
- | ----------- | --------------------------------- | ----------------------------------------- |
84
- | `core.init` | `(options?: CoreOptions) => void` | 初始化(同步,加载配置目录中的所有 YAML) |
85
-
86
- > `CoreOptions`:`{ logging?: Partial<LoggingConfig>, configDir?: string, watchConfig?: boolean }`
87
- >
88
- > 注意:core 没有 `close()` 方法,初始化后持续可用。
89
-
90
- ### 配置管理 — `core.config`
91
-
92
- > 浏览器端保留 `core.config` 对象用于类型/runtime 对齐,但没有文件系统:`get()` 返回 `undefined`,`has()` 返回 `false`,`keys()` 返回 `[]`;`load()` / `validate()` / `reload()` 返回 `HaiCommonError.SERVICE_UNAVAILABLE`;`watch()` 会立即回调该错误并返回 no-op 取消函数。
93
-
94
- | 方法 | 签名 | 说明 |
95
- | ------------ | -------------------------------------------------------------- | ------------------------------ |
96
- | `get` | `<T>(name: string) => T \| undefined` | 获取模块配置段 |
97
- | `getOrThrow` | `<T>(name: string) => T` | 获取配置(未加载时抛异常) |
98
- | `load` | `<T>(name, filePath, schema?) => HaiResult<T>` | 手动加载配置文件 |
99
- | `validate` | `<T>(name: string, schema: ZodType) => HaiResult<T>` | 用 Zod Schema 校验已加载的配置 |
100
- | `has` | `(name: string) => boolean` | 检查配置段是否存在 |
101
- | `reload` | `(name: string) => HaiResult<unknown>` | 重新从磁盘加载配置 |
102
- | `clear` | `(name?: string) => void` | 清除配置缓存 |
103
- | `keys` | `() => string[]` | 获取所有已加载的配置名称 |
104
- | `watch` | `<T>(name, callback) => () => void` | 监听配置文件变更并自动重载 |
105
- | `unwatch` | `(name?: string) => void` | 停止配置文件监听 |
106
- | `isWatching` | `(name: string) => boolean` | 检查是否正在监听某个配置 |
107
-
108
- **`watch()` 回调处理**:
109
-
110
- ```typescript
111
- // watch 回调签名:(config: T | null, error?: HaiError) => void
112
- // - 成功重载时:传入新配置,error 为 undefined
113
- // - 重载失败时:config 为 null,error 为错误详情(包含 code 和 message)
114
-
115
- const unwatch = core.config.watch('db', (cfg, error) => {
116
- if (error) {
117
- // 处理重载失败(如文件解析错误),不应更新本地使用的配置
118
- core.logger.error('Config reload failed', {
119
- name: 'db',
120
- code: error.code,
121
- message: error.message,
122
- })
123
- return
124
- }
125
- // 使用新配置
126
- // 注意:此时 core.config.get('db') 已经指向新数据
127
- core.logger.info('Config updated', { db: cfg })
128
- })
129
-
130
- // 调用 unwatch() 停止监听
131
- unwatch()
132
- ```
133
-
134
- 配置文件格式(YAML,支持约定式环境变量映射和显式插值):
135
-
136
- - 每个 YAML 叶子配置项自动映射为 `HAI_<配置名>_<YAML 路径>`;配置名和 key 转大写、层级以 `_` 分隔,camelCase 不拆词
137
- - 例如 `core.config.load('ai', ...)` 加载的 `llm.apiKey` 对应 `HAI_AI_LLM_APIKEY`,`llm.models[0].temperature` 对应 `HAI_AI_LLM_MODELS_0_TEMPERATURE`
138
- - 约定环境变量存在时覆盖 YAML 值并按 YAML 标量规则还原类型;不存在时保留 YAML 值,且不会新增 YAML 中未声明的配置项
139
- - 优先级固定为约定环境变量 > 显式 `${VAR}` > YAML 默认值;显式语法只用于指定特殊变量名
140
-
141
- - `${VAR}` — 读取 `process.env.VAR`;缺失则返回 `HaiConfigError.CONFIG_ENV_VAR_MISSING` 错误
142
- - `${VAR:default}` — 读取 `process.env.VAR`;缺失则使用默认值
143
- - **类型还原**:整个值恰好是单个变量表达式时,结果还原为原生类型(number / boolean 等);混合文本始终为字符串
144
- - **空值语义**:`${VAR:}` 或值为空字符串的 `${VAR}` 保持为 `''`,不会被 YAML 解析为 `null`;是否把空字符串视为未配置由模块 Schema 明确定义
145
-
146
- ```yaml
147
- # config/_core.yml
148
- name: my-app
149
- env: development
150
- logging:
151
- level: info
152
- format: pretty
153
- feature:
154
- debug: false
155
- maxRetries: 3
156
- # 只有特殊变量名才需要显式语法:
157
- url: http://${HOST}:${PORT} # → string(混合文本,不还原)
158
- ```
159
-
160
- ### 日志 — `core.logger`
161
-
162
- `core.logger` 既是默认 Logger 实例,也是日志管理的统一入口。
163
-
164
- **日志记录**(实例方法):`trace` / `debug` / `info` / `warn` / `error` / `fatal`
165
-
166
- ```typescript
167
- core.logger.info('User created', { userId: '123' })
168
- core.logger.error('Failed to connect', { error })
169
- core.logger.debug('Processing item', { id, step: 'validation' })
170
- ```
171
-
172
- **管理方法**:
173
-
174
- | 方法 | 签名 | 说明 |
175
- | ----------- | -------------------------------------- | ------------------------ |
176
- | `create` | `(options?: LoggerOptions) => Logger` | 创建新 Logger 实例 |
177
- | `child` | `(context: Record<string, unknown>) => Logger` | 创建携带固定上下文的子 Logger |
178
- | `configure` | `(config: Partial<LoggingConfig>) => void` | 配置全局日志选项(级别、格式等) |
179
- | `setLevel` | `(level: LogLevel) => void` | 设置全局日志级别 |
180
- | `getLevel` | `() => LogLevel` | 获取当前全局日志级别 |
181
-
182
- ```typescript
183
- // 创建模块级 logger
184
- const logger = core.logger.create({ name: 'my-module' })
185
- logger.info('Module initialized')
186
-
187
- // 创建携带固定上下文的子 logger(自动合并到每条日志)
188
- const reqLogger = core.logger.child({ requestId: 'req-001' })
189
- reqLogger.info('Request started') // 日志中自动包含 requestId
190
-
191
- // 配置输出格式和级别
192
- core.logger.configure({
193
- level: 'warn',
194
- format: 'json',
195
- redact: ['password', 'token'], // 脱敏字段
196
- }) // Node.js 支持
197
- core.logger.setLevel('debug')
198
- const level = core.logger.getLevel() // 'debug'
199
- ```
200
-
201
- **日志级别规范**:
202
-
203
- | 级别 | 适用场景 |
204
- | ------- | ---------------------------------- |
205
- | `trace` | 循环内详细调试数据 |
206
- | `debug` | 函数进入、参数概要、读操作 |
207
- | `info` | 业务事件(初始化完成、写操作成功) |
208
- | `warn` | 异常但可恢复(校验失败、重试) |
209
- | `error` | 操作失败(需人工排查) |
210
- | `fatal` | 致命错误(服务无法继续) |
211
-
212
- ### 国际化 — `core.i18n`
213
-
214
- | 方法 | 签名 | 说明 |
215
- | --------------------- | --------------------------------------------------------------------- | -------------- |
216
- | `getGlobalLocale` | `() => string` | 获取当前语言 |
217
- | `setGlobalLocale` | `(locale: string) => void` | 设置全局语言 |
218
- | `createMessageGetter` | `(messages: Record<string, Record<string, string>>) => MessageGetter` | 创建消息获取器 |
219
-
220
- 创建模块 i18n:
221
-
222
- ```typescript
223
- import enUS from '../messages/en-US.json'
224
- import zhCN from '../messages/zh-CN.json'
225
-
226
- const m = core.i18n.createMessageGetter({
227
- 'zh-CN': zhCN,
228
- 'en-US': enUS,
229
- })
230
-
231
- // 使用
232
- m('user_created') // "用户已创建"
233
- m('welcome', { params: { name: '张三' } }) // "欢迎,张三"
234
- ```
235
-
236
- ### Zod 校验错误映射 — `core.zodValidation`
237
-
238
- 适用于 `@h-ai/kit`、`@h-ai/serv`、`@h-ai/api-client` 等需要把 Zod 默认英文错误
239
- 转换为模块自身 i18n 文案的场景。优先复用 `core.zodValidation`,不要在各模块重复维护
240
- 默认消息正则 / issue 本地化逻辑。
241
-
242
- | 方法 | 签名 | 说明 |
243
- |------|------|------|
244
- | `createPrefixedZodMessageGetter` | `(prefix, getMessage) => ZodMessageGetter` | 按模块前缀自动派生 `serv_validation*` / `kit_validation*` 这类消息 key |
245
- | `mapZodErrorToFormErrors` | `(error, getMessage) => ValidationFormError[]` | 一步完成提取 + 本地化 + 扁平化 |
246
-
247
- ```typescript
248
- import type { ZodMessageGetter } from '@h-ai/core'
249
- import { core } from '@h-ai/core'
250
- import { z } from 'zod'
251
-
252
- const LoginSchema = z.object({
253
- email: z.string().email(),
254
- password: z.string().min(8),
255
- nickname: z.string().min(1, '请填写昵称'),
256
- })
257
-
258
- const getMessage: ZodMessageGetter = (key, params) => {
259
- switch (key) {
260
- case 'validationEmail':
261
- return '请输入合法邮箱地址'
262
- case 'validationStringMin':
263
- return `至少输入 ${params?.min} 个字符`
264
- default:
265
- return '输入不合法'
266
- }
267
- }
268
-
269
- const result = LoginSchema.safeParse({
270
- email: 'bad',
271
- password: '123',
272
- nickname: '',
273
- })
274
-
275
- if (!result.success) {
276
- const getMessage = core.zodValidation.createPrefixedZodMessageGetter(
277
- 'serv',
278
- (messageKey, params) => {
279
- if (messageKey === 'serv_validationEmail')
280
- return '请输入合法邮箱地址'
281
- if (messageKey === 'serv_validationStringMin')
282
- return `至少输入 ${params?.min} 个字符`
283
- return '输入不合法'
284
- },
285
- )
286
- const errors = core.zodValidation.mapZodErrorToFormErrors(result.error, getMessage)
287
-
288
- errors[0]?.field // 'email'
289
- errors[0]?.message // '请输入合法邮箱地址'
290
- errors[2]?.message // '请填写昵称'(schema 自定义消息原样保留)
291
- }
292
- ```
293
-
294
- ### ID 生成 — `core.id`
295
-
296
- | 方法 | 签名 | 说明 |
297
- | -------------- | --------------------------------------- | ----------------------------- |
298
- | `generate` | `(size?: number) => string` | nanoid(默认 21 字符) |
299
- | `short` | `() => string` | 10 字符短 ID |
300
- | `withPrefix` | `(prefix: string, length?: number) => string` | 带前缀的 nanoid |
301
- | `trace` | `() => string` | `trace-` 前缀 ID |
302
- | `request` | `() => string` | `req-` 前缀 ID |
303
- | `uuid` | `() => string` | UUID v4 |
304
- | `isValidUUID` | `(value: string) => boolean` | 校验 UUID 格式 |
305
- | `isValidNanoId`| `(value: string, size?: number) => boolean` | 校验 nanoid 格式 |
306
-
307
- ```typescript
308
- const id = core.id.generate() // 'V1StGXR_Z5j3eK4uA9b8c'
309
- const shortId = core.id.short() // 10 字符
310
- const prefixed = core.id.withPrefix('order_') // 'order_V1StGXR...'
311
- const traceId = core.id.trace() // 'trace-V1StGXR...'
312
- const reqId = core.id.request() // 'req-V1StGXR...'
313
- const uuid = core.id.uuid() // UUID v4
314
-
315
- core.id.isValidUUID('f47ac10b-...') // true/false
316
- core.id.isValidNanoId('abc123', 6) // true/false
317
- ```
318
-
319
- #### UUID 生成规范(强制)
320
-
321
- - 生成 UUID **一律用 `core.id.uuid()`**,禁止裸调 `crypto.randomUUID()`。
322
- - 原因:`crypto.randomUUID` 仅在**安全上下文(HTTPS / localhost)**下可用,生产 http 环境会抛
323
- `crypto.randomUUID is not a function`;`core.id.uuid()` 已做 `randomUUID → getRandomValues → Math.random` 三级兜底,Node.js 与浏览器通用。
324
- - 需要不带横线的 compact 形式:`core.id.uuid().replaceAll('-', '')`。
325
- - 代码评审与新建代码时以此为准,发现裸调 `crypto.randomUUID()` 一律改走 `core.id.uuid()`。
326
-
327
- ### 工具函数
328
-
329
- #### 类型检查 — `core.typeUtils`
330
-
331
- `isDefined` / `isObject` / `isFunction` / `isPromise` / `isString` / `isNumber` / `isBoolean` / `isArray`
332
-
333
- #### 对象操作 — `core.object`
334
-
335
- `deepClone` / `deepMerge` / `pick` / `omit` / `keys` / `values` / `entries` / `fromEntries`
336
-
337
- ```typescript
338
- core.object.deepMerge(a, b, c) // 递归合并(防原型污染)
339
- core.object.pick(obj, ['a', 'b'])
340
- core.object.omit(obj, ['c', 'd'])
341
- ```
342
-
343
- #### 字符串操作 — `core.string`
344
-
345
- `capitalize` / `kebabCase` / `camelCase` / `snakeCase` / `pascalCase` / `truncate` / `trim` / `isBlank` / `isNotBlank` / `padStart` / `padEnd` / `constantTimeEqual`
346
-
347
- ```typescript
348
- core.string.capitalize('hello') // 'Hello'
349
- core.string.kebabCase('helloWorld') // 'hello-world'
350
- core.string.constantTimeEqual(a, b) // 防时序攻击比较
351
- ```
352
-
353
- #### 数组操作 — `core.array`
354
-
355
- `unique` / `chunk` / `groupBy` / `first` / `last` / `flatten` / `compact` / `shuffle` / `intersection` / `difference`
356
-
357
- ```typescript
358
- core.array.unique([1, 1, 2]) // [1, 2]
359
- core.array.chunk([1, 2, 3, 4], 2) // [[1,2], [3,4]]
360
- core.array.groupBy(items, item => item.type) // Record<string, T[]>
361
- core.array.intersection([1, 2], [2, 3]) // [2]
362
- ```
363
-
364
- #### 异步操作 — `core.async`
365
-
366
- `delay` / `withTimeout` / `retry` / `parallel` / `serial` / `debounce` / `throttle`
367
-
368
- ```typescript
369
- await core.async.delay(1000)
370
- await core.async.withTimeout(fetch('/api'), 5000)
371
- await core.async.retry(fn, { maxRetries: 3, delay: 1000 })
372
- await core.async.parallel(items, processFn, 5) // 并发限制
373
- const debouncedFn = core.async.debounce(fn, 300)
374
- ```
375
-
376
- #### 时间操作 — `core.time`
377
-
378
- `formatDate` / `timeAgo` / `now` / `nowSeconds` / `parseDate` / `isValidDate` / `addDays` / `addHours` / `startOfDay` / `endOfDay`
379
-
380
- ```typescript
381
- core.time.formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
382
- core.time.timeAgo(new Date(Date.now() - 60000)) // '1 分钟前'
383
- core.time.addDays(date, 7)
384
- ```
385
-
386
- ### HaiResult 模型与错误处理
387
-
388
- 所有 hai 模块操作返回 `HaiResult<T>` 类型,强制处理成功/失败分支:
389
-
390
- ```typescript
391
- import { ok, err, HaiCommonError } from '@h-ai/core'
392
-
393
- // 成功
394
- return ok(data)
395
-
396
- // 失败
397
- return err(HaiCommonError.NOT_FOUND, 'Resource not found')
398
- ```
399
-
400
- **使用模式**:
401
-
402
- ```typescript
403
- const result = await someOperation()
404
-
405
- // 模式1:直接检查
406
- if (!result.success) {
407
- core.logger.error('Operation failed', { error: result.error })
408
- return result // 直接透传错误
409
- }
410
- const data = result.data
411
-
412
- // 模式2:提前返回
413
- if (!result.success)
414
- return result
415
- // 后续代码可安全使用 result.data
416
- ```
417
-
418
- ## 错误码
419
-
420
- Core 模块定义了两组标准错误码:
421
-
422
- ### `HaiCommonError`(通用错误)
423
-
424
- | 错误码 | code | 说明 |
425
- |--------|------|------|
426
- | `HaiCommonError.NOT_INITIALIZED` | `hai:common:001` | 模块未初始化 |
427
- | `HaiCommonError.INIT_FAILED` | `hai:common:002` | 初始化失败 |
428
- | `HaiCommonError.INIT_IN_PROGRESS` | `hai:common:004` | 正在初始化中 |
429
- | `HaiCommonError.UNAUTHORIZED` | `hai:common:100` | 未认证 |
430
- | `HaiCommonError.FORBIDDEN` | `hai:common:101` | 无权限 |
431
- | `HaiCommonError.TOKEN_EXPIRED` | `hai:common:102` | Token 已过期 |
432
- | `HaiCommonError.TOKEN_INVALID` | `hai:common:103` | Token 无效 |
433
- | `HaiCommonError.VALIDATION_ERROR` | `hai:common:200` | 校验失败 |
434
- | `HaiCommonError.INVALID_REQUEST` | `hai:common:201` | 请求无效 |
435
- | `HaiCommonError.PARAMETER_MISSING` | `hai:common:202` | 参数缺失 |
436
- | `HaiCommonError.NOT_FOUND` | `hai:common:300` | 资源不存在 |
437
- | `HaiCommonError.ALREADY_EXISTS` | `hai:common:301` | 资源已存在 |
438
- | `HaiCommonError.CONFLICT` | `hai:common:302` | 冲突 |
439
- | `HaiCommonError.API_ERROR` | `hai:common:400` | API 错误 |
440
- | `HaiCommonError.NETWORK_ERROR` | `hai:common:401` | 网络错误 |
441
- | `HaiCommonError.TIMEOUT` | `hai:common:402` | 超时 |
442
- | `HaiCommonError.SERVICE_UNAVAILABLE` | `hai:common:403` | 服务不可用 |
443
- | `HaiCommonError.INTERNAL_ERROR` | `hai:common:500` | 内部错误 |
444
- | `HaiCommonError.DATABASE_ERROR` | `hai:common:501` | 数据库错误 |
445
- | `HaiCommonError.UNKNOWN_ERROR` | `hai:common:599` | 未知错误 |
446
-
447
- **使用示例**:
448
-
449
- ```typescript
450
- import { HaiCommonError, err } from '@h-ai/core'
451
-
452
- // 创建错误实例
453
- err(HaiCommonError.NOT_FOUND, 'User not found')
454
- err(HaiCommonError.VALIDATION_ERROR, 'Invalid email', validationErrors)
455
- ```
456
-
457
- ### `HaiConfigError`(配置错误)
458
-
459
- | 错误码 | code | 说明 |
460
- |--------|------|------|
461
- | `HaiConfigError.CONFIG_FILE_NOT_FOUND` | `hai:core:010` | 配置文件不存在 |
462
- | `HaiConfigError.CONFIG_PARSE_ERROR` | `hai:core:011` | YAML 解析失败 |
463
- | `HaiConfigError.CONFIG_VALIDATION_ERROR` | `hai:core:012` | Schema 校验失败 |
464
- | `HaiConfigError.CONFIG_ENV_VAR_MISSING` | `hai:core:013` | 必需环境变量缺失 |
465
- | `HaiConfigError.CONFIG_NOT_LOADED` | `hai:core:014` | 配置未加载 |
466
-
467
- **使用场景**:
468
-
469
- ```typescript
470
- import { core, HaiConfigError } from '@h-ai/core'
471
-
472
- const result = core.config.load('db', './config/db.yml', schema)
473
- if (!result.success) {
474
- if (result.error.code === HaiConfigError.CONFIG_FILE_NOT_FOUND.code) {
475
- core.logger.error('Config file missing')
476
- }
477
- else if (result.error.code === HaiConfigError.CONFIG_VALIDATION_ERROR.code) {
478
- core.logger.error('Config validation failed', { issues: result.error.cause })
479
- }
480
- }
481
- ```
482
-
483
- ## 常见模式
484
-
485
- ### 服务端初始化(SvelteKit hooks.server.ts)
486
-
487
- ```typescript
488
- import { initModules } from '$lib/server/init'
489
-
490
- // init.ts
491
- import { core } from '@h-ai/core'
492
-
493
- const appHandle = kit.createHandle({ /* ... */ })
494
-
495
- export const handle = kit.sequence(appHandle)
496
-
497
- let initialized = false
498
- export async function initModules() {
499
- if (initialized)
500
- return
501
- core.init({ configDir: './config' })
502
- // ... 其他模块 init
503
- initialized = true
504
- }
505
- ```
506
-
507
- ### 配置校验最佳实践
508
-
509
- ```typescript
510
- import { z } from 'zod'
511
-
512
- const MyConfigSchema = z.object({
513
- apiKey: z.string().min(1),
514
- timeout: z.number().positive().default(5000),
515
- })
516
-
517
- // 模块初始化时校验
518
- const result = core.config.validate('myModule', MyConfigSchema)
519
- if (!result.success) {
520
- core.logger.error('Invalid config', { error: result.error })
521
- return result
522
- }
523
- const config = result.data // 类型安全
524
- ```
525
-
526
- ---
527
-
528
- ## 相关 Skills
529
-
530
- - `hai-build`:项目整体架构与技能导航
531
- - `hai-kit`:SvelteKit 集成层(Handle/Guard/Middleware)
532
- - `hai-reldb` / `hai-cache` / `hai-iam`:使用 core.config 读取配置的下游模块
533
-
534
- ---
535
-
536
- ## 浏览器侧使用
537
-
538
- > **禁止在应用中直接引入 `loglevel`。** `core.logger` 在浏览器端自动使用 loglevel 作为后端,API 与 Node.js 完全一致。
539
-
540
- ### 初始化
541
-
542
- 浏览器端无文件系统,不需要 `configDir`。在 `+layout.svelte` 或应用入口调用:
543
-
544
- ```typescript
545
- import { core } from '@h-ai/core'
546
-
547
- // 浏览器端:仅传入 logging 配置即可
548
- core.init({ logging: { level: 'info' } })
549
- ```
550
-
551
- ### 日志记录
552
-
553
- ```typescript
554
- // 与 Node.js 完全一致的 API
555
- core.logger.info('Page loaded', { route: '/home' })
556
- core.logger.error('API call failed', { url, status })
557
-
558
- // 创建模块级 logger
559
- const logger = core.logger.create({ name: 'my-page' })
560
- logger.debug('Data fetched', { count: items.length })
561
-
562
- // ❌ 禁止直接引用底层日志库
563
- // import log from 'loglevel' ← 不要这样做
564
- ```
565
-
566
- ### 浏览器侧限制
567
-
568
- | 功能 | 行为 |
569
- |------|------|
570
- | `core.config` 文件配置 | 不支持;文件相关方法返回 `SERVICE_UNAVAILABLE`,配置应由服务端读取后下发 |
571
- | `format: 'json' \| 'pretty'` | 被忽略(始终输出到 DevTools console) |
572
- | `redact` 字段脱敏 | 不生效(需手动脱敏) |
573
- | `fatal` 级别 | 映射为 `console.error`(带 `[FATAL]` 前缀) |
574
- | context 输出 | JSON 字符串追加到消息末尾(非结构化对象) |
575
-
576
- 云资源配置消费:Neon 输出 HAI_DB(PostgreSQL JSON),Upstash 输出 HAI_CACHE(Redis rediss URL JSON),R2 输出 HAI_STORAGE(扁平 S3 JSON)。邮件/短信输出 HAI_REACH_PROVIDERS 数组,deployApp 合并两个渠道并保留 YAML 中的消息模板;此覆盖替换原 providers 数组。Core 支持整体对象/数组覆盖,父节点优先于叶子变量,仍执行 Schema 校验;构建子进程与目标运行环境使用相同开通结果。R2 必须提供已有 S3 accessKeyId/secretAccessKey;Resend 必须提供已验证域名的 from;阿里云必须提供已审核 signName/模板。应用须已有相应 _db/_cache/_storage/_reach.yml 并在启动时加载,网络客户端需要 Node 运行时。
12
+ | 能力 | 使用 @h-ai/core 配置、日志、i18n、ID、Zod 错误本地化和 HaiResult |
13
+ | 适用场景 | 启动配置、日志脱敏、国际化或公共错误处理 |
14
+ | 输入 | CoreOptions、YAML、环境变量、locale、日志上下文 |
15
+ | 输出 | core.init 为同步 void;配置校验为 HaiResult;工具返回直接值 |
16
+ | 限制 | core 无 close;浏览器不加载 YAML。配置日志需脱敏,ID 不能当作密钥。 |
17
+
18
+ ## 使用路径
19
+
20
+ - Node 启动用 core.init({ configDir }) 加载配置;勿传 logging 覆盖 _core.yml 中的日志设置,除非任务明确要求。
21
+ - HAI_<配置名>_<路径> 可整体覆盖根/对象/数组;父节点优先,未覆盖时才遍历已有叶子。camelCase 不拆词,约定变量优先于显式插值。
22
+ - 配置 getter 与 ID/logger 方法不是 HaiResult;validate/load 等须检查 success。
23
+
24
+ ## 按需参考
25
+
26
+ 详细配置、API 签名、错误码和范本在 [reference.md](reference.md)。按下列标题定位所需小节,不默认通读;用例中的业务变量需结合当前应用补齐。
27
+
28
+ - 运行环境
29
+ - 适用场景
30
+ - 使用步骤
31
+ - 核心 API
32
+ - 错误码
33
+ - 常见模式
34
+ - 相关 Skills
35
+ - 浏览器侧使用