@h-ai/core 0.1.0-alpha9 → 1.0.0-beta.1

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 CHANGED
@@ -1,21 +1,30 @@
1
1
  # @h-ai/core
2
2
 
3
- > hai Agent Framework 核心模块 — 提供统一的基础工具、类型定义、配置管理和日志功能。
3
+ [简体中文](./README.md) | [English](./README.en.md) | [日本語](./README.ja.md)
4
4
 
5
- ## 支持的能力
5
+ [框架首页](../../README.md)
6
+
7
+ ## 简介
8
+
9
+ hai Agent Framework 核心模块 — 提供统一的基础工具、类型定义、配置管理和日志功能。
10
+
11
+ ## 核心功能清单
6
12
 
7
13
  - **HaiResult 类型** — 函数式错误处理(`ok` / `err`),所有模块统一返回值
8
14
  - **统一日志** — Node.js 基于 pino,浏览器基于 loglevel,API 一致
9
15
  - **国际化(i18n)** — 集中式 locale 管理 + 类型安全的消息获取器
10
- - **配置管理** — YAML 加载、环境变量插值、Zod 校验、文件监听(Node.js 专用)
16
+ - **Zod 校验错误映射** — 兼容 Zod v3/v4 的 issue 提取与默认英文消息本地化
17
+ - **配置管理** — YAML 加载、约定式环境变量映射、显式环境变量插值、Zod 校验、文件监听(Node.js 专用)
11
18
  - **ID 生成** — nanoid 与 UUID v4
12
19
  - **错误定义** — 标准化错误码体系,支持跨模块统一的错误定义与实例创建
13
20
  - **模块工具** — NotInitializedKit 代理模式,各模块共用的生命周期基础设施
14
21
  - **工具函数库** — 类型检查、对象 / 字符串 / 数组 / 异步 / 时间操作
15
22
 
16
- ## 快速开始
23
+ ## 使用入门
17
24
 
18
- ### Node.js 环境
25
+ ### 快速开始
26
+
27
+ #### Node.js 环境
19
28
 
20
29
  ```typescript
21
30
  import { core } from '@h-ai/core'
@@ -57,6 +66,7 @@ const prefixed = core.id.withPrefix('order_')
57
66
 
58
67
  // 工具函数
59
68
  core.object.deepMerge(objA, objB)
69
+ core.sanitize.sanitizeSensitiveFields({ password: 'secret', email: 'user@example.com' })
60
70
  core.string.capitalize('hello')
61
71
  core.array.unique([1, 1, 2, 3])
62
72
  core.array.chunk([1, 2, 3, 4, 5], 2) // [[1,2], [3,4], [5]]
@@ -66,7 +76,7 @@ core.time.formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
66
76
  core.typeUtils.isDefined(value)
67
77
  ```
68
78
 
69
- ### 浏览器环境
79
+ #### 浏览器环境
70
80
 
71
81
  ```typescript
72
82
  import { core } from '@h-ai/core'
@@ -82,13 +92,20 @@ core.string.capitalize('hello')
82
92
  core.array.unique([1, 1, 2])
83
93
  ```
84
94
 
85
- ## 详细 API
95
+ 浏览器端会保留 `core.config` API 形态以匹配类型定义,但不支持文件配置:
96
+
97
+ - `core.config.get()` 返回 `undefined`,`has()` 返回 `false`,`keys()` 返回 `[]`
98
+ - `loadEnvFiles()` / `load()` / `validate()` / `reload()` 返回 `HaiCommonError.SERVICE_UNAVAILABLE`
99
+ - `watch()` 会立即用同一错误调用回调,并返回安全的 no-op 取消函数
100
+ - 需要配置时请由服务端读取后通过接口下发,或只在浏览器端用 `core.init({ logging })` 配置日志
101
+
102
+ ### 详细 API
86
103
 
87
- ### 日志(Logger)
104
+ #### 日志(Logger)
88
105
 
89
106
  `core.logger` 既是默认 Logger 实例,也是日志管理的统一入口。
90
107
 
91
- #### 日志记录
108
+ ##### 日志记录
92
109
 
93
110
  6 个级别:`trace` / `debug` / `info` / `warn` / `error` / `fatal`
94
111
 
@@ -98,7 +115,7 @@ core.logger.error('Failed to connect', { error })
98
115
  core.logger.debug('Processing item', { id, step: 'validation' })
99
116
  ```
100
117
 
101
- #### 创建模块级 logger
118
+ ##### 创建模块级 logger
102
119
 
103
120
  ```typescript
104
121
  // 独立名称空间,适合模块内全局使用
@@ -106,7 +123,7 @@ const logger = core.logger.create({ name: 'payment' })
106
123
  logger.info('Payment processed')
107
124
  ```
108
125
 
109
- #### 子 logger(child)
126
+ ##### 子 logger(child)
110
127
 
111
128
  ```typescript
112
129
  // 携带固定上下文,每条日志自动附带这些字段
@@ -122,10 +139,10 @@ opLogger.error('Login failed', { reason: 'invalid_password' })
122
139
 
123
140
  > **典型用法**:在请求入口创建 `reqLogger`,传入 service 层,service 层再 `child` 添加操作上下文,最终日志自动串联完整链路。
124
141
 
125
- #### 日志管理
142
+ ##### 日志管理
126
143
 
127
144
  ```typescript
128
- // 配置输出格式和级别(Node.js)
145
+ // 配置输出格式和级别(Node.js / Browser)
129
146
  core.logger.configure({
130
147
  level: 'info',
131
148
  format: 'json', // 'json' | 'pretty'
@@ -137,7 +154,15 @@ core.logger.setLevel('debug')
137
154
  const level = core.logger.getLevel() // 'debug'
138
155
  ```
139
156
 
140
- #### 日志级别规范
157
+ `core.init()` 加载 `_core.yml.logging` 后,或调用 `configure` / `setLevel` 后,默认、模块和嵌套 child Logger 会在后续输出中使用当前全局配置,即使这些实例在初始化前创建。`create({ level, format, context })` 的显式级别、格式和本地上下文仍优先;未指定的选项动态继承全局配置,调用时上下文覆盖固定上下文。附加 `redact` 对这些实例统一生效,不需要重建模块 Logger。
158
+
159
+ 浏览器仍输出到 DevTools console:`pretty` 使用可读消息与上下文,`json` 输出包含 `level` / `msg` 的 JSON 字符串。默认凭据脱敏与配置的字段路径脱敏同时生效,支持嵌套点/括号路径和 `*`;消息文本本身需调用方脱敏。
160
+
161
+ 日志上下文中的原生 `Error` 会先转为包含 `name`、`message`、`stack`、`cause`、`AggregateError.errors` 与可枚举自有上下文的副本,再执行默认字段/URL 脱敏与配置的 `redact`;两端规则一致,原异常不变。异常子图循环返回 `[Circular]`,32 层上限返回 `[MaxDepth]`;其中的自定义替换值也经同一规范化处理,普通对象的替换语义不变。无 Error 的普通对象循环仍保留原结构,不保证可直接 JSON 序列化。异常文本会清理显式标注的凭据(如 `passwordHash=...`、`secretKey=...`)、认证头和 URL 凭据;无法保证识别任意无标签秘密,调用方仍应清理或用 `error.message` / `error.stack` 等路径整体遮蔽。
162
+
163
+ `core.logger` 默认会对日志上下文执行安全脱敏:`password`、`passwordHash`、`passwd`、`token`、`secret`、`secretKey`、`privateKey`、`apiV3Key` 等凭证类字段会替换为 `[REDACTED]`,`url` / `baseUrl` / `endpoint` 这类字段会掩码 URL 中的用户名、密码和敏感查询参数(含 OAuth 键值 fragment);非法 URL 返回固定占位符;邮箱、手机号默认不会被遮盖。
164
+
165
+ ##### 日志级别规范
141
166
 
142
167
  | 级别 | 适用场景 |
143
168
  | ------- | ---------------------------------- |
@@ -148,7 +173,9 @@ const level = core.logger.getLevel() // 'debug'
148
173
  | `error` | 操作失败(需人工排查) |
149
174
  | `fatal` | 致命错误(服务无法继续) |
150
175
 
151
- ### 国际化(i18n)
176
+ #### 国际化(i18n)
177
+
178
+ 内置语言为 `zh-CN`、`en-US`、`ja-JP`;`ja` 简写会规范化为 `ja-JP`,所有模块共用此语言设置。
152
179
 
153
180
  ```typescript
154
181
  // 设置全局语言(所有 getMessage 函数自动响应)
@@ -168,19 +195,106 @@ getMessage('welcome', { params: { name: 'Alice' } }) // 'Welcome Alice'
168
195
  getMessage('welcome', { locale: 'zh-CN', params: { name: 'Alice' } }) // '欢迎 Alice'
169
196
 
170
197
  // 常量
171
- core.i18n.DEFAULT_LOCALES // [{ code: 'zh-CN', label: '简体中文' }, { code: 'en-US', label: 'English' }]
198
+ core.i18n.DEFAULT_LOCALES // [{ code: 'zh-CN', label: '简体中文' }, { code: 'en-US', label: 'English' }, { code: 'ja-JP', label: '日本語' }]
172
199
  core.i18n.DEFAULT_LOCALE // 'zh-CN'
173
200
  ```
174
201
 
175
- ### 配置管理(Node.js 专用)
202
+ #### 脱敏工具
203
+
204
+ `core.sanitize.sanitizeSensitiveFields()` 可递归脱敏对象/数组中的敏感字段;默认会处理 password/passwordHash、passwd、token、secret/secretKey、privateKey、apiV3Key 等凭证类字段,并自动掩码 `url` / `baseUrl` / `endpoint` 字段中的内嵌凭证、敏感查询参数和 OAuth 键值 fragment。`core.sanitize.sanitizeUrl(url, options)` 可单独处理 URL,非法 URL 返回 `(invalid url)`;自定义 matcher 同样适用于参数名。`sanitizeText` 清理显式凭据标签和 URL,也可传入调用方已知的秘密值;它不识别任意无标签秘密。
205
+
206
+ ```typescript
207
+ core.sanitize.sanitizeSensitiveFields({
208
+ email: 'user@example.com',
209
+ password: 'secret',
210
+ baseUrl: 'https://user:pass@example.com/api/v1',
211
+ profile: { token: 'jwt-token' },
212
+ })
213
+ // => { email: 'user@example.com', password: '[REDACTED]', baseUrl: 'https://[REDACTED]:[REDACTED]@example.com/api/v1', profile: { token: '[REDACTED]' } }
214
+
215
+ core.sanitize.sanitizeSensitiveFields({ email: 'user@example.com' }, {
216
+ matcher: /email/i,
217
+ replacement: '***',
218
+ })
219
+ // => { email: '***' }
220
+ ```
221
+
222
+ #### Zod 校验错误 i18n 映射
223
+
224
+ `core.zodValidation` 用于把 Zod `SafeParseError` / `ZodError` 转为扁平表单错误,并将 Zod
225
+ 默认英文消息映射为调用方自己的 i18n 文案。
226
+
227
+ 严格 schema 的未知字段错误映射为 `validationUnexpectedFields`,不向用户暴露内部字段名。自定义消息获取器需要处理此 key;Kit、Serv 已提供中英日文提示。Schema 显式指定的业务消息仍原样保留。
228
+
229
+ - 两个公开入口:
230
+ - `createPrefixedZodMessageGetter()`:按模块前缀自动派生 `serv_validation*` / `kit_validation*` 这类 key,省掉手写 `KEY_MAP`
231
+ - `mapZodErrorToFormErrors()`:一步完成提取 + 本地化 + 扁平化
232
+ - 自定义业务消息不会被覆盖;只有 Zod 默认英文消息才会被替换
233
+
234
+ ```typescript
235
+ import type { ZodMessageGetter } from '@h-ai/core'
236
+ import { core } from '@h-ai/core'
237
+ import { z } from 'zod'
238
+
239
+ const LoginSchema = z.object({
240
+ email: z.string().email(),
241
+ password: z.string().min(8),
242
+ nickname: z.string().min(1, '请填写昵称'),
243
+ })
244
+
245
+ const getMessage: ZodMessageGetter = (key, params) => {
246
+ switch (key) {
247
+ case 'validationEmail':
248
+ return '请输入合法邮箱地址'
249
+ case 'validationStringMin':
250
+ return `至少输入 ${params?.min} 个字符`
251
+ case 'validationRequired':
252
+ return '此项为必填项'
253
+ default:
254
+ return '输入不合法'
255
+ }
256
+ }
257
+
258
+ const result = LoginSchema.safeParse({
259
+ email: 'bad',
260
+ password: '123',
261
+ nickname: '',
262
+ })
263
+
264
+ if (!result.success) {
265
+ const getMessage = core.zodValidation.createPrefixedZodMessageGetter(
266
+ 'kit',
267
+ (messageKey, params) => {
268
+ if (messageKey === 'kit_validationEmail')
269
+ return '请输入合法邮箱地址'
270
+ if (messageKey === 'kit_validationStringMin')
271
+ return `至少输入 ${params?.min} 个字符`
272
+ return '输入不合法'
273
+ },
274
+ )
275
+ const errors = core.zodValidation.mapZodErrorToFormErrors(result.error, getMessage)
276
+
277
+ errors[0]?.field // 'email'
278
+ errors[0]?.message // '请输入合法邮箱地址'
279
+ errors[2]?.message // '请填写昵称'(schema 自定义消息原样保留)
280
+
281
+ core.logger.info('Validation failed', { errors })
282
+ }
283
+ ```
284
+
285
+ #### 配置管理(Node.js 专用)
286
+
287
+ > 浏览器端没有文件系统,`core.config` 仅提供占位 API:文件相关方法返回 `SERVICE_UNAVAILABLE`,不会加载 YAML。
288
+
289
+ Node 自动加载和监听配置目录内所有 `.yml` / `.yaml`,没有保留文件名例外。项目根 `hai.json` 中的 `build` / `deploy` 由 CLI 读取,不进入运行时 YAML 配置。单项加载返回失败时记录错误并继续,必需配置由应用调用 `core.config.validate` 并检查 `success`;目录扫描、日志设置等异常仍可能传播。
176
290
 
177
291
  ```typescript
178
292
  import { core, HaiConfigError } from '@h-ai/core'
179
293
  import { z } from 'zod'
180
294
 
181
- // 方式 1:init 时自动加载整个配置目录
295
+ // 方式 1:init 时自动加载运行时配置
182
296
  core.init({
183
- configDir: './config', // 扫描该目录所有 .yml 文件
297
+ configDir: './config', // 扫描配置目录内所有 .yml / .yaml
184
298
  watchConfig: true, // 监听变更并自动重载
185
299
  })
186
300
 
@@ -202,8 +316,8 @@ const validResult = core.config.validate('app', AppSchema)
202
316
 
203
317
  // 查询和获取
204
318
  if (core.config.has('app')) {
205
- const cfg = core.config.get<typeof AppSchema>('app') // undefined 或配置
206
- const cfg2 = core.config.getOrThrow<typeof AppSchema>('app') // 未加载时抛异常
319
+ const cfg = core.config.get<z.infer<typeof AppSchema>>('app') // undefined 或配置
320
+ const cfg2 = core.config.getOrThrow<z.infer<typeof AppSchema>>('app') // 未加载时抛异常
207
321
  }
208
322
  core.config.keys() // ['core', 'app', 'db', ...]
209
323
 
@@ -225,15 +339,53 @@ core.config.clear('app') // 清除单个
225
339
  core.config.clear() // 清除全部
226
340
  ```
227
341
 
228
- #### 环境变量插值
342
+ ##### dotenv 文件与环境切换
343
+
344
+ Node 专用的 `core.config.loadEnvFiles({ mode, envDir })` 返回 HaiResult,读取 `.env` → `.env.<mode>` → `.env.local`,已有进程环境最高;development/production 映射为 dev/prod,文件不存在时跳过。该函数不修改进程,调用方检查 success 后注入。除 `.env.local` 外的公共环境文件及完整说明 `.env.example` 提交 Git。`core.init` 不自动读取 dotenv,浏览器调用返回 SERVICE_UNAVAILABLE。
345
+
346
+ ```ts
347
+ const env = core.config.loadEnvFiles({ mode: 'dev', envDir: '.' })
348
+ if (!env.success)
349
+ throw new Error(env.error.message)
350
+ Object.assign(process.env, env.data)
351
+ core.init({ configDir: './config' })
352
+ ```
353
+
354
+ ##### 环境变量映射与插值
355
+
356
+ `core.config.load(name, ...)` 会为每个 YAML 叶子配置项自动映射
357
+ `HAI_<配置名>_<YAML 路径>` 环境变量。配置名和每层 key 转成大写,层级以 `_`
358
+ 分隔,camelCase 不拆词;例如 `ai.llm.apiKey` 对应 `HAI_AI_LLM_APIKEY`,
359
+ `ai.llm.maxTokens` 对应 `HAI_AI_LLM_MAXTOKENS`。数组索引也作为一层,例如
360
+ `ai.llm.models[0].temperature` 对应 `HAI_AI_LLM_MODELS_0_TEMPERATURE`。
361
+
362
+ 环境变量存在时覆盖 YAML 值,并按 YAML 标量规则还原 number / boolean 等类型;
363
+ 环境变量不存在时保留 YAML 值。对象或数组节点也可用同名变量整体替换(JSON),例如 HAI_DB 切换完整连接配置、HAI_REACH_PROVIDERS 替换渠道数组。父节点整体覆盖优先于其子字段环境变量;替换后仍执行 Schema 校验。
364
+
365
+ ```yaml
366
+ # config/_ai.yml(core.init 扫描后配置名为 ai)
367
+ llm:
368
+ apiKey: ''
369
+ baseUrl: https://api.xiaomimimo.com/v1
370
+ model: mimo-v2.5
371
+ maxTokens: 4096
372
+ ```
373
+
374
+ ```dotenv
375
+ HAI_AI_LLM_APIKEY=your-api-key
376
+ HAI_AI_LLM_MAXTOKENS=8192
377
+ ```
229
378
 
230
- `core.config.load()` 加载 YAML 时,自动对字符串值做 `${VAR}` 环境变量替换。
379
+ 确实需要特殊变量名时,继续使用显式 `${VAR}` 语法。优先级固定为:
380
+ 约定环境变量 > 显式环境变量 > YAML 默认值。
231
381
 
232
382
  - `${VAR}` — 读取 `process.env.VAR`;若不存在则返回 `CONFIG_ENV_VAR_MISSING` 错误
233
383
  - `${VAR:default}` — 读取 `process.env.VAR`;若不存在则使用冒号后的默认值
234
384
 
235
385
  **类型还原:** 当整个值恰好是单个变量表达式(如 `${PORT:3000}`)时,插值结果按 YAML 规则还原为原生类型(number / boolean 等)。混合文本始终为字符串。
236
386
 
387
+ `${VAR:}` 或值为空字符串的 `${VAR}` 会保留为 `''`,不会被 YAML 转成 `null`。需要表达“未配置”的模块应在自身 Schema 中把空字符串规范化为 `undefined`。
388
+
237
389
  ```yaml
238
390
  # config/app.yml
239
391
  debug: ${DEBUG:false} # → boolean false
@@ -242,7 +394,7 @@ database: ${DB_CONNECTION} # → 环境变量缺失时报错
242
394
  url: http://${HOST}:${PORT} # → string(混合文本)
243
395
  ```
244
396
 
245
- ### ID 生成
397
+ #### ID 生成
246
398
 
247
399
  ```typescript
248
400
  const id = core.id.generate() // nanoid 21 字符
@@ -258,7 +410,9 @@ core.id.isValidUUID('f47ac10b-...') // true/false
258
410
  core.id.isValidNanoId('abc123', 6) // true/false
259
411
  ```
260
412
 
261
- ### 工具函数
413
+ UUID 只使用密码学安全的 randomUUID/getRandomValues;均不可用时明确抛错,不生成 Math.random 标识。
414
+
415
+ #### 工具函数
262
416
 
263
417
  ```typescript
264
418
  // 类型检查
@@ -294,7 +448,7 @@ core.string.isBlank('') // true
294
448
  core.string.isNotBlank('x') // true
295
449
  core.string.padStart('5', 3, '0') // '005'
296
450
  core.string.padEnd('5', 3, '0') // '500'
297
- core.string.constantTimeEqual(a, b) // 防时序攻击比较
451
+ core.string.constantTimeEqual(a, b) // Node 使用原生等长比较;浏览器不保证恒时
298
452
 
299
453
  // 数组操作
300
454
  core.array.unique([1, 1, 2]) // [1, 2]
@@ -330,7 +484,9 @@ core.time.startOfDay(date)
330
484
  core.time.endOfDay(date)
331
485
  ```
332
486
 
333
- ### 模块基础工具(module)
487
+ Node 入口的 core.string.constantTimeEqual 将原始 UTF-16 单元编码后使用 node:crypto.timingSafeEqual 比较等长字节,长度不相同时直接 false,长度本身不受保护。浏览器实现仅避免按内容提前返回,不提供 JavaScript 引擎级恒时保证,不用于服务端凭证校验。外围分支、编码和请求处理不因此自动成为恒时。
488
+
489
+ #### 模块基础工具(module)
334
490
 
335
491
  提供各模块共用的初始化错误处理:
336
492
 
@@ -348,9 +504,9 @@ const currentDdl: DdlOperations = notInitialized.proxy<DdlOperations>()
348
504
  const currentHash: HashOperations = notInitialized.proxy<HashOperations>('sync')
349
505
  ```
350
506
 
351
- ## 错误处理
507
+ ### 错误处理
352
508
 
353
- ### HaiResult 类型
509
+ #### HaiResult 类型
354
510
 
355
511
  所有可能失败的操作返回 `HaiResult<T>`:
356
512
 
@@ -373,7 +529,7 @@ else {
373
529
  }
374
530
  ```
375
531
 
376
- ### 自定义错误码(模块级)
532
+ #### 自定义错误码(模块级)
377
533
 
378
534
  ```typescript
379
535
  import type { ErrorInfo } from '@h-ai/core'
@@ -399,7 +555,7 @@ const errorInst = core.error.buildHaiErrorInst(
399
555
  )
400
556
  ```
401
557
 
402
- ### 配置错误码
558
+ #### 配置错误码
403
559
 
404
560
  ```typescript
405
561
  import { HaiConfigError } from '@h-ai/core'
@@ -411,12 +567,87 @@ HaiConfigError.CONFIG_ENV_VAR_MISSING // hai:core:013 - 环境变量缺失
411
567
  HaiConfigError.CONFIG_NOT_LOADED // hai:core:014 - 配置未加载
412
568
  ```
413
569
 
414
- ## 测试
570
+ ### 请求语言隔离
571
+
572
+ Node 请求与 SSR 使用 `core.i18n.runWithLocale(locale, () => handleRequest())`;SvelteKit 可使用 `kit.i18n.runWithLocale(locale, () => resolve(event))`,serv 自动按请求头建立上下文。异步子任务继承请求语言,优先级为显式 `MessageOptions.locale`、请求语言、进程启动默认语言。`setGlobalLocale` 在请求上下文中只更新当前请求;上下文外仅用于启动默认值,不能在请求 hook 中调用它修改默认值。
573
+
574
+ 浏览器继续使用应用级 `setGlobalLocale` / `createLocaleStore`。SSR 的 UI store 初始化和 setter 只修改自身状态,不写全局语言;UI 消息从请求上下文读取。`runWithLocale` 仅支持 Node 入口,浏览器通过显式 locale 或应用状态翻译。
575
+
576
+ ## 可信地址与请求访问策略(Node)
577
+
578
+ `core.access` 为 HTTP 宿主提供共享的 IP、时间与路径检查,不自动安装 HTTP 拦截器。Serv 使用 `http.access`,SvelteKit 使用 `kit.createHandle({ access })`;IAM 的 `loginAccess` 只检查登录,不能替代每次请求检查。
579
+
580
+ ```ts
581
+ import { core } from '@h-ai/core'
582
+
583
+ const check = core.access.createRequestCheck({
584
+ rules: [{ id: 'external-api', paths: ['/api/v1/external/**'], allowIps: ['192.0.2.0/24'], encryption: 'disabled' }],
585
+ })
586
+ const decision = check('/api/v1/external/sync', 'POST', '192.0.2.10')
587
+ const mode = check.encryption('/api/v1/external/sync', 'POST') // disabled
588
+ ```
589
+
590
+ `createRequestCheck` 在创建时验证并编译规则;`loadPolicy({ file: './config/security/request-access.yml' })` 只在启动时读取独立 YAML,最大 1 MiB,错误配置抛出异常并阻止宿主启动。全局条件与所有命中规则取交集,拒绝地址优先;默认空地址、时间列表不限制对应维度,时区为 UTC。路径是实际 URL pathname,包含部署前缀;支持精确路径和末尾 `/**`,GET 不隐含 HEAD。
591
+
592
+ 可信地址通过 `core.access.createClientIpResolver()` 解析。默认 `auto` 在普通宿主忽略转发头,仅受管 Compose 的私有代理拓扑采用容器网段;`[]` 显式关闭代理信任,自管入口填入真实代理 IP/CIDR。不得把任意请求头当作可信 socket 地址。检查器不在请求中读文件、解析 CIDR 或扫描全部路径规则;同一时区的分钟结果复用,跨分钟仍重新判定。`core.access` 仅在 Node 入口存在,浏览器不提供伪实现。公开配置 Schema 由 `core-config` 入口转出,类型集中于 `core-types`,函数通过 `core-main` 注册的 `core.access` 调用。
593
+
594
+ 路径规则的 `encryption` 可选 `required` 或 `disabled`,省略时沿用宿主。选择器复用路径索引,不计算 IP/时间。重叠路径与方法若声明相反模式,创建检查器时失败,与声明顺序无关;同模式或不同方法可以组合。`check.requiresEncryption` 告知宿主是否必须装配传输加密,Core 本身不持有密钥或执行加解密。关闭应用层加密仍需 HTTPS、身份鉴别和访问检查。
595
+
596
+ 完整默认值、代理条件、独立文件、部署和验证步骤见[请求访问控制手册](../../docs/usage/configuration/request-access.md)。
597
+
598
+ ## 配置的实际消费边界
599
+
600
+ `_core.yml` 仅接受 name/version/env/defaultLocale 元数据与 logging;未知字段加载失败。Core 不提供全局 debug、ID 长度/前缀、业务 features 或语言注册列表;日志级别用 logging.level,ID 选项在每次 core.id 调用传入,业务配置放 app.yml 并由应用 Schema 校验。defaultLocale 是可读取的元数据,不会替应用注册翻译或切换 Paraglide。导入 Logger 不改写 LANG、LC_ALL、PYTHONIOENCODING,包括 Windows TTY。全部模块的配置与环境消费链见[配置对应表](../../.agents/skills/hai-module-create/configuration.md)。
601
+
602
+ ## 请求关联与日志观察
603
+
604
+ 采集器内部的持久化/上传路径可使用 `core.context.run({ suppressLogObservers: true }, operation)` 暂停观察回调,避免异步采集自身日志;stdout 等常规日志输出保持生效,退出作用域后恢复。
605
+
606
+ `core.logger.observe` 观察当前级别允许且已执行默认/自定义字段脱敏的记录,返回取消函数。订阅者应只做有界内存收集,再异步持久化;日志观察本身不代表已经保存。回调异常和同步重入不影响业务。自由文本与图像仍需采集侧的专门隐私策略。
607
+
608
+ `core.context.run` 在 Node 中用 AsyncLocalStorage 隔离并发请求和子任务,现有模块 logger 自动附带当前 requestId/userId/scopeId/jobId。身份范围只来自可信服务端代码;关联 ID 不是授权。浏览器保持同一 API,但 run 只建立同步作用域,不能用于跨 await 的身份隔离。
609
+
610
+ ```ts
611
+ import type { LogRecord } from '@h-ai/core'
612
+ import { core } from '@h-ai/core'
613
+
614
+ const records: LogRecord[] = []
615
+ const unsubscribe = core.logger.observe((record) => {
616
+ if (records.length === 100)
617
+ records.shift()
618
+ records.push(record)
619
+ })
620
+ await core.context.run({ requestId: 'request-1', scopeId: 'tenant-1', userId: 'user-1' }, async () => {
621
+ await Promise.resolve()
622
+ core.logger.warn('Business operation failed')
623
+ })
624
+ unsubscribe()
625
+ ```
626
+
627
+ ## 应用生命周期编排
628
+
629
+ `core.module.createRuntime([{ name, enabled?, start, close }])` 按声明启动、反序关闭,合并并发调用。启动前登记部分资源的清理义务;失败回滚。关闭失败停在该模块并保留依赖,下次 close/start 重试,避免先关闭仍被使用的数据库。错误仅保留模块、阶段、错误码和配置字段路径;不转发配置值或 SDK 原始异常。base/API/fullstack 和管理台均使用此入口。
630
+
631
+ 启动错误包含配置原因和处理建议:提供 `ext.fields` 时,`HaiConfigError.CONFIG_ENV_VAR_MISSING` 提示环境变量未设置,`CONFIG_VALIDATION_ERROR` 或 Schema 校验异常提示校验未通过;只有字段路径时提示“配置项缺失或无效”,不猜测具体原因。即使启动边界只打印 `error.message`,也能看到字段、处理建议和底层错误码。自定义初始化钩子可在缺少必需环境变量时抛出带 `code: HaiConfigError.CONFIG_ENV_VAR_MISSING.code` 和 `ext: { fields: ['变量名'] }` 的异常;fullstack 模板的初始管理员密码检查采用此方式。
632
+
633
+ ## 错误提示与关联日志
634
+
635
+ HTTP 错误返回本地化安全文案及追踪编号;ToastContainer 默认隐藏编号,由排查信息图标展开和复制。响应同时包含 `error.traceId` 和 `X-Trace-Id`。框架复用本次 requestId 作为 traceId,前后端日志自动关联。底层异常、堆栈和操作上下文经凭据脱敏后写入日志,不通过 cause/ext 向用户公开。字段校验只返回本地化的字段名和错误。
636
+
637
+ `core.error.toPublic(error, traceId?)` 用于接口/UI 边界;`core.error.formatMessage(error)` 用于重新翻译后的提示,重复调用不会重复追加标识。日志传入完整 Error 对象,不能只记录通用标题或提前 String(error) 丢失上下文。数据库失败记录驱动错误码、消息、堆栈及表/列;SQL 原文、绑定值和连接凭据不进入日志。
638
+
639
+ ### 公开业务进度
640
+
641
+ 内部 ext 默认不公开。只有明确批准且经过 Schema 校验的业务进度才使用 publicExt,由边界转换为 ext;不能放入内部异常。
642
+
643
+ ## 开发测试
415
644
 
416
645
  ```bash
417
646
  pnpm --filter @h-ai/core test
418
647
  ```
419
648
 
420
- ## License
649
+ 公开声明和内部结构的注释及可编译示例见[模块 JSDoc 约定](../../.agents/instructions/module-conventions.md#jsdoc-与示例验证)。Node core.init 同步返回 void;单项 config.load 返回失败时记录错误并继续,必需配置须 validate 并检查 success。目录扫描等异常仍可能抛出;模块业务 HaiResult 则先检查 success。
650
+
651
+ ## 许可证
421
652
 
422
653
  Apache-2.0
package/dist/browser.d.ts CHANGED
@@ -1,43 +1,23 @@
1
- import { C as CoreOptions, b as CoreLogger, I as I18nFn, c as IdFn, T as TypeUtilFn, O as ObjectFn, S as StringFn, A as ArrayFn, d as AsyncFn, e as TimeFn, E as ErrorFn, M as ModuleFn } from './core-util-module-B-wH5_ZT.js';
2
- export { f as CoreConfig, g as CoreConfigSchema, h as CoreFunctions, i as Env, j as EnvSchema, k as ErrorInfo, l as ErrorInfoValue, m as HaiCommonError, n as HaiConfigError, H as HaiError, o as HaiErrorDef, a as HaiResult, p as IdConfig, q as IdConfigSchema, r as InterpolationParams, L as Locale, s as LocaleInfo, t as LocaleMessages, u as LogContext, v as LogFormat, w as LogFormatSchema, x as LogLevel, y as LogLevelSchema, z as Logger, B as LoggerFunctions, D as LoggerOptions, F as LoggingConfig, G as LoggingConfigSchema, J as MatchHandlers, K as MessageDictionary, N as MessageOptions, P as PaginatedResult, Q as PaginationOptions, R as PaginationOptionsInput, U as err, V as ok } from './core-util-module-B-wH5_ZT.js';
1
+ import { B as BrowserCoreFunctions } from './core-types-_JqVAGGT.js';
2
+ export { A as AccessCondition, a as AccessConditionSchema, b as AccessDecision, c as AccessFunctions, d as ArrayFn, e as AsyncFn, f as BrowserConfigFn, C as ConfigFn, g as CoreConfig, h as CoreConfigSchema, i as CoreFunctions, j as CoreLogger, k as CoreOptions, l as CreateNotInitializedKitFn, E as Env, m as EnvSchema, n as ErrorFn, o as ErrorInfo, p as ErrorInfoValue, H as HaiCommonError, q as HaiConfigError, r as HaiError, s as HaiErrorDef, t as HaiResult, I as I18nFn, u as IdFn, v as InterpolationParams, L as Locale, w as LocaleInfo, x as LocaleMessages, y as LogContext, z as LogFormat, D as LogFormatSchema, F as LogLevel, G as LogLevelSchema, J as LogObserver, K as LogRecord, M as Logger, N as LoggerFunctions, O as LoggerOptions, P as LoggingConfig, Q as LoggingConfigSchema, R as MatchHandlers, S as MessageDictionary, T as MessageOptions, U as ModuleFn, V as ModuleRuntime, W as NodeCoreFunctions, X as NotInitializedKit, Y as ObjectFn, Z as PaginatedResult, _ as PaginationOptions, $ as PaginationOptionsInput, a0 as RequestAccessCheck, a1 as RequestAccessConfigInput, a2 as RequestAccessConfigSchema, a3 as RequestAccessPolicy, a4 as RequestAccessPolicySchema, a5 as RequestContext, a6 as RequestEncryptionMode, a7 as RuntimeStep, a8 as SanitizeFn, a9 as SanitizeSensitiveFieldsOptions, aa as SensitiveKeyMatcher, ab as StringFn, ac as TimeFn, ad as TrustedProxies, ae as TrustedProxiesSchema, af as TypeUtilFn, ag as ValidationFormError, ah as WatchCallback, ai as ZodMessageGetter, aj as ZodValidationFn, ak as ZodValidationMessageKey, al as err, am as ok } from './core-types-_JqVAGGT.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
6
- * Core 服务对象 - 聚合常用功能(浏览器)。
6
+ * @h-ai/core — Core 服务聚合(浏览器)
7
7
  *
8
- * @example
9
- * ```ts
10
- * import { core } from '@h-ai/core'
11
- * core.logger.info('browser ready')
12
- * ```
8
+ * 提供浏览器环境的 core 对象,聚合常用功能。 所有功能统一通过 core 对象访问;文件配置方法保留明确失败的占位接口。
9
+ * @module core-main.browser
13
10
  */
14
- declare const core: {
15
- /** 初始化 Core */
16
- init: typeof initCore;
17
- logger: CoreLogger;
18
- i18n: I18nFn;
19
- id: IdFn;
20
- typeUtils: TypeUtilFn;
21
- object: ObjectFn;
22
- string: StringFn;
23
- array: ArrayFn;
24
- async: AsyncFn;
25
- time: TimeFn;
26
- error: ErrorFn;
27
- module: ModuleFn;
28
- };
11
+
29
12
  /**
30
- * 初始化 Core(内部实现,通过 core.init() 调用)。
31
- * 注意:浏览器环境不支持 config 和 watchConfig 选项。
32
- *
33
- * @param options - 初始化选项
34
- *
13
+ * Core 服务对象 - 聚合常用功能(浏览器)。
35
14
  * @example
36
15
  * ```ts
37
16
  * import { core } from '@h-ai/core'
38
- * core.init({ logging: { level: 'debug' } })
17
+ * core.init({ logging: { level: 'info' } })
18
+ * core.logger.info('Browser ready')
39
19
  * ```
40
20
  */
41
- declare function initCore(options?: CoreOptions): void;
21
+ declare const core: BrowserCoreFunctions;
42
22
 
43
- export { CoreLogger, CoreOptions, core };
23
+ export { BrowserCoreFunctions, core };