@h-ai/core 0.1.0-alpha.66 → 0.1.0-alpha.68

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.en.md CHANGED
@@ -4,9 +4,11 @@
4
4
 
5
5
  [Framework home](../../README.en.md)
6
6
 
7
- > The core of hai Agent Framework: shared utilities, types, configuration management, and logging.
7
+ ## Introduction
8
8
 
9
- ## Capabilities
9
+ The core of hai Agent Framework: shared utilities, types, configuration management, and logging.
10
+
11
+ ## Core features
10
12
 
11
13
  - **HaiResult types**: functional error handling (`ok` / `err`) with a shared result format across modules.
12
14
  - **Unified logging**: pino on Node.js and loglevel in browsers, with the same API.
@@ -18,9 +20,11 @@
18
20
  - **Module utilities**: NotInitializedKit proxies and shared lifecycle infrastructure.
19
21
  - **Utility library**: type checks and object / string / array / async / time operations.
20
22
 
21
- ## Quick start
23
+ ## Getting started
24
+
25
+ ### Quick start
22
26
 
23
- ### Node.js environment
27
+ #### Node.js environment
24
28
 
25
29
  ```typescript
26
30
  import { core } from '@h-ai/core'
@@ -72,7 +76,7 @@ core.time.formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
72
76
  core.typeUtils.isDefined(value)
73
77
  ```
74
78
 
75
- ### Browser environment
79
+ #### Browser environment
76
80
 
77
81
  ```typescript
78
82
  import { core } from '@h-ai/core'
@@ -95,13 +99,13 @@ The browser retains the `core.config` API shape for type compatibility, but does
95
99
  - `watch()` immediately calls its callback with the same error and returns a safe no-op unsubscribe function.
96
100
  - Read configuration on the server and deliver it through an API, or use `core.init({ logging })` only to configure browser logging.
97
101
 
98
- ## Detailed API
102
+ ### Detailed API
99
103
 
100
- ### Logging (Logger)
104
+ #### Logging (Logger)
101
105
 
102
106
  `core.logger` is both the default Logger instance and the shared entry point for logger management.
103
107
 
104
- #### Write logs
108
+ ##### Write logs
105
109
 
106
110
  Six levels: `trace` / `debug` / `info` / `warn` / `error` / `fatal`.
107
111
 
@@ -111,7 +115,7 @@ core.logger.error('Failed to connect', { error })
111
115
  core.logger.debug('Processing item', { id, step: 'validation' })
112
116
  ```
113
117
 
114
- #### Create a module logger
118
+ ##### Create a module logger
115
119
 
116
120
  ```typescript
117
121
  // 独立名称空间,适合模块内全局使用
@@ -119,7 +123,7 @@ const logger = core.logger.create({ name: 'payment' })
119
123
  logger.info('Payment processed')
120
124
  ```
121
125
 
122
- #### Child loggers
126
+ ##### Child loggers
123
127
 
124
128
  ```typescript
125
129
  // 携带固定上下文,每条日志自动附带这些字段
@@ -135,7 +139,7 @@ opLogger.error('Login failed', { reason: 'invalid_password' })
135
139
 
136
140
  > **Typical usage**: create `reqLogger` at the request entry, pass it to the service layer, and add operation context with `child`. Logs then include the complete request context.
137
141
 
138
- #### Logger management
142
+ ##### Logger management
139
143
 
140
144
  ```typescript
141
145
  // 配置输出格式和级别(Node.js / Browser)
@@ -154,9 +158,11 @@ After `core.init()` loads `_core.yml.logging`, or after `configure` / `setLevel`
154
158
 
155
159
  Browser logs still use the DevTools console: `pretty` prints readable messages with context, while `json` prints a JSON string containing `level` / `msg`. Default credential masking and configured path redaction both apply, including nested dot/bracket paths and `*`. Callers remain responsible for redacting message text.
156
160
 
157
- `core.logger` redacts log context by default: credentials such as `password`, `token`, `secret`, `privateKey`, and `apiV3Key` become `[REDACTED]`. Fields such as `url` / `baseUrl` / `endpoint` mask only embedded usernames/passwords. Email addresses and phone numbers are not masked by default.
161
+ Native `Error` values in log context are copied into diagnostics containing `name`, `message`, `stack`, `cause`, `AggregateError.errors`, and enumerable own context before default field/URL masking and configured `redact` paths run. Both backends use the same rules and leave the original error unchanged. Error-subgraph cycles become `[Circular]` and the 32-level limit becomes `[MaxDepth]`; custom replacements in these subgraphs follow the same normalization, while ordinary-object replacement behavior is unchanged. Ordinary-object cycles without an Error retain their original structure and are not guaranteed to be directly JSON-serializable. `name/message/stack` are free text: secrets embedded in them are not automatically parsed. Clean them before logging or mask whole paths such as `error.message` / `error.stack`.
158
162
 
159
- #### Log level conventions
163
+ `core.logger` redacts log context by default: credentials such as `password`, `token`, `secret`, `privateKey`, and `apiV3Key` become `[REDACTED]`. Fields such as `url` / `baseUrl` / `endpoint` mask embedded usernames/passwords, sensitive query parameters, and OAuth key/value fragments; malformed URLs return a fixed placeholder. Email addresses and phone numbers are not masked by default.
164
+
165
+ ##### Log level conventions
160
166
 
161
167
  | Level | Use case |
162
168
  | ------- | --------------------------------------------------- |
@@ -167,7 +173,9 @@ Browser logs still use the DevTools console: `pretty` prints readable messages w
167
173
  | `error` | Operation failures requiring investigation |
168
174
  | `fatal` | Fatal errors preventing continued service |
169
175
 
170
- ### Internationalization (i18n)
176
+ #### Internationalization (i18n)
177
+
178
+ Built-in locales are `zh-CN`, `en-US` and `ja-JP`. The `ja` alias normalizes to `ja-JP`; all modules share this setting.
171
179
 
172
180
  ```typescript
173
181
  // 设置全局语言(所有 getMessage 函数自动响应)
@@ -187,13 +195,13 @@ getMessage('welcome', { params: { name: 'Alice' } }) // 'Welcome Alice'
187
195
  getMessage('welcome', { locale: 'zh-CN', params: { name: 'Alice' } }) // '欢迎 Alice'
188
196
 
189
197
  // 常量
190
- 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: '日本語' }]
191
199
  core.i18n.DEFAULT_LOCALE // 'zh-CN'
192
200
  ```
193
201
 
194
- ### Redaction utilities
202
+ #### Redaction utilities
195
203
 
196
- `core.sanitize.sanitizeSensitiveFields()` recursively redacts objects and arrays. By default it handles password, token, secret, privateKey, apiV3Key, and similar credentials, and masks embedded credentials in `url` / `baseUrl` / `endpoint`. Modules can supply custom matchers.
204
+ `core.sanitize.sanitizeSensitiveFields()` recursively redacts objects and arrays. By default it handles password, token, secret, privateKey, apiV3Key, and similar credentials, and masks embedded credentials in `url` / `baseUrl` / `endpoint`. `core.sanitize.sanitizeUrl(url, options)` also handles individual URLs and returns `(invalid url)` for malformed input. Custom matchers apply to parameter names as well.
197
205
 
198
206
  ```typescript
199
207
  core.sanitize.sanitizeSensitiveFields({
@@ -211,7 +219,7 @@ core.sanitize.sanitizeSensitiveFields({ email: 'user@example.com' }, {
211
219
  // => { email: '***' }
212
220
  ```
213
221
 
214
- ### Zod validation error localization
222
+ #### Zod validation error localization
215
223
 
216
224
  `core.zodValidation` converts Zod `SafeParseError` / `ZodError` into flat form errors and maps Zod's
217
225
  default English messages to the caller's own localized messages.
@@ -272,17 +280,19 @@ if (!result.success) {
272
280
  }
273
281
  ```
274
282
 
275
- ### Configuration management (Node.js only)
283
+ #### Configuration management (Node.js only)
276
284
 
277
285
  > Browsers have no filesystem access. `core.config` offers placeholder APIs only: file-related methods return `SERVICE_UNAVAILABLE` and do not load YAML.
278
286
 
287
+ Node automatically loads and watches runtime `.yml` / `.yaml` files, excluding exactly `_deploy.yml` and `_deploy.yaml`. The deployment CLI parses those reserved files after loading its target environment and credentials. Business `deploy.yml` and explicit `core.config.load` remain supported. A failed per-file load result is logged and loading continues; applications must validate required configuration and check `success`. Directory scanning, logger setup and other exceptions may still propagate.
288
+
279
289
  ```typescript
280
290
  import { core, HaiConfigError } from '@h-ai/core'
281
291
  import { z } from 'zod'
282
292
 
283
- // 方式 1:init 时自动加载整个配置目录
293
+ // 方式 1:init 时自动加载运行时配置
284
294
  core.init({
285
- configDir: './config', // 扫描该目录所有 .yml 文件
295
+ configDir: './config', // 扫描 .yml / .yaml,排除 _deploy.yml / _deploy.yaml
286
296
  watchConfig: true, // 监听变更并自动重载
287
297
  })
288
298
 
@@ -304,8 +314,8 @@ const validResult = core.config.validate('app', AppSchema)
304
314
 
305
315
  // 查询和获取
306
316
  if (core.config.has('app')) {
307
- const cfg = core.config.get<typeof AppSchema>('app') // undefined 或配置
308
- const cfg2 = core.config.getOrThrow<typeof AppSchema>('app') // 未加载时抛异常
317
+ const cfg = core.config.get<z.infer<typeof AppSchema>>('app') // undefined 或配置
318
+ const cfg2 = core.config.getOrThrow<z.infer<typeof AppSchema>>('app') // 未加载时抛异常
309
319
  }
310
320
  core.config.keys() // ['core', 'app', 'db', ...]
311
321
 
@@ -327,7 +337,7 @@ core.config.clear('app') // 清除单个
327
337
  core.config.clear() // 清除全部
328
338
  ```
329
339
 
330
- #### Dotenv files and environment modes
340
+ ##### Dotenv files and environment modes
331
341
 
332
342
  Node-only `core.config.loadEnvFiles({ mode, envDir })` returns HaiResult and merges `.env` → `.env.<mode>` → `.env.local`, with existing process variables taking priority. development/production map to dev/prod; missing files are skipped. It does not mutate the process: check success before assigning. Track public environment files and `.env.example`; ignore private `.env.local`. core.init does not load dotenv. Browser calls return SERVICE_UNAVAILABLE.
333
343
 
@@ -339,7 +349,7 @@ Object.assign(process.env, env.data)
339
349
  core.init({ configDir: './config' })
340
350
  ```
341
351
 
342
- #### Environment variable mapping and interpolation
352
+ ##### Environment variable mapping and interpolation
343
353
 
344
354
  `core.config.load(name, ...)` automatically maps each YAML leaf to an
345
355
  environment variable named `HAI_<CONFIG_NAME>_<YAML_PATH>`. Configuration names and keys are uppercased, levels are separated by `_`,
@@ -382,7 +392,7 @@ database: ${DB_CONNECTION} # → 环境变量缺失时报错
382
392
  url: http://${HOST}:${PORT} # → string(混合文本)
383
393
  ```
384
394
 
385
- ### ID generation
395
+ #### ID generation
386
396
 
387
397
  ```typescript
388
398
  const id = core.id.generate() // nanoid 21 字符
@@ -398,7 +408,9 @@ core.id.isValidUUID('f47ac10b-...') // true/false
398
408
  core.id.isValidNanoId('abc123', 6) // true/false
399
409
  ```
400
410
 
401
- ### Utilities
411
+ UUID generation requires cryptographic randomUUID/getRandomValues and throws if neither is available; it never falls back to Math.random.
412
+
413
+ #### Utilities
402
414
 
403
415
  ```typescript
404
416
  // 类型检查
@@ -434,7 +446,7 @@ core.string.isBlank('') // true
434
446
  core.string.isNotBlank('x') // true
435
447
  core.string.padStart('5', 3, '0') // '005'
436
448
  core.string.padEnd('5', 3, '0') // '500'
437
- core.string.constantTimeEqual(a, b) // 防时序攻击比较
449
+ core.string.constantTimeEqual(a, b) // Node 使用原生等长比较;浏览器不保证恒时
438
450
 
439
451
  // 数组操作
440
452
  core.array.unique([1, 1, 2]) // [1, 2]
@@ -470,7 +482,9 @@ core.time.startOfDay(date)
470
482
  core.time.endOfDay(date)
471
483
  ```
472
484
 
473
- ### Module utilities (module)
485
+ The Node entry uses node:crypto.timingSafeEqual over equal-length UTF-16LE code units. Different lengths return false; length is not hidden. The browser implementation avoids content-based early returns but has no JavaScript-engine timing guarantee and is not a server credential boundary. Surrounding branches, encoding and request handling are not made timing-safe by this helper.
486
+
487
+ #### Module utilities (module)
474
488
 
475
489
  Shared initialization error handling for modules:
476
490
 
@@ -488,9 +502,9 @@ const currentDdl: DdlOperations = notInitialized.proxy<DdlOperations>()
488
502
  const currentHash: HashOperations = notInitialized.proxy<HashOperations>('sync')
489
503
  ```
490
504
 
491
- ## Error handling
505
+ ### Error handling
492
506
 
493
- ### HaiResult types
507
+ #### HaiResult types
494
508
 
495
509
  All operations that may fail return `HaiResult<T>`:
496
510
 
@@ -513,7 +527,7 @@ else {
513
527
  }
514
528
  ```
515
529
 
516
- ### Custom error codes (per module)
530
+ #### Custom error codes (per module)
517
531
 
518
532
  ```typescript
519
533
  import type { ErrorInfo } from '@h-ai/core'
@@ -539,7 +553,7 @@ const errorInst = core.error.buildHaiErrorInst(
539
553
  )
540
554
  ```
541
555
 
542
- ### Configuration error codes
556
+ #### Configuration error codes
543
557
 
544
558
  ```typescript
545
559
  import { HaiConfigError } from '@h-ai/core'
@@ -551,12 +565,24 @@ HaiConfigError.CONFIG_ENV_VAR_MISSING // hai:core:013 - 环境变量缺失
551
565
  HaiConfigError.CONFIG_NOT_LOADED // hai:core:014 - 配置未加载
552
566
  ```
553
567
 
554
- ## Tests
568
+ ### Request locale isolation
569
+
570
+ Wrap Node requests and SSR in `core.i18n.runWithLocale(locale, () => handleRequest())`. SvelteKit can use `kit.i18n.runWithLocale(locale, () => resolve(event))`; serv creates the scope automatically from request headers. Async descendants inherit it. Explicit `MessageOptions.locale` takes precedence over the request locale and the startup default. Within a scope, `setGlobalLocale` only changes that request; outside a scope it configures the startup default and must not be called by request hooks.
571
+
572
+ Browsers keep application-level `setGlobalLocale` / `createLocaleStore`. During SSR, UI store initialization and setters only update the store; messages read the request scope. `runWithLocale` requires the Node entry; browser translations use explicit locale options or application state.
573
+
574
+ ## Configuration consumption
575
+
576
+ `_core.yml` accepts only name/version/env/defaultLocale metadata and logging; unknown fields fail loading. Core has no global debug, ID length/prefix, business features or locale registration list. Use logging.level, per-call core.id arguments, and application-owned app.yml with its own schema. defaultLocale is metadata, not automatic translation registration or a Paraglide switch. Importing Logger preserves LANG, LC_ALL and PYTHONIOENCODING, including Windows TTY. See the [configuration-to-consumer map (Chinese)](../../.agents/skills/hai-module-create/configuration.md).
577
+
578
+ ## Development and testing
555
579
 
556
580
  ```bash
557
581
  pnpm --filter @h-ai/core test
558
582
  ```
559
583
 
584
+ See the [module JSDoc conventions (Chinese)](../../.agents/instructions/module-conventions.md#jsdoc-与示例验证) for declaration comments and compilable examples. Node core.init is synchronous and returns void. A failed per-file config.load result is logged and loading continues; validate required configuration and check success. Directory scanning and other exceptions may still throw. Business HaiResult APIs require a success check.
585
+
560
586
  ## License
561
587
 
562
588
  Apache-2.0
package/README.ja.md CHANGED
@@ -4,7 +4,9 @@
4
4
 
5
5
  [フレームワークのホーム](../../README.ja.md)
6
6
 
7
- > hai Agent Framework の中核モジュール。共通ツール、型定義、設定管理、ログ機能を提供します。
7
+ ## 概要
8
+
9
+ hai Agent Framework の中核モジュール。共通ツール、型定義、設定管理、ログ機能を提供します。
8
10
 
9
11
  ## 主な機能
10
12
 
@@ -18,9 +20,11 @@
18
20
  - **モジュールツール**:NotInitializedKit のプロキシと共通のライフサイクル基盤。
19
21
  - **ツール関数**:型確認、オブジェクト・文字列・配列・非同期・時刻の操作。
20
22
 
21
- ## クイックスタート
23
+ ## 使い始める
24
+
25
+ ### クイックスタート
22
26
 
23
- ### Node.js 環境
27
+ #### Node.js 環境
24
28
 
25
29
  ```typescript
26
30
  import { core } from '@h-ai/core'
@@ -72,7 +76,7 @@ core.time.formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
72
76
  core.typeUtils.isDefined(value)
73
77
  ```
74
78
 
75
- ### ブラウザー環境
79
+ #### ブラウザー環境
76
80
 
77
81
  ```typescript
78
82
  import { core } from '@h-ai/core'
@@ -95,13 +99,13 @@ core.array.unique([1, 1, 2])
95
99
  - `watch()` は同じエラーで直ちにコールバックを呼び、安全な no-op の監視解除関数を返します。
96
100
  - 設定が必要な場合はサーバーで読み込み API で渡すか、ブラウザーでは `core.init({ logging })` でログのみ設定してください。
97
101
 
98
- ## API の詳細
102
+ ### API の詳細
99
103
 
100
- ### ログ(Logger)
104
+ #### ログ(Logger)
101
105
 
102
106
  `core.logger` は既定の Logger インスタンスであり、ログ管理の共通入口でもあります。
103
107
 
104
- #### ログの記録
108
+ ##### ログの記録
105
109
 
106
110
  6 つのレベル:`trace` / `debug` / `info` / `warn` / `error` / `fatal`。
107
111
 
@@ -111,7 +115,7 @@ core.logger.error('Failed to connect', { error })
111
115
  core.logger.debug('Processing item', { id, step: 'validation' })
112
116
  ```
113
117
 
114
- #### モジュール用 logger の作成
118
+ ##### モジュール用 logger の作成
115
119
 
116
120
  ```typescript
117
121
  // 独立名称空间,适合模块内全局使用
@@ -119,7 +123,7 @@ const logger = core.logger.create({ name: 'payment' })
119
123
  logger.info('Payment processed')
120
124
  ```
121
125
 
122
- #### 子 logger(child)
126
+ ##### 子 logger(child)
123
127
 
124
128
  ```typescript
125
129
  // 携带固定上下文,每条日志自动附带这些字段
@@ -135,7 +139,7 @@ opLogger.error('Login failed', { reason: 'invalid_password' })
135
139
 
136
140
  > **典型的な使い方**:リクエスト入口で `reqLogger` を作成してサービス層に渡し、サービス層で `child` に操作コンテキストを追加します。ログに処理全体のコンテキストが自動的に含まれます。
137
141
 
138
- #### ログの管理
142
+ ##### ログの管理
139
143
 
140
144
  ```typescript
141
145
  // 配置输出格式和级别(Node.js / Browser)
@@ -154,9 +158,11 @@ const level = core.logger.getLevel() // 'debug'
154
158
 
155
159
  ブラウザーでは引き続き DevTools console に出力します。`pretty` は読みやすいメッセージと context、`json` は `level` / `msg` を含む JSON 文字列です。既定の認証情報マスキングと設定したパスのマスキングを併用し、ネストしたドット・括弧パスと `*` に対応します。メッセージ本文の機密情報は呼び出し側で除去してください。
156
160
 
157
- `core.logger` は既定でログの機密情報をマスクします。`password`、`token`、`secret`、`privateKey`、`apiV3Key` などは `[REDACTED]` に置換されます。`url` / `baseUrl` / `endpoint` は URL 内のユーザー名・パスワードのみをマスクします。メールアドレス・電話番号は既定では隠しません。
161
+ ログ文脈のネイティブ `Error` は、`name`、`message`、`stack`、`cause`、`AggregateError.errors` と列挙可能な自身の文脈を持つコピーに変換してから、既定のフィールド/URL マスクと設定済み `redact` を適用します。両環境で同じ規則を使い、元の例外は変更しません。例外サブグラフの循環は `[Circular]`、32 階層の上限は `[MaxDepth]` になり、この範囲の独自置換値にも同じ正規化を適用します。通常オブジェクトの置換動作は変えません。Error を含まない通常オブジェクトの循環は元の構造を保持し、直接 JSON 化できる保証はありません。`name/message/stack` は自由文で、含まれる秘密を自動解析しません。記録前に除去するか、`error.message` / `error.stack` などのパス全体を隠してください。
158
162
 
159
- #### ログレベルの規約
163
+ `core.logger` は既定でログの機密情報をマスクします。`password`、`token`、`secret`、`privateKey`、`apiV3Key` などは `[REDACTED]` に置換されます。`url` / `baseUrl` / `endpoint` は URL 内のユーザー名・パスワード、機密クエリ値、OAuth 形式の fragment をマスクします。不正な URL は固定値を返します。メールアドレス・電話番号は既定では隠しません。
164
+
165
+ ##### ログレベルの規約
160
166
 
161
167
  | レベル | 用途 |
162
168
  | ------- | ---------------------------------------- |
@@ -167,7 +173,9 @@ const level = core.logger.getLevel() // 'debug'
167
173
  | `error` | 調査が必要な操作の失敗 |
168
174
  | `fatal` | サービス継続不能の致命的エラー |
169
175
 
170
- ### 国際化(i18n)
176
+ #### 国際化(i18n)
177
+
178
+ 組み込みの言語は `zh-CN`、`en-US`、`ja-JP` です。略記 `ja` は `ja-JP` に正規化され、すべてのモジュールで設定を共有します。
171
179
 
172
180
  ```typescript
173
181
  // 设置全局语言(所有 getMessage 函数自动响应)
@@ -187,13 +195,13 @@ getMessage('welcome', { params: { name: 'Alice' } }) // 'Welcome Alice'
187
195
  getMessage('welcome', { locale: 'zh-CN', params: { name: 'Alice' } }) // '欢迎 Alice'
188
196
 
189
197
  // 常量
190
- 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: '日本語' }]
191
199
  core.i18n.DEFAULT_LOCALE // 'zh-CN'
192
200
  ```
193
201
 
194
- ### 機密情報のマスク
202
+ #### 機密情報のマスク
195
203
 
196
- `core.sanitize.sanitizeSensitiveFields()` はオブジェクト・配列内の機密情報を再帰的にマスクします。既定では password、token、secret、privateKey、apiV3Key などを処理し、`url` / `baseUrl` / `endpoint` の認証情報もマスクします。独自の matcher も指定できます。
204
+ `core.sanitize.sanitizeSensitiveFields()` はオブジェクト・配列内の機密情報を再帰的にマスクします。既定では password、token、secret、privateKey、apiV3Key などを処理し、`url` / `baseUrl` / `endpoint` の認証情報もマスクします。`core.sanitize.sanitizeUrl(url, options)` は単独の URL にも使用でき、不正な URL は `(invalid url)` を返します。独自の matcher はクエリ名にも適用されます。
197
205
 
198
206
  ```typescript
199
207
  core.sanitize.sanitizeSensitiveFields({
@@ -211,7 +219,7 @@ core.sanitize.sanitizeSensitiveFields({ email: 'user@example.com' }, {
211
219
  // => { email: '***' }
212
220
  ```
213
221
 
214
- ### Zod 検証エラーの i18n 変換
222
+ #### Zod 検証エラーの i18n 変換
215
223
 
216
224
  `core.zodValidation` は Zod の `SafeParseError` / `ZodError` をフラットなフォームエラーに変換し、
217
225
  Zod の既定の英語メッセージを呼び出し元の i18n 文言に変換します。
@@ -272,17 +280,19 @@ if (!result.success) {
272
280
  }
273
281
  ```
274
282
 
275
- ### 設定管理(Node.js 専用)
283
+ #### 設定管理(Node.js 専用)
276
284
 
277
285
  > ブラウザーにはファイルシステムがないため、`core.config` は代替 API の形状のみを提供します。ファイル関連メソッドは `SERVICE_UNAVAILABLE` を返し、YAML を読み込みません。
278
286
 
287
+ Node は実行時の `.yml` / `.yaml` を自動読み込み・監視しますが、予約名 `_deploy.yml` と `_deploy.yaml` は除外します。これらはデプロイ CLI が対象環境と認証情報の読み込み後に個別解析します。業務用 `deploy.yml` と明示的な `core.config.load` は引き続き利用できます。単一ファイルの失敗結果はログに記録して続行するため、アプリは必須設定を validate して `success` を確認してください。ディレクトリー走査や Logger 設定などの例外は伝播する場合があります。
288
+
279
289
  ```typescript
280
290
  import { core, HaiConfigError } from '@h-ai/core'
281
291
  import { z } from 'zod'
282
292
 
283
- // 方式 1:init 时自动加载整个配置目录
293
+ // 方式 1:init 时自动加载运行时配置
284
294
  core.init({
285
- configDir: './config', // 扫描该目录所有 .yml 文件
295
+ configDir: './config', // 扫描 .yml / .yaml,排除 _deploy.yml / _deploy.yaml
286
296
  watchConfig: true, // 监听变更并自动重载
287
297
  })
288
298
 
@@ -304,8 +314,8 @@ const validResult = core.config.validate('app', AppSchema)
304
314
 
305
315
  // 查询和获取
306
316
  if (core.config.has('app')) {
307
- const cfg = core.config.get<typeof AppSchema>('app') // undefined 或配置
308
- const cfg2 = core.config.getOrThrow<typeof AppSchema>('app') // 未加载时抛异常
317
+ const cfg = core.config.get<z.infer<typeof AppSchema>>('app') // undefined 或配置
318
+ const cfg2 = core.config.getOrThrow<z.infer<typeof AppSchema>>('app') // 未加载时抛异常
309
319
  }
310
320
  core.config.keys() // ['core', 'app', 'db', ...]
311
321
 
@@ -327,7 +337,7 @@ core.config.clear('app') // 清除单个
327
337
  core.config.clear() // 清除全部
328
338
  ```
329
339
 
330
- #### dotenv ファイルと環境切り替え
340
+ ##### dotenv ファイルと環境切り替え
331
341
 
332
342
  Node 専用の `core.config.loadEnvFiles({ mode, envDir })` は HaiResult を返し、`.env` → `.env.<mode>` → `.env.local` の順で読み込みます。既存のプロセス変数が最優先です。development/production は dev/prod に対応し、存在しないファイルは省略します。プロセスは変更しないため、success を確認してから代入します。公開設定と `.env.example` は Git 登録し、私有の `.env.local` は除外します。core.init は dotenv を読み込まず、ブラウザーでは SERVICE_UNAVAILABLE を返します。
333
343
 
@@ -339,7 +349,7 @@ Object.assign(process.env, env.data)
339
349
  core.init({ configDir: './config' })
340
350
  ```
341
351
 
342
- #### 環境変数のマッピングと展開
352
+ ##### 環境変数のマッピングと展開
343
353
 
344
354
  `core.config.load(name, ...)` は YAML の各末端設定を
345
355
  `HAI_<CONFIG_NAME>_<YAML_PATH>` の環境変数に自動対応付けします。設定名・各 key は大文字化し、階層は `_` で区切り、
@@ -382,7 +392,7 @@ database: ${DB_CONNECTION} # → 环境变量缺失时报错
382
392
  url: http://${HOST}:${PORT} # → string(混合文本)
383
393
  ```
384
394
 
385
- ### ID 生成
395
+ #### ID 生成
386
396
 
387
397
  ```typescript
388
398
  const id = core.id.generate() // nanoid 21 字符
@@ -398,7 +408,9 @@ core.id.isValidUUID('f47ac10b-...') // true/false
398
408
  core.id.isValidNanoId('abc123', 6) // true/false
399
409
  ```
400
410
 
401
- ### ツール関数
411
+ UUID は暗号学的乱数の randomUUID/getRandomValues のみを使用し、どちらも利用できない場合は例外を送出します。Math.random にはフォールバックしません。
412
+
413
+ #### ツール関数
402
414
 
403
415
  ```typescript
404
416
  // 类型检查
@@ -434,7 +446,7 @@ core.string.isBlank('') // true
434
446
  core.string.isNotBlank('x') // true
435
447
  core.string.padStart('5', 3, '0') // '005'
436
448
  core.string.padEnd('5', 3, '0') // '500'
437
- core.string.constantTimeEqual(a, b) // 防时序攻击比较
449
+ core.string.constantTimeEqual(a, b) // Node 使用原生等长比较;浏览器不保证恒时
438
450
 
439
451
  // 数组操作
440
452
  core.array.unique([1, 1, 2]) // [1, 2]
@@ -470,7 +482,9 @@ core.time.startOfDay(date)
470
482
  core.time.endOfDay(date)
471
483
  ```
472
484
 
473
- ### モジュール共通ツール(module)
485
+ Node 入口は元の UTF-16 コード単位を UTF-16LE に変換し、同じバイト長を node:crypto.timingSafeEqual で比較します。長さが違えば false となり、長さ自体は隠しません。ブラウザー実装には JavaScript エンジンの定時間保証がなく、サーバーの認証境界には使用しません。周辺の分岐・変換・リクエスト処理まで定時間になるわけではありません。
486
+
487
+ #### モジュール共通ツール(module)
474
488
 
475
489
  モジュール共通の初期化エラー処理:
476
490
 
@@ -488,9 +502,9 @@ const currentDdl: DdlOperations = notInitialized.proxy<DdlOperations>()
488
502
  const currentHash: HashOperations = notInitialized.proxy<HashOperations>('sync')
489
503
  ```
490
504
 
491
- ## エラー処理
505
+ ### エラー処理
492
506
 
493
- ### HaiResult 型
507
+ #### HaiResult 型
494
508
 
495
509
  失敗する可能性がある操作は `HaiResult<T>` を返します:
496
510
 
@@ -513,7 +527,7 @@ else {
513
527
  }
514
528
  ```
515
529
 
516
- ### 独自のエラーコード(モジュール単位)
530
+ #### 独自のエラーコード(モジュール単位)
517
531
 
518
532
  ```typescript
519
533
  import type { ErrorInfo } from '@h-ai/core'
@@ -539,7 +553,7 @@ const errorInst = core.error.buildHaiErrorInst(
539
553
  )
540
554
  ```
541
555
 
542
- ### 設定エラーコード
556
+ #### 設定エラーコード
543
557
 
544
558
  ```typescript
545
559
  import { HaiConfigError } from '@h-ai/core'
@@ -551,12 +565,24 @@ HaiConfigError.CONFIG_ENV_VAR_MISSING // hai:core:013 - 环境变量缺失
551
565
  HaiConfigError.CONFIG_NOT_LOADED // hai:core:014 - 配置未加载
552
566
  ```
553
567
 
554
- ## テスト
568
+ ### リクエスト言語の分離
569
+
570
+ Node リクエストと SSR は `core.i18n.runWithLocale(locale, () => handleRequest())` で囲みます。SvelteKit は `kit.i18n.runWithLocale(locale, () => resolve(event))` を使えます。serv はリクエストヘッダーから自動設定します。非同期の子処理も言語を継承し、明示的な `MessageOptions.locale`、リクエスト言語、起動時の既定値の順で優先します。コンテキスト内の `setGlobalLocale` はそのリクエストだけを変更します。コンテキスト外の設定は起動時に限定し、リクエスト hook から呼びません。
571
+
572
+ ブラウザーはアプリ全体の `setGlobalLocale` / `createLocaleStore` を使います。SSR の UI store 初期化と setter は自身の状態のみ変更し、メッセージはリクエストコンテキストを参照します。`runWithLocale` は Node 入口専用です。
573
+
574
+ ## 設定の消費範囲
575
+
576
+ `_core.yml` は name/version/env/defaultLocale のメタデータと logging のみを受け付け、不明なフィールドでは読み込みに失敗します。グローバルな debug、ID 長/接頭辞、業務 features、言語登録リストは提供しません。logging.level、core.id の呼び出し引数、独自 Schema で検証する app.yml を使用してください。defaultLocale はメタデータであり、翻訳登録や Paraglide の切り替えを自動実行しません。Logger の import は Windows TTY でも LANG、LC_ALL、PYTHONIOENCODING を変更しません。[設定と消費点の対応表(中国語)](../../.agents/skills/hai-module-create/configuration.md)を参照してください。
577
+
578
+ ## 開発とテスト
555
579
 
556
580
  ```bash
557
581
  pnpm --filter @h-ai/core test
558
582
  ```
559
583
 
560
- ## License
584
+ 宣言の注釈と型検査できる例は [JSDoc 規約(中国語)](../../.agents/instructions/module-conventions.md#jsdoc-与示例验证) を参照してください。Node core.init は同期的に void を返します。単一ファイルの config.load 失敗結果は記録して続行するため、必須設定は validate して success を確認してください。ディレクトリー走査などの例外は伝播する場合があります。業務 HaiResult API は success を先に確認します。
585
+
586
+ ## ライセンス
561
587
 
562
588
  Apache-2.0