@h-ai/cli 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 +86 -241
- package/README.ja.md +86 -241
- package/README.md +86 -241
- package/dist/{src-RDQ2EIBT.js → chunk-FI56GONY.js} +32 -2669
- package/dist/chunk-FI56GONY.js.map +1 -0
- package/dist/chunk-MBPB37FR.js +1348 -0
- package/dist/chunk-MBPB37FR.js.map +1 -0
- package/dist/{chunk-QYX6V7H2.js → chunk-ZFNBUF77.js} +347 -276
- package/dist/chunk-ZFNBUF77.js.map +1 -0
- package/dist/cli-config-templates-W2LLPMB6.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-US5CCAJ4.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
- package/dist/index.js +1304 -532
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -0
- package/dist/src-DSJJXM2F.js +3849 -0
- package/dist/src-DSJJXM2F.js.map +1 -0
- package/package.json +9 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/messages/ja-JP.json +38 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +33 -8
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +6 -1
- package/templates/apps/api/playwright.config.ts.hbs +15 -7
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +30 -9
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +32 -8
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +40 -14
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +6 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +4 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +13 -14
- package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
- package/templates/apps/h5/messages/ja-JP.json +40 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +16 -5
- package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +15 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/messages/ja-JP.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +13 -5
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +23 -8
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +11 -3
- package/templates/base/playwright.config.ts +15 -4
- package/templates/base/src/hooks.server.ts.hbs +15 -6
- package/templates/base/src/lib/server/init.ts.hbs +139 -30
- package/templates/base/vite.config.ts.hbs +5 -3
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
- package/templates/i18n/project.inlang/settings.json +1 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/shared/e2e-env.ts +98 -0
- package/templates/shared/run-e2e.mjs +51 -12
- package/templates/skills/AGENTS.md +7 -3
- package/templates/skills/bridges/admin/AGENTS.md +6 -2
- package/templates/skills/bridges/api/AGENTS.md +3 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +5 -1
- package/templates/skills/bridges/h5/AGENTS.md +6 -2
- package/templates/skills/bridges/mobile-app/AGENTS.md +6 -2
- package/templates/skills/bridges/website/AGENTS.md +6 -2
- package/templates/skills/hai-ai/SKILL.md +8 -2
- package/templates/skills/hai-ai/guide.md +134 -60
- package/templates/skills/hai-ai/reference.md +51 -11
- package/templates/skills/hai-api-client/SKILL.md +15 -160
- package/templates/skills/hai-api-client/reference.md +178 -0
- package/templates/skills/hai-api-contract/SKILL.md +16 -273
- package/templates/skills/hai-api-contract/reference.md +280 -0
- package/templates/skills/hai-app-create/SKILL.md +6 -2
- package/templates/skills/hai-app-create/development.md +24 -8
- package/templates/skills/hai-app-create/reference.md +7 -5
- package/templates/skills/hai-app-review/SKILL.md +7 -3
- package/templates/skills/hai-app-review/differential.md +34 -0
- package/templates/skills/hai-app-tests/SKILL.md +6 -3
- package/templates/skills/hai-app-tests/reference.md +5 -5
- package/templates/skills/hai-app-tests/validation.md +38 -0
- package/templates/skills/hai-audit/SKILL.md +24 -147
- package/templates/skills/hai-audit/reference.md +148 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +16 -13
- package/templates/skills/hai-cache/SKILL.md +20 -181
- package/templates/skills/hai-cache/reference.md +229 -0
- package/templates/skills/hai-capacitor/SKILL.md +7 -4
- package/templates/skills/hai-capacitor/reference.md +15 -9
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +7 -3
- package/templates/skills/hai-core/reference.md +25 -5
- package/templates/skills/hai-crypto/SKILL.md +22 -250
- package/templates/skills/hai-crypto/reference.md +310 -0
- package/templates/skills/hai-datapipe/SKILL.md +16 -195
- package/templates/skills/hai-datapipe/reference.md +204 -0
- package/templates/skills/hai-deploy/SKILL.md +24 -54
- package/templates/skills/hai-deploy/middleware.md +51 -43
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +25 -11
- package/templates/skills/hai-iam/reference.md +181 -154
- package/templates/skills/hai-kit/SKILL.md +35 -174
- package/templates/skills/hai-kit/reference.md +190 -0
- package/templates/skills/hai-payment/SKILL.md +33 -263
- package/templates/skills/hai-payment/reference.md +295 -0
- package/templates/skills/hai-pr-review/SKILL.md +21 -88
- package/templates/skills/hai-reach/SKILL.md +6 -3
- package/templates/skills/hai-reach/reference.md +83 -22
- package/templates/skills/hai-reldb/SKILL.md +6 -3
- package/templates/skills/hai-reldb/reference.md +68 -25
- package/templates/skills/hai-scheduler/SKILL.md +18 -195
- package/templates/skills/hai-scheduler/reference.md +216 -0
- package/templates/skills/hai-serv/SKILL.md +25 -10
- package/templates/skills/hai-serv/reference.md +123 -87
- package/templates/skills/hai-spec-implement/SKILL.md +5 -1
- package/templates/skills/hai-spec-implement/evaluation.md +18 -0
- package/templates/skills/hai-spec-implement/handoff.md +47 -0
- package/templates/skills/hai-spec-implement/templates.md +5 -0
- package/templates/skills/hai-spec-implement/workflow.md +5 -3
- package/templates/skills/hai-spec-review/SKILL.md +4 -0
- package/templates/skills/hai-storage/SKILL.md +23 -238
- package/templates/skills/hai-storage/reference.md +282 -0
- package/templates/skills/hai-ui/SKILL.md +17 -3
- package/templates/skills/hai-ui/guide.md +3 -3
- package/templates/skills/hai-ui/reference.md +26 -11
- package/templates/skills/hai-ui-design/SKILL.md +31 -0
- package/templates/skills/hai-ui-design/patterns.md +26 -0
- package/templates/skills/hai-ui-review/SKILL.md +30 -0
- package/templates/skills/hai-ui-review/checklist.md +22 -0
- package/templates/skills/hai-vecdb/SKILL.md +14 -10
- package/templates/skills/hai-vecdb/reference.md +33 -3
- package/dist/chunk-FWN5B54L.js +0 -468
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# hai-crypto 参考
|
|
2
|
+
|
|
3
|
+
> `@h-ai/crypto` 提供非对称加密、哈希、对称加密与密码哈希能力,支持 Node.js 与浏览器双端。
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
认证协议仅支持 v2:独立随机 SM4-CBC 密钥和 HMAC-SM3 密钥由 SM2 封装,先验签、再验证 MAC,最后解密 CBC。请求由客户端私钥签名,响应由所选服务端版本签名。认证字段包括 version、direction、serverKeyId、clientId、128 位随机 requestId(兼 nonce)、issuedAt、HTTP method、pathname + 原始 search 的 SM3 pathHash,以及响应 status。请求有效期 5 分钟,未来时钟偏差最多 30 秒;共享 registry 原子消费 (clientId, requestId),nonce 至少保留到 issuedAt + 6 分钟以覆盖节点偏差,存储故障拒绝请求。Redis 必须保留有效 nonce,使用 noeviction/可靠持久化策略;各节点时钟须同步。此能力防止同一传输消息重放,业务重试仍需自己的幂等键。
|
|
7
|
+
|
|
8
|
+
空 POST 也发送空明文的认证封装;GET/HEAD 将封装放在 X-Hai-Transport 头,204/304/HEAD 等无正文响应也使用同名头承载认证封装。其它请求/响应在 JSON 正文承载封装并带 X-Encrypted: true。客户端必须关联原请求上下文和 HTTP status,缺标记、旧格式或认证失败一律 reject;明文接口只能经 excludePaths 显式排除。CORS 须允许并暴露 X-Hai-Transport。握手公钥的真实性依赖 TLS,不替代 IAM 业务授权。
|
|
9
|
+
|
|
10
|
+
## 运行环境
|
|
11
|
+
|
|
12
|
+
**Node.js + 浏览器双端可用。** 浏览器端通过 `crypto.init()` 初始化(无需配置参数)。
|
|
13
|
+
|
|
14
|
+
| 能力 | Node.js | 浏览器 | 说明 |
|
|
15
|
+
|------|---------|--------|------|
|
|
16
|
+
| `crypto.asymmetric` 非对称加密 | ✅ | ✅ | SM2 加解密、签名验签 |
|
|
17
|
+
| `crypto.hash` 哈希 | ✅ | ✅ | SM3 哈希、HMAC |
|
|
18
|
+
| `crypto.symmetric` 对称加密 | ✅ | ✅ | SM4 ECB/CBC |
|
|
19
|
+
| `crypto.password` 密码哈希 | ✅ | ❌ | 依赖 Node.js crypto(服务端专用) |
|
|
20
|
+
| `crypto.transport` 传输加密 | ✅ | ✅ | 服务端管理器 + 客户端 encryptedFetch |
|
|
21
|
+
|
|
22
|
+
浏览器端主要用于 kit 传输加密场景(`kit.client.create({ transport: { crypto } })`),一般不需要直接调用 crypto API。
|
|
23
|
+
|
|
24
|
+
## 适用场景
|
|
25
|
+
|
|
26
|
+
- 非对称加密/解密、签名/验签、密钥生成
|
|
27
|
+
- 哈希计算与 HMAC
|
|
28
|
+
- 对称加密/解密(ECB/CBC 模式)
|
|
29
|
+
- 密码存储与验证(加盐迭代哈希)
|
|
30
|
+
- 传输加密:为 serv / kit / api-client 提供统一协议、密钥协商和请求响应加解密
|
|
31
|
+
|
|
32
|
+
## 使用步骤
|
|
33
|
+
|
|
34
|
+
### 1. 初始化与关闭
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { crypto } from '@h-ai/crypto'
|
|
38
|
+
|
|
39
|
+
await crypto.init()
|
|
40
|
+
|
|
41
|
+
// 使用后关闭
|
|
42
|
+
await crypto.close()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 核心 API
|
|
46
|
+
|
|
47
|
+
### 非对称加密 — `crypto.asymmetric`
|
|
48
|
+
|
|
49
|
+
| 方法 | 签名 | 说明 |
|
|
50
|
+
| ------------------- | ----------------------------------------------------------- | ------------ |
|
|
51
|
+
| `generateKeyPair` | `() => HaiResult<KeyPair>` | 生成密钥对 |
|
|
52
|
+
| `encrypt` | `(data, publicKey, options?) => HaiResult<string>` | 公钥加密 |
|
|
53
|
+
| `decrypt` | `(ciphertext, privateKey, options?) => HaiResult<string>` | 私钥解密 |
|
|
54
|
+
| `sign` | `(data, privateKey, options?) => HaiResult<string>` | 私钥签名 |
|
|
55
|
+
| `verify` | `(data, signature, publicKey, options?) => HaiResult<boolean>` | 公钥验签 |
|
|
56
|
+
| `isValidPublicKey` | `(key: string) => boolean` | 校验公钥编码和 SM2 曲线点 |
|
|
57
|
+
| `isValidPrivateKey` | `(key: string) => boolean` | 校验私钥格式 |
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const keyPair = crypto.asymmetric.generateKeyPair()
|
|
61
|
+
if (keyPair.success) {
|
|
62
|
+
const { publicKey, privateKey } = keyPair.data
|
|
63
|
+
|
|
64
|
+
const encrypted = crypto.asymmetric.encrypt('敏感数据', publicKey)
|
|
65
|
+
if (encrypted.success) {
|
|
66
|
+
const decrypted = crypto.asymmetric.decrypt(encrypted.data, privateKey)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const signature = crypto.asymmetric.sign('待签名数据', privateKey)
|
|
70
|
+
if (signature.success) {
|
|
71
|
+
const valid = crypto.asymmetric.verify('待签名数据', signature.data, publicKey)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**AsymmetricEncryptOptions**:`{ cipherMode?: 0 | 1, outputFormat?: 'hex' | 'base64' }`
|
|
77
|
+
**SignOptions**:`{ hash?: boolean, userId?: string }`
|
|
78
|
+
|
|
79
|
+
### 哈希 — `crypto.hash`
|
|
80
|
+
|
|
81
|
+
| 方法 | 签名 | 说明 |
|
|
82
|
+
| -------- | ---------------------------------------------------------- | -------- |
|
|
83
|
+
| `hash` | `(data: string \| Uint8Array, options?) => HaiResult<string>` | 哈希 |
|
|
84
|
+
| `hmac` | `(data, key) => HaiResult<string>` | HMAC |
|
|
85
|
+
| `verify` | `(data, expectedHash) => HaiResult<boolean>` | 验证哈希 |
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const hash = crypto.hash.hash('hello')
|
|
89
|
+
const hmac = crypto.hash.hmac('hello', 'secret-key')
|
|
90
|
+
const valid = crypto.hash.verify('hello', hash.data!)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**HashOptions**:`{ inputEncoding?: 'utf8' | 'hex' }`
|
|
94
|
+
|
|
95
|
+
### 对称加密 — `crypto.symmetric`
|
|
96
|
+
|
|
97
|
+
| 方法 | 签名 | 说明 |
|
|
98
|
+
| --------------- | ----------------------------------------------- | --------------------------- |
|
|
99
|
+
| `generateKey` | `() => string` | 生成 128-bit 密钥(32 hex) |
|
|
100
|
+
| `generateIV` | `() => string` | 生成随机 IV(32 hex) |
|
|
101
|
+
| `encrypt` | `(data, key, options?) => HaiResult<SymmetricEncryptedPayload>` | 加密(返回结构化密文) |
|
|
102
|
+
| `decrypt` | `(payload, key) => HaiResult<string>` | 解密结构化密文 |
|
|
103
|
+
| `encryptWithIV` | `(data, key) => HaiResult<EncryptWithIVResult>` | CBC 模式加密(自动生成 IV) |
|
|
104
|
+
| `decryptWithIV` | `(ciphertext, key, iv) => HaiResult<string>` | CBC 模式解密 |
|
|
105
|
+
| `isValidKey` | `(key: string) => boolean` | 校验密钥格式 |
|
|
106
|
+
| `isValidIV` | `(iv: string) => boolean` | 校验 IV 格式 |
|
|
107
|
+
|
|
108
|
+
SM4 密钥使用 `generateKey()` 生成;本模块不提供密码 KDF。密码存储使用服务端 `crypto.password.hash()`,不要将密码或密码哈希直接用作 SM4 密钥。
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const key = crypto.symmetric.generateKey()
|
|
112
|
+
|
|
113
|
+
// 默认安全用法:CBC + 自动 IV,返回 { mode, ciphertext, iv, encoding }
|
|
114
|
+
const encrypted = crypto.symmetric.encrypt('明文数据', key)
|
|
115
|
+
if (encrypted.success) {
|
|
116
|
+
const decrypted = crypto.symmetric.decrypt(encrypted.data, key)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 需要单独存储 IV 时使用结构化结果
|
|
120
|
+
const withIV = crypto.symmetric.encryptWithIV('明文数据', key)
|
|
121
|
+
if (withIV.success) {
|
|
122
|
+
const decrypted = crypto.symmetric.decryptWithIV(withIV.data.ciphertext, key, withIV.data.iv)
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**SymmetricEncryptOptions**:`{ mode?: 'ecb' | 'cbc', iv?: string, outputFormat?: 'hex' | 'base64' }`
|
|
127
|
+
**SymmetricEncryptedPayload**:`{ mode: 'ecb' | 'cbc', ciphertext: string, iv?: string, encoding: 'hex' | 'base64' }`
|
|
128
|
+
|
|
129
|
+
> `encrypt()` 默认使用 CBC 并自动生成 IV。ECB 会泄漏明文结构,只有底层协议明确要求时才显式 `{ mode: 'ecb' }`。
|
|
130
|
+
|
|
131
|
+
### 密码哈希 — `crypto.password`
|
|
132
|
+
|
|
133
|
+
| 方法 | 签名 | 说明 |
|
|
134
|
+
| -------- | --------------------------------------- | -------- |
|
|
135
|
+
| `hash` | `(password, config?) => HaiResult<string>` | 密码哈希 |
|
|
136
|
+
| `verify` | `(password, hash) => HaiResult<boolean>` | 验证密码 |
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const hashed = crypto.password.hash('MyPassword123')
|
|
140
|
+
if (hashed.success) {
|
|
141
|
+
const valid = crypto.password.verify('MyPassword123', hashed.data)
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
哈希格式:`$hai$<iterations>$<salt>$<hash>`
|
|
146
|
+
**PasswordConfig**:`{ saltLength?: number, iterations?: number }`
|
|
147
|
+
|
|
148
|
+
### 传输加密 — `crypto.transport`
|
|
149
|
+
|
|
150
|
+
传输加密主工厂必须从 `crypto.transport` 访问;共享 `keyStore` provider 请从 `@h-ai/crypto` 根入口导入,不从子目录内部路径导入。
|
|
151
|
+
|
|
152
|
+
| 方法 | 签名 | 说明 |
|
|
153
|
+
| --- | --- | --- |
|
|
154
|
+
| `createServer` | `({ serverKeyRing, keyStore }) => HaiResult<TransportEncryptionManager>` | 校验受管密钥环并创建管理器,两项必填 |
|
|
155
|
+
| `createClient` | `({ keyExchangeUrl, fetch? }) => TransportClient` | 创建客户端 encryptedFetch 会话 |
|
|
156
|
+
| `protocol` | `TRANSPORT_PROTOCOL` | 协议常量:`X-Client-Id`、`X-Encrypted`、默认协商路径 |
|
|
157
|
+
|
|
158
|
+
共享 `keyStore` provider(从 `@h-ai/crypto` 根入口导入):
|
|
159
|
+
|
|
160
|
+
| 工厂 | 签名 | 说明 |
|
|
161
|
+
| --- | --- | --- |
|
|
162
|
+
| `createInMemoryKeyStore` | `(maxClients?) => TransportKeyStore` | 默认 FIFO 内存实现 |
|
|
163
|
+
| `createRedisTransportKeyStore` | `({ cache, ttlSeconds? }) => TransportKeyStore` | 基于 `@h-ai/cache` 的共享实现,通常配 Redis |
|
|
164
|
+
| `createReldbTransportKeyStore` | `({ reldb, ttlSeconds?, maxClients? }) => ReldbTransportKeyStore` | 自动建表、共享事务容量和有界维护 |
|
|
165
|
+
|
|
166
|
+
所有 registry 注册前验证非压缩 SM2 曲线点(128 hex 或 04 + 128 hex);纯函数 `isValidSM2PublicKey(value)` 无需 init,serv/kit 在写存储前以 400 拒绝畸形输入。Redis/DB TTL 省略则不过期,显式值须为 1–2147483647 整数秒,建议有限 TTL。Redis 用原生逐键 TTL,容量由部署管理;noeviction 防止提前驱逐 nonce。DB `maxClients` 默认 10000,正安全整数且各节点须与持久容量行一致;事务锁容量行后计数和插入,容量满拒绝、不驱逐有效公钥或 nonce。
|
|
167
|
+
|
|
168
|
+
DB 首次建表失败可重试;公钥过期使用 DB epoch 毫秒,DB 与节点均须同步时钟。注册自动执行一批清理,应用定期调用 `await registry.pruneExpired()` 处理低流量维护:每次最多 100 条过期公钥和 100 条过期 nonce,不建隐式计时器。旧 timestamp 表须停机迁移到 created_at_ms/expires_at_ms BIGINT;容量更改需同步持久记录和所有节点,不能清空有效 nonce。
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const registry = createReldbTransportKeyStore({ reldb, ttlSeconds: 3600, maxClients: 10000 })
|
|
172
|
+
// 由应用自己的维护调度器调用;失败后可重试。
|
|
173
|
+
await registry.pruneExpired()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const server = crypto.transport.createServer({ serverKeyRing, keyStore })
|
|
178
|
+
if (!server.success)
|
|
179
|
+
return server
|
|
180
|
+
|
|
181
|
+
const client = crypto.transport.createClient({
|
|
182
|
+
keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange',
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const resp = await client.encryptedFetch('https://api.example.com/api/v1/echo', {
|
|
186
|
+
method: 'POST',
|
|
187
|
+
body: JSON.stringify({ hello: 'world' }),
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
|
|
193
|
+
import { cache } from '@h-ai/cache'
|
|
194
|
+
|
|
195
|
+
await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
|
|
196
|
+
|
|
197
|
+
const sharedServer = crypto.transport.createServer({
|
|
198
|
+
serverKeyRing,
|
|
199
|
+
keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
使用流程:
|
|
204
|
+
1. 先 `await crypto.init()`。
|
|
205
|
+
2. 服务端显式传入受管 serverKeyRing 和共享 keyStore,协商返回 `serverKeyId + serverPublicKey + clientId`。
|
|
206
|
+
3. 客户端 `createClient({ keyExchangeUrl })` 后,首次 `init()` / `encryptedFetch()` 会自动做密钥协商。
|
|
207
|
+
4. 所有请求均发送 v2 认证封装;包括 GET/HEAD 和无 input POST,不能只附带 clientId。
|
|
208
|
+
5. 自定义适配器先 await authenticateTransportRequest(manager, request),再用 manager.encryptResponse(verified.data.context, data, status) 关联响应。
|
|
209
|
+
6. 客户端收到 `X-Encrypted: true` 的响应后自动解密;`destroy()` 可清空当前会话。
|
|
210
|
+
|
|
211
|
+
`encryptedFetch()` 与原生 fetch 一样会 reject:网络错误、密钥协商失败、请求加密失败或响应解密失败都应在调用方按 fetch 错误处理策略捕获。
|
|
212
|
+
|
|
213
|
+
### 受管服务端密钥与轮换
|
|
214
|
+
|
|
215
|
+
`serverKeyRing` 为 `{ activeKeyId, keys: { [id]: { publicKey, privateKey } } }`,ID 为 1–64 个字母、数字、下划线或连字符。根入口 `parseTransportServerKeyRing(secret)` 接受服务端 Secret JSON 或配置对象,返回 HaiResult,错误不附带原始 Secret 或解析片段。工厂验证每对公私钥并捕获快照;没有隐式随机私钥或 memory registry 回退。`keyStore` 也必填,生产用共享 Redis/数据库;开发/测试可显式 `createInMemoryKeyStore(capacity)`。
|
|
216
|
+
|
|
217
|
+
示例服务端读取 `process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING`,检查解析结果后仅传给服务端工厂;禁止写入普通 YAML、PUBLIC_/VITE_、日志或响应。CLI 仅在创建服务端项目时向忽略的 `.env.local` 生成一次本机开发 ring,生产由 Secret Manager 分发同一受管 ring。
|
|
218
|
+
|
|
219
|
+
请求密文 `EncryptedRequestPayload` 携带协商的 `serverKeyId`;服务端只按 ID 选择私钥,未知/撤销 ID 拒绝,不尝试其它 key。轮换先向所有节点分发包含新旧版本的 ring,再切 activeKeyId 并滚动重建 manager;过渡期后删除旧 ID 并更新所有节点。旧客户端 destroy 后重协商;禁止自动重放非幂等请求。共享 ring + registry 支持跨节点与重启,不依赖 sticky session。
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import { parseTransportServerKeyRing } from '@h-ai/crypto'
|
|
223
|
+
|
|
224
|
+
const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
|
|
225
|
+
if (!ring.success)
|
|
226
|
+
throw new Error(ring.error.message)
|
|
227
|
+
const manager = crypto.transport.createServer({ serverKeyRing: ring.data, keyStore: sharedRegistry })
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 传输正文预算与自定义适配器
|
|
231
|
+
|
|
232
|
+
serv、kit 和客户端统一从根入口导入 `TRANSPORT_BODY_LIMITS`:握手 JSON 16 KiB、单个公钥 4 KiB、明文 1 MiB、密文 JSON 2 MiB + 32 KiB,均为实际 UTF-8 字节上限,不使用 Content-Length 判定。客户端共享握手最多等待 30 秒;单个业务请求取消只中断自己的等待,其 signal 继续用于请求及响应读取。异常加密响应会 reject,不返回原密文。
|
|
233
|
+
|
|
234
|
+
根入口纯函数 `readTransportBody(stream, { maxBytes, signal? })` 无需 init,直接消费原始流;超限抛 RangeError,无效 UTF-8 抛 TypeError,保留读取异常或 signal.reason。超限/取消会立即取消流并释放 reader,清理失败不覆盖原异常。不要把 clone/tee 分支交给它,否则另一分支仍可能缓冲正文。成功读取后重建 Request/Response,清除旧 Content-Length/Content-Encoding 并保留 signal。
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { readTransportBody, TRANSPORT_BODY_LIMITS } from '@h-ai/crypto'
|
|
238
|
+
|
|
239
|
+
const text = await readTransportBody(request.body, {
|
|
240
|
+
maxBytes: TRANSPORT_BODY_LIMITS.keyExchangeBytes,
|
|
241
|
+
signal: request.signal,
|
|
242
|
+
})
|
|
243
|
+
const payload: unknown = JSON.parse(text)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
常规应用优先使用上层封装:
|
|
247
|
+
- serv:`serv.createApp({ transport: { crypto, serverKeyRing, keyStore } })`
|
|
248
|
+
- kit:`kit.createHandle({ crypto: { crypto, transport: { serverKeyRing, keyStore } } })` + `kit.client.create({ transport: { crypto } })`
|
|
249
|
+
- api-client:`apiClient.init({ transport: { crypto } })`
|
|
250
|
+
|
|
251
|
+
## 错误码 — `HaiCryptoError`
|
|
252
|
+
|
|
253
|
+
| 错误码 | code | 说明 |
|
|
254
|
+
|--------|------|------|
|
|
255
|
+
| `HaiCryptoError.INVALID_INPUT` | `hai:crypto:002` | 无效输入 |
|
|
256
|
+
| `HaiCryptoError.INVALID_KEY` | `hai:crypto:003` | 无效密钥 |
|
|
257
|
+
| `HaiCryptoError.NOT_INITIALIZED` | `hai:crypto:010` | 未初始化 |
|
|
258
|
+
| `HaiCryptoError.INIT_FAILED` | `hai:crypto:011` | 初始化失败 |
|
|
259
|
+
| `HaiCryptoError.KEY_GENERATION_FAILED` | `hai:crypto:020` | 密钥生成失败 |
|
|
260
|
+
| `HaiCryptoError.ENCRYPTION_FAILED` | `hai:crypto:021` | 加密失败 |
|
|
261
|
+
| `HaiCryptoError.DECRYPTION_FAILED` | `hai:crypto:022` | 解密失败 |
|
|
262
|
+
| `HaiCryptoError.SIGN_FAILED` | `hai:crypto:023` | 签名失败 |
|
|
263
|
+
| `HaiCryptoError.VERIFY_FAILED` | `hai:crypto:024` | 验签失败 |
|
|
264
|
+
| `HaiCryptoError.HASH_FAILED` | `hai:crypto:040` | 哈希计算失败 |
|
|
265
|
+
| `HaiCryptoError.HMAC_FAILED` | `hai:crypto:041` | HMAC 计算失败 |
|
|
266
|
+
| `HaiCryptoError.INVALID_IV` | `hai:crypto:060` | 无效 IV |
|
|
267
|
+
|
|
268
|
+
## 常见模式
|
|
269
|
+
|
|
270
|
+
### 数据加密存储
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const key = process.env.HAI_CRYPTO_DATA_KEY!
|
|
274
|
+
const encrypted = crypto.symmetric.encrypt(sensitiveData, key)
|
|
275
|
+
// 存入数据库
|
|
276
|
+
|
|
277
|
+
// 读取时解密
|
|
278
|
+
const decrypted = crypto.symmetric.decrypt(storedPayload, key)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
> 默认密文是结构化对象,必须完整存储 `mode` / `ciphertext` / `iv` / `encoding` 字段;如果数据库字段已拆分 IV 与密文,请改用 `encryptWithIV()` / `decryptWithIV()`。
|
|
282
|
+
|
|
283
|
+
### 密码存储与验证
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// 注册时哈希存储
|
|
287
|
+
const hashResult = crypto.password.hash(userPassword)
|
|
288
|
+
if (hashResult.success) {
|
|
289
|
+
// 存储 hashResult.data 到数据库
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// 登录时验证
|
|
293
|
+
const verifyResult = crypto.password.verify(inputPassword, storedHash)
|
|
294
|
+
if (verifyResult.success && verifyResult.data) {
|
|
295
|
+
// 密码匹配
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## 相关 Skills
|
|
300
|
+
|
|
301
|
+
- `hai-core`:配置与 HaiResult 模型
|
|
302
|
+
- `hai-iam`:密码哈希(内部自动调用 crypto.password)
|
|
303
|
+
- `hai-kit`:SvelteKit 集成(传输加密)
|
|
304
|
+
- `hai-serv` / `hai-api-client`:跨域 HTTP API 的透明传输加密
|
|
305
|
+
|
|
306
|
+
管理端在线加密演示直接调用 Crypto SM3/SM4 API,SM4 使用随机临时密钥和 IV,并在显示成功前解密校验;密钥不保存。输入改变即清空旧结果,禁止用随机摘要或 Base64 伪装算法结果。
|
|
307
|
+
|
|
308
|
+
## 生产共享状态
|
|
309
|
+
|
|
310
|
+
生产传输 registry 必须声明 stateScope=shared 并实际使用 Redis 或 PostgreSQL/MySQL。memory、包装 memory 的 Redis 工厂、包装 SQLite 的 RelDB 工厂会被拒绝;各节点加载相同受管版本化密钥环。 NODE_ENV 或 HAI_CORE_ENV 为 production 时在初始化成功前检查,只有 HAI_E2E=1/HAI_E2E_MOCK=1 明确测试豁免。不要将租约当作最终写入条件,外部未知效果须按稳定业务键核对。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-datapipe
|
|
3
|
-
description: "使用 @h-ai/datapipe
|
|
3
|
+
description: "使用 @h-ai/datapipe 清洗和分块文本、组合预处理管线;用于 RAG 入库前处理,不负责 embedding。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-datapipe
|
|
@@ -15,202 +15,23 @@ description: "使用 @h-ai/datapipe 清洗文本、分块并组合 RAG 入库管
|
|
|
15
15
|
| 输出 | 清洗/分块的 HaiResult;管线工厂按签名使用 |
|
|
16
16
|
| 限制 | 纯函数,无 init/close;区分字符数和模型 token 数,分块结果不等于已生成 embedding。 |
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## 使用路径
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
1. 直接使用 clean/chunk/pipeline,无 init/close;清洗或分块失败后不读 data。
|
|
21
|
+
2. 根据原文结构选择模式,明确 maxSize、overlap 与字符/词边界;这里不负责模型 token 预算。
|
|
22
|
+
3. 自定义正则仅来自可信配置。入库前先检查管线结果,再生成 embedding,并验证向量维度与集合匹配。
|
|
23
|
+
4. 验证空文本、非法配置、边界切分与自定义转换失败;保留文档内容及必要元数据。
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
## 按需参考
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
|
|
25
28
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
29
|
+
- [运行环境](reference.md#运行环境)
|
|
30
|
+
- [适用场景](reference.md#适用场景)
|
|
31
|
+
- [使用步骤](reference.md#使用步骤)
|
|
32
|
+
- [核心 API](reference.md#核心-api)
|
|
33
|
+
- [错误码 — `HaiDatapipeError`](reference.md#错误码--haidatapipeerror)
|
|
34
|
+
- [常见模式](reference.md#常见模式)
|
|
35
|
+
- [相关 Skills](reference.md#相关-skills)
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### 直接调用(无需 init)
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
import { datapipe } from '@h-ai/datapipe'
|
|
37
|
-
|
|
38
|
-
// 清洗
|
|
39
|
-
const cleaned = datapipe.clean(htmlText, { removeHtml: true, removeUrls: true })
|
|
40
|
-
|
|
41
|
-
// 分块
|
|
42
|
-
const chunks = datapipe.chunk(cleaned.data, { mode: 'markdown', maxSize: 2000, overlap: 200 })
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 管线模式
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
const result = await datapipe.pipeline()
|
|
49
|
-
.clean({ removeHtml: true, removeUrls: true })
|
|
50
|
-
.transform(text => text.toLowerCase())
|
|
51
|
-
.chunk({ mode: 'paragraph', maxSize: 1000, overlap: 100 })
|
|
52
|
-
.chunkTransform(chunks => chunks.filter(c => c.content.length > 50))
|
|
53
|
-
.run(rawText)
|
|
54
|
-
|
|
55
|
-
if (result.success) {
|
|
56
|
-
// result.data => { text: string, chunks: DataChunk[] }
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## 核心 API
|
|
61
|
-
|
|
62
|
-
### 清洗 — `datapipe.clean`
|
|
63
|
-
|
|
64
|
-
```text
|
|
65
|
-
datapipe.clean(text: string, options?: CleanOptionsInput): HaiResult<string>
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**CleanOptionsInput**:
|
|
69
|
-
|
|
70
|
-
| 字段 | 类型 | 默认 | 说明 |
|
|
71
|
-
| -------------------- | ------------------ | ----- | ------------------------ |
|
|
72
|
-
| `removeHtml` | `boolean` | true | 移除 HTML 标签 |
|
|
73
|
-
| `removeUrls` | `boolean` | false | 移除 URL |
|
|
74
|
-
| `removeEmails` | `boolean` | false | 移除 Email 地址 |
|
|
75
|
-
| `normalizeWhitespace`| `boolean` | true | 多空格→单空格、去多余空行 |
|
|
76
|
-
| `trim` | `boolean` | true | 去除首尾空白 |
|
|
77
|
-
| `customReplacements` | `{ pattern, replacement }[]` | — | 自定义正则替换规则 |
|
|
78
|
-
|
|
79
|
-
### 分块 — `datapipe.chunk`
|
|
80
|
-
|
|
81
|
-
```text
|
|
82
|
-
datapipe.chunk(text: string, options: ChunkOptionsInput): HaiResult<DataChunk[]>
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**ChunkOptionsInput**:
|
|
86
|
-
|
|
87
|
-
| 字段 | 类型 | 默认 | 说明 |
|
|
88
|
-
| ------------------ | --------- | ---- | ------------------------------ |
|
|
89
|
-
| `mode` | `ChunkMode` | — | 分块模式(必填) |
|
|
90
|
-
| `maxSize` | `number` | 1000 | 分块最大大小 |
|
|
91
|
-
| `overlap` | `number` | 0 | 重叠大小(上下文衔接) |
|
|
92
|
-
| `separator` | `string` | — | 自定义分隔符正则(mode=custom)|
|
|
93
|
-
| `markdownMinLevel` | `number` | 2 | Markdown 最低标题级别(1-6) |
|
|
94
|
-
| `markdownKeepTitle`| `boolean` | true | 分块中保留 Markdown 标题 |
|
|
95
|
-
|
|
96
|
-
**分块模式**:
|
|
97
|
-
|
|
98
|
-
| 模式 | 说明 |
|
|
99
|
-
| ----------- | --------------------------- |
|
|
100
|
-
| `sentence` | 按句子(中英文句号/问号/感叹号) |
|
|
101
|
-
| `paragraph` | 按段落(双换行) |
|
|
102
|
-
| `markdown` | 按 Markdown 标题层级 |
|
|
103
|
-
| `page` | 按分页符(\f) |
|
|
104
|
-
| `word` | 按字数 |
|
|
105
|
-
| `character` | 按字符数 |
|
|
106
|
-
| `custom` | 自定义正则分隔符 |
|
|
107
|
-
|
|
108
|
-
**DataChunk**:
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
interface DataChunk {
|
|
112
|
-
index: number // 分块索引(从 0 开始)
|
|
113
|
-
content: string // 分块内容
|
|
114
|
-
metadata?: Record<string, unknown> // 元数据(Markdown 模式含 title、level)
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### 管线 — `datapipe.pipeline()`
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
datapipe.pipeline()
|
|
122
|
-
.clean(options) // 添加清洗步骤
|
|
123
|
-
.transform(fn) // 添加文本转换步骤(同步/异步)
|
|
124
|
-
.chunk(options) // 添加分块步骤
|
|
125
|
-
.chunkTransform(fn) // 添加分块后处理步骤
|
|
126
|
-
.run(text) // 执行管线 → Promise<HaiResult<PipelineResult>>
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**PipelineResult**:
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
interface PipelineResult {
|
|
133
|
-
text: string // 处理后的文本(分块前的最终文本)
|
|
134
|
-
chunks: DataChunk[] // 分块列表(如果管线包含 chunk 步骤)
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## 错误码 — `HaiDatapipeError`
|
|
139
|
-
|
|
140
|
-
| 错误码 | code | 说明 |
|
|
141
|
-
|--------|------|------|
|
|
142
|
-
| `HaiDatapipeError.CLEAN_FAILED` | `hai:datapipe:001` | 清洗失败 |
|
|
143
|
-
| `HaiDatapipeError.CHUNK_FAILED` | `hai:datapipe:002` | 分块失败 |
|
|
144
|
-
| `HaiDatapipeError.TRANSFORM_FAILED` | `hai:datapipe:003` | 转换失败 |
|
|
145
|
-
| `HaiDatapipeError.PIPELINE_FAILED` | `hai:datapipe:004` | 管线执行失败 |
|
|
146
|
-
| `HaiDatapipeError.CONFIG_ERROR` | `hai:datapipe:005` | 配置错误 |
|
|
147
|
-
| `HaiDatapipeError.MISSING_SEPARATOR` | `hai:datapipe:006` | 自定义分隔符缺失 |
|
|
148
|
-
|
|
149
|
-
## 常见模式
|
|
150
|
-
|
|
151
|
-
### RAG 文档入库前处理
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import { datapipe } from '@h-ai/datapipe'
|
|
155
|
-
import { ai } from '@h-ai/ai'
|
|
156
|
-
import { vecdb } from '@h-ai/vecdb'
|
|
157
|
-
|
|
158
|
-
// 清洗 + Markdown 分块
|
|
159
|
-
const result = await datapipe.pipeline()
|
|
160
|
-
.clean({ removeHtml: true, removeUrls: true })
|
|
161
|
-
.chunk({ mode: 'markdown', maxSize: 1000, overlap: 100 })
|
|
162
|
-
.run(rawDocument)
|
|
163
|
-
|
|
164
|
-
if (!result.success) return result
|
|
165
|
-
|
|
166
|
-
// 生成嵌入
|
|
167
|
-
const embeddings = await ai.embedding.embedBatch(
|
|
168
|
-
result.data.chunks.map(c => c.content),
|
|
169
|
-
)
|
|
170
|
-
if (!embeddings.success) return embeddings
|
|
171
|
-
|
|
172
|
-
// 入库
|
|
173
|
-
const docs = result.data.chunks.map((chunk, i) => ({
|
|
174
|
-
id: `chunk-${i}`,
|
|
175
|
-
vector: embeddings.data[i],
|
|
176
|
-
content: chunk.content,
|
|
177
|
-
metadata: chunk.metadata,
|
|
178
|
-
}))
|
|
179
|
-
await vecdb.vector.insert('knowledge', docs)
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### 多步清洗管线
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
const result = await datapipe.pipeline()
|
|
186
|
-
.clean({ removeHtml: true, removeEmails: true })
|
|
187
|
-
.transform(text => text.replace(/\[.*?\]/g, '')) // 移除 [注释]
|
|
188
|
-
.transform(text => text.replace(/\(https?:.*?\)/g, '')) // 移除 Markdown 链接 URL
|
|
189
|
-
.chunk({ mode: 'paragraph', maxSize: 800 })
|
|
190
|
-
.chunkTransform(chunks => chunks.filter(c => c.content.length > 20)) // 过滤过短分块
|
|
191
|
-
.run(rawText)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### 错误分支处理
|
|
195
|
-
|
|
196
|
-
```typescript
|
|
197
|
-
import { datapipe, HaiDatapipeError } from '@h-ai/datapipe'
|
|
198
|
-
|
|
199
|
-
const result = datapipe.chunk(text, { mode: 'custom' })
|
|
200
|
-
if (!result.success) {
|
|
201
|
-
switch (result.error.code) {
|
|
202
|
-
case HaiDatapipeError.MISSING_SEPARATOR.code:
|
|
203
|
-
// mode='custom' 时需提供 separator
|
|
204
|
-
break
|
|
205
|
-
case HaiDatapipeError.CONFIG_ERROR.code:
|
|
206
|
-
// 配置参数校验失败
|
|
207
|
-
break
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
## 相关 Skills
|
|
213
|
-
|
|
214
|
-
- `hai-vecdb`:向量数据库存储(分块后入库)
|
|
215
|
-
- `hai-ai`:LLM 与 Embedding 能力
|
|
216
|
-
- `hai-core`:HaiResult 模型
|
|
37
|
+
分块 `maxSize` 默认 1500,`overlap` 默认 0 且必须满足 `0 <= overlap < maxSize`。word 模式两者均按非空白词计量(连续中文可视为一个长词),其余模式均按 Unicode code point 计量,不是 UTF-16 length、字节或模型 token。上限包含重叠与分隔符;自然句/段/页/自定义边界只影响优先切点,超长段统一按 `maxSize-overlap` 滑窗。保留原始空白、标点、分页符及自定义分隔符,移除重叠后可还原全文;不再 split/join 或 trim 分块。Markdown section 之间不重叠,section 内使用相同硬上限,代码围栏内的标题保留为正文;仅 `markdownKeepTitle=false` 明确移除标题行。空/全空白的整体输入返回空数组。自定义正则须由可信配置提供,非法表达式返回 CHUNK_FAILED。
|