@h-ai/cli 0.1.0-alpha.10
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/LICENSE +202 -0
- package/README.md +167 -0
- package/dist/chunk-2H7UOFLK.js +10 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-LANIDJC7.js +659 -0
- package/dist/chunk-LANIDJC7.js.map +1 -0
- package/dist/chunk-YV7WPUI6.js +206 -0
- package/dist/chunk-YV7WPUI6.js.map +1 -0
- package/dist/config-templates-S5RI2MC6.js +5 -0
- package/dist/config-templates-S5RI2MC6.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1650 -0
- package/dist/index.js.map +1 -0
- package/dist/skill-templates-N5DU5FNX.js +5 -0
- package/dist/skill-templates-N5DU5FNX.js.map +1 -0
- package/dist/src-XJ6YXNAF.js +15030 -0
- package/dist/src-XJ6YXNAF.js.map +1 -0
- package/package.json +59 -0
- package/templates/README.md +126 -0
- package/templates/apps/admin/messages/en-US.json +33 -0
- package/templates/apps/admin/messages/zh-CN.json +33 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +30 -0
- package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -0
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +44 -0
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +45 -0
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +37 -0
- package/templates/apps/admin/src/routes/api/health/+server.ts +12 -0
- package/templates/apps/android-app/.env.hbs +3 -0
- package/templates/apps/android-app/capacitor.config.ts.hbs +12 -0
- package/templates/apps/android-app/messages/en-US.json +24 -0
- package/templates/apps/android-app/messages/zh-CN.json +24 -0
- package/templates/apps/android-app/src/lib/api.ts.hbs +30 -0
- package/templates/apps/android-app/src/lib/capacitor.ts +21 -0
- package/templates/apps/android-app/src/routes/+layout.svelte.hbs +42 -0
- package/templates/apps/android-app/src/routes/+layout.ts +7 -0
- package/templates/apps/android-app/src/routes/+page.svelte.hbs +27 -0
- package/templates/apps/api/src/routes/+layout.svelte +14 -0
- package/templates/apps/api/src/routes/+page.svelte +11 -0
- package/templates/apps/api/src/routes/api/v1/health/+server.ts +14 -0
- package/templates/apps/api/src/routes/api/v1/items/+server.ts +37 -0
- package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +30 -0
- package/templates/apps/h5/messages/en-US.json +37 -0
- package/templates/apps/h5/messages/zh-CN.json +37 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -0
- package/templates/apps/h5/src/routes/+page.svelte.hbs +27 -0
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +27 -0
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +27 -0
- package/templates/apps/website/messages/en-US.json +32 -0
- package/templates/apps/website/messages/zh-CN.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +66 -0
- package/templates/apps/website/src/routes/+page.svelte.hbs +33 -0
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +27 -0
- package/templates/apps/website/src/routes/api/health/+server.ts +12 -0
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +59 -0
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +63 -0
- package/templates/base/e2e/helpers.ts +49 -0
- package/templates/base/package.json.hbs +67 -0
- package/templates/base/playwright.config.ts +32 -0
- package/templates/base/src/app.css +2 -0
- package/templates/base/src/app.d.ts.hbs +18 -0
- package/templates/base/src/app.html.hbs +16 -0
- package/templates/base/src/hooks.server.ts.hbs +123 -0
- package/templates/base/src/lib/server/init.ts.hbs +190 -0
- package/templates/base/svelte.config.js.hbs +30 -0
- package/templates/base/tsconfig.json +8 -0
- package/templates/base/vite.config.ts.hbs +27 -0
- package/templates/features/ai/routes/api/chat/+server.ts +26 -0
- package/templates/features/datapipe/routes/api/datapipe/pipeline/+server.ts +41 -0
- package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +21 -0
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +98 -0
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +124 -0
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +98 -0
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +114 -0
- package/templates/features/iam/routes-shared/api/auth/login/+server.ts +32 -0
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +15 -0
- package/templates/features/iam/routes-shared/api/auth/me/+server.ts +12 -0
- package/templates/features/iam/routes-shared/api/auth/register/+server.ts +26 -0
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +30 -0
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +42 -0
- package/templates/features/payment/routes/api/v1/payment/query/[orderNo]/+server.ts +25 -0
- package/templates/features/storage/routes/api/upload/+server.ts +35 -0
- package/templates/features/vecdb/routes/api/vecdb/search/+server.ts +36 -0
- package/templates/i18n/project.inlang/settings.json +11 -0
- package/templates/skills/AGENTS.md +26 -0
- package/templates/skills/CLAUDE.md +12 -0
- package/templates/skills/copilot-instructions.md +61 -0
- package/templates/skills/hai-ai/SKILL.md +721 -0
- package/templates/skills/hai-api-client/SKILL.md +355 -0
- package/templates/skills/hai-app-create/SKILL.md +371 -0
- package/templates/skills/hai-app-review/SKILL.md +156 -0
- package/templates/skills/hai-app-tests/SKILL.md +468 -0
- package/templates/skills/hai-audit/SKILL.md +145 -0
- package/templates/skills/hai-build/SKILL.md +319 -0
- package/templates/skills/hai-cache/SKILL.md +190 -0
- package/templates/skills/hai-capacitor/SKILL.md +377 -0
- package/templates/skills/hai-core/SKILL.md +484 -0
- package/templates/skills/hai-crypto/SKILL.md +198 -0
- package/templates/skills/hai-datapipe/SKILL.md +220 -0
- package/templates/skills/hai-deploy/SKILL.md +216 -0
- package/templates/skills/hai-iam/SKILL.md +361 -0
- package/templates/skills/hai-kit/SKILL.md +484 -0
- package/templates/skills/hai-payment/SKILL.md +275 -0
- package/templates/skills/hai-reach/SKILL.md +315 -0
- package/templates/skills/hai-reldb/SKILL.md +465 -0
- package/templates/skills/hai-scheduler/SKILL.md +188 -0
- package/templates/skills/hai-storage/SKILL.md +276 -0
- package/templates/skills/hai-ui/SKILL.md +438 -0
- package/templates/skills/hai-vecdb/SKILL.md +279 -0
- package/templates/skills/opencode.json +11 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hai-kit
|
|
3
|
+
description: 使用 @h-ai/kit 构建 SvelteKit 请求管道与 API 安全边界(handle/guard/middleware/handler/validate/response/fromContract)。当需求涉及 hooks.server.ts、hooks.client.ts、权限守卫、限流、统一响应、Bearer Token 认证、契约处理、CORS 配置、svelte.config.js 或 playwright.config.ts 时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# hai-kit
|
|
7
|
+
|
|
8
|
+
`@h-ai/kit` 是 hai-framework 的 SvelteKit 集成层。它把请求处理中的"通用横切能力"收敛到统一命名空间 `kit`:
|
|
9
|
+
|
|
10
|
+
- 请求入口:`createHandle` / `sequence`
|
|
11
|
+
- 安全边界:`guard` / `middleware`
|
|
12
|
+
- API 工具:`handler` / `response` / `validate`
|
|
13
|
+
- Bearer Token 认证:从 `Authorization` header 解析,SSR 端通过 `hai_token` Cookie 透传
|
|
14
|
+
- 契约处理:`fromContract(endpoint, fn)` — 基于 EndpointDef 的类型安全 handler
|
|
15
|
+
- 双构建模式:`createAdapter()` 根据环境变量选择 adapter-node 或 adapter-static
|
|
16
|
+
|
|
17
|
+
## 模块概述
|
|
18
|
+
|
|
19
|
+
当任务涉及以下关键词时,优先使用本 Skill:
|
|
20
|
+
|
|
21
|
+
- `hooks.server.ts` / `hooks.client.ts`
|
|
22
|
+
- `svelte.config.js` / `vite.config.ts` / `playwright.config.ts`
|
|
23
|
+
- `kit.createHandle` / `kit.sequence`
|
|
24
|
+
- `kit.guard.*`(`require` / `check`)
|
|
25
|
+
- `kit.handler` / `kit.response` / `kit.validate`
|
|
26
|
+
- `kit.fromContract(endpoint, fn)` — 契约到 SvelteKit handler
|
|
27
|
+
- `kit.client.create()` — 浏览器端统一 API 客户端(`apiFetch`)
|
|
28
|
+
- `kit.auth.createTokenStore()` — 浏览器端 Token 存储
|
|
29
|
+
- `kit.auth.createHandleFetch()` — `hooks.client.ts` 自动附加 Token
|
|
30
|
+
- Bearer Token / `Authorization` header
|
|
31
|
+
- CORS 配置(含 Capacitor Origin 预设)
|
|
32
|
+
- 传输加密(key exchange / `X-Client-Id`)
|
|
33
|
+
|
|
34
|
+
> **重要变更**:Cookie Session 和 CSRF 中间件已移除。统一使用 Bearer Token 认证。
|
|
35
|
+
|
|
36
|
+
## 依赖
|
|
37
|
+
|
|
38
|
+
| 模块 | 用途 | 是否必需 | 初始化要求 |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `@h-ai/iam` | 身份认证与授权(A2A 认证) | 可选 | 使用 A2A 功能时需在 kit 使用前初始化 |
|
|
41
|
+
| `@h-ai/crypto` | 加密(传输加密 / Cookie 加密) | 可选 | 启用加密时需在 kit 使用前初始化 |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 项目配置(从 npm 安装)
|
|
46
|
+
|
|
47
|
+
> 以下示例面向通过 `npm install @h-ai/kit` 引用发布包的项目。monorepo 内部使用 `workspace:*` 时路径略有不同。
|
|
48
|
+
|
|
49
|
+
### 1. 安装依赖
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 核心包
|
|
53
|
+
npm install @h-ai/kit
|
|
54
|
+
|
|
55
|
+
# 必须的对等依赖
|
|
56
|
+
npm install svelte @sveltejs/kit
|
|
57
|
+
npm install -D @sveltejs/vite-plugin-svelte vite
|
|
58
|
+
|
|
59
|
+
# 可选:双构建 adapter
|
|
60
|
+
npm install -D @sveltejs/adapter-node @sveltejs/adapter-static
|
|
61
|
+
|
|
62
|
+
# API 校验
|
|
63
|
+
npm install zod
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. svelte.config.js
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import adapter from '@sveltejs/adapter-auto'
|
|
70
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
71
|
+
|
|
72
|
+
/** @type {import('@sveltejs/kit').Config} */
|
|
73
|
+
const config = {
|
|
74
|
+
preprocess: [vitePreprocess()],
|
|
75
|
+
compilerOptions: {
|
|
76
|
+
runes: true,
|
|
77
|
+
},
|
|
78
|
+
kit: {
|
|
79
|
+
adapter: adapter(),
|
|
80
|
+
// 如需双构建模式(SSR/SSG 按环境切换):
|
|
81
|
+
// import { createAdapter } from '@h-ai/kit/adapter'
|
|
82
|
+
// adapter: createAdapter(),
|
|
83
|
+
csrf: {
|
|
84
|
+
// E2E 测试时信任本地域名
|
|
85
|
+
trustedOrigins: process.env.HAI_E2E === '1'
|
|
86
|
+
? ['http://localhost:4173', 'http://127.0.0.1:4173']
|
|
87
|
+
: [],
|
|
88
|
+
},
|
|
89
|
+
alias: {
|
|
90
|
+
$components: './src/lib/components',
|
|
91
|
+
$stores: './src/lib/stores',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default config
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> 若同时使用 `@h-ai/ui`,在 `preprocess` 数组前方加 `autoImportHaiUi()`(详见 hai-ui Skill)。
|
|
100
|
+
|
|
101
|
+
### 3. vite.config.ts
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import process from 'node:process'
|
|
105
|
+
import { sveltekit } from '@sveltejs/kit/vite'
|
|
106
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
107
|
+
import { defineConfig, loadEnv } from 'vite'
|
|
108
|
+
|
|
109
|
+
export default defineConfig(({ mode }) => {
|
|
110
|
+
const env = loadEnv(mode, process.cwd(), '')
|
|
111
|
+
Object.assign(process.env, env)
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
plugins: [
|
|
115
|
+
sveltekit(),
|
|
116
|
+
tailwindcss(),
|
|
117
|
+
// 如使用 Paraglide i18n:
|
|
118
|
+
// paraglideVitePlugin({ project: './project.inlang', outdir: './src/lib/paraglide', strategy: ['cookie', 'baseLocale'] }),
|
|
119
|
+
],
|
|
120
|
+
optimizeDeps: {
|
|
121
|
+
exclude: ['bits-ui'], // bits-ui 使用 Svelte 虚拟模块
|
|
122
|
+
},
|
|
123
|
+
ssr: {
|
|
124
|
+
// @h-ai 包为 ESM,需 Vite 在 SSR 时处理
|
|
125
|
+
noExternal: [/@h-ai\//],
|
|
126
|
+
},
|
|
127
|
+
server: {
|
|
128
|
+
port: 5173,
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 4. src/hooks.server.ts — 请求管道
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import type { Handle } from '@sveltejs/kit'
|
|
138
|
+
import { kit } from '@h-ai/kit'
|
|
139
|
+
|
|
140
|
+
// ─── 模块初始化(延迟 + 并发安全) ─── //
|
|
141
|
+
let initPromise: Promise<void> | null = null
|
|
142
|
+
|
|
143
|
+
async function ensureInit() {
|
|
144
|
+
if (!initPromise) {
|
|
145
|
+
initPromise = initModules().catch((err) => {
|
|
146
|
+
initPromise = null // 失败清除,允许重试
|
|
147
|
+
throw err
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
await initPromise
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function initModules() {
|
|
154
|
+
// 按需初始化 reldb、iam、cache 等模块
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ─── 初始化 Handle ─── //
|
|
158
|
+
const initHandle: Handle = async ({ event, resolve }) => {
|
|
159
|
+
await ensureInit()
|
|
160
|
+
return resolve(event)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── 认证 Handle ─── //
|
|
164
|
+
const haiHandle = kit.createHandle({
|
|
165
|
+
auth: {
|
|
166
|
+
verifyToken: async (token) => {
|
|
167
|
+
// 验证 Bearer Token,返回 SessionData 或 null
|
|
168
|
+
return token ? { userId: 'u_1', roles: ['admin'], permissions: ['user:read'] } : null
|
|
169
|
+
},
|
|
170
|
+
loginUrl: '/auth/login',
|
|
171
|
+
protectedPaths: ['/admin/*', '/api/*'],
|
|
172
|
+
publicPaths: ['/api/auth/*', '/api/public/*'],
|
|
173
|
+
},
|
|
174
|
+
rateLimit: { windowMs: 60_000, maxRequests: 100 },
|
|
175
|
+
logging: true,
|
|
176
|
+
cors: {
|
|
177
|
+
origins: ['https://example.com'],
|
|
178
|
+
capacitor: true, // 自动允许 Capacitor 原生 App WebView Origin
|
|
179
|
+
credentials: true,
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
export const handle: Handle = kit.sequence(initHandle, haiHandle)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 5. src/hooks.client.ts — 客户端 Token 注入
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { kit } from '@h-ai/kit'
|
|
190
|
+
|
|
191
|
+
// 自动将 localStorage 中的 Bearer Token 附加到 SSR fetch 请求
|
|
192
|
+
export const handleFetch = kit.auth.createHandleFetch()
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 6. playwright.config.ts — E2E 测试
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import process from 'node:process'
|
|
199
|
+
import { defineConfig } from '@playwright/test'
|
|
200
|
+
|
|
201
|
+
const baseURL = process.env.BASE_URL || 'http://localhost:4173'
|
|
202
|
+
|
|
203
|
+
export default defineConfig({
|
|
204
|
+
testDir: './e2e',
|
|
205
|
+
fullyParallel: false,
|
|
206
|
+
forbidOnly: !!process.env.CI,
|
|
207
|
+
retries: process.env.CI ? 2 : 0,
|
|
208
|
+
workers: 2,
|
|
209
|
+
reporter: 'list',
|
|
210
|
+
timeout: 30_000,
|
|
211
|
+
|
|
212
|
+
use: {
|
|
213
|
+
baseURL,
|
|
214
|
+
channel: 'chrome',
|
|
215
|
+
extraHTTPHeaders: { Origin: baseURL },
|
|
216
|
+
trace: 'on-first-retry',
|
|
217
|
+
screenshot: 'only-on-failure',
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
webServer: {
|
|
221
|
+
command: 'pnpm build && pnpm exec vite preview --port 4173 --strictPort',
|
|
222
|
+
env: { HAI_E2E: '1', NODE_ENV: 'test' },
|
|
223
|
+
url: baseURL,
|
|
224
|
+
reuseExistingServer: false,
|
|
225
|
+
timeout: 180_000,
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
> `HAI_E2E=1` 与 `NODE_ENV=test` 搭配使用:提高 rateLimit 上限、信任本地 Origin。
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 使用步骤
|
|
235
|
+
|
|
236
|
+
### 1) API 端点使用 `kit.handler`
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { kit } from '@h-ai/kit'
|
|
240
|
+
import { z } from 'zod'
|
|
241
|
+
|
|
242
|
+
const CreateSchema = z.object({ name: z.string().min(1) })
|
|
243
|
+
|
|
244
|
+
export const POST = kit.handler(async ({ request, locals }) => {
|
|
245
|
+
kit.guard.require(locals.session, 'user:create')
|
|
246
|
+
const data = await kit.validate.body(request, CreateSchema)
|
|
247
|
+
return kit.response.created(data)
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 2) 基于契约的类型安全 handler(推荐)
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { iam } from '@h-ai/iam'
|
|
255
|
+
import { iamEndpoints } from '@h-ai/iam/api'
|
|
256
|
+
import { kit } from '@h-ai/kit'
|
|
257
|
+
|
|
258
|
+
export const POST = kit.fromContract(iamEndpoints.login, async (input, event) => {
|
|
259
|
+
const result = await iam.auth.login(input)
|
|
260
|
+
if (!result.success) {
|
|
261
|
+
return kit.response.unauthorized(result.error.message)
|
|
262
|
+
}
|
|
263
|
+
return kit.response.ok(result.data)
|
|
264
|
+
})
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 3) CORS + Capacitor 配置
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
export const handle = kit.createHandle({
|
|
271
|
+
cors: {
|
|
272
|
+
origins: ['https://example.com', 'https://*.example.com'],
|
|
273
|
+
capacitor: true, // Capacitor 原生 App WebView Origin
|
|
274
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
275
|
+
credentials: true,
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 4) 需要传输加密时启用 `crypto` 配置
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { crypto } from '@h-ai/crypto'
|
|
284
|
+
import { kit } from '@h-ai/kit'
|
|
285
|
+
|
|
286
|
+
crypto.init()
|
|
287
|
+
|
|
288
|
+
export const handle = kit.createHandle({
|
|
289
|
+
crypto: {
|
|
290
|
+
crypto,
|
|
291
|
+
transport: { requireEncryption: false },
|
|
292
|
+
cookieEncryptionKey: process.env.HAI_KIT_COOKIE_KEY,
|
|
293
|
+
},
|
|
294
|
+
})
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## 核心 API
|
|
298
|
+
|
|
299
|
+
| API | 用途 | 关键点 |
|
|
300
|
+
| --------------------------- | ---------------------- | --------------------------------------------- |
|
|
301
|
+
| `kit.createHandle(config?)` | 创建 SvelteKit Handle | Bearer Token 解析、守卫、中间件、CORS |
|
|
302
|
+
| `kit.sequence(...handles)` | 组合多个 Handle | 洋葱模型顺序执行 |
|
|
303
|
+
| `kit.handler(fn)` | 包装 API 处理函数 | 非控制流异常统一转 500 |
|
|
304
|
+
| `kit.fromContract(ep, fn)` | 契约到 handler | 自动 Zod 入参校验,类型安全 |
|
|
305
|
+
| `kit.guard.*` | 权限守卫 | `require`(throw 式) / `check`(布尔) |
|
|
306
|
+
| `kit.response.*` | 标准化响应 | 统一 `{ success, data?, error?, requestId? }` |
|
|
307
|
+
| `kit.validate.*` | 请求体/查询/参数校验 | 失败 throw Response(SvelteKit 控制流) |
|
|
308
|
+
| `kit.i18n.setLocale(locale)` | 设置请求级 locale | 在 handle 中调用,供服务端模块使用 |
|
|
309
|
+
| `defineEndpoint(def)` | 定义 EndpointDef 契约 | 与 api-client 配合使用 |
|
|
310
|
+
|
|
311
|
+
### EndpointDef 契约
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import { defineEndpoint } from '@h-ai/kit'
|
|
315
|
+
import { z } from 'zod'
|
|
316
|
+
|
|
317
|
+
const myEndpoint = defineEndpoint({
|
|
318
|
+
method: 'POST',
|
|
319
|
+
path: '/api/v1/items',
|
|
320
|
+
input: z.object({ name: z.string() }),
|
|
321
|
+
output: z.object({ id: z.string(), name: z.string() }),
|
|
322
|
+
requireAuth: true,
|
|
323
|
+
})
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## 错误码(常见)
|
|
327
|
+
|
|
328
|
+
### API 响应错误码(`kit.response`)
|
|
329
|
+
|
|
330
|
+
| code | status | 场景 |
|
|
331
|
+
| ------------------ | ------ | ------------------ |
|
|
332
|
+
| `BAD_REQUEST` | 400 | 参数或请求格式错误 |
|
|
333
|
+
| `UNAUTHORIZED` | 401 | 未认证 |
|
|
334
|
+
| `FORBIDDEN` | 403 | 无权限 |
|
|
335
|
+
| `NOT_FOUND` | 404 | 资源不存在 |
|
|
336
|
+
| `CONFLICT` | 409 | 资源冲突 |
|
|
337
|
+
| `VALIDATION_ERROR` | 422 | 数据校验失败 |
|
|
338
|
+
| `INTERNAL_ERROR` | 500 | 未处理异常 |
|
|
339
|
+
|
|
340
|
+
## API 契约范式(端到端类型安全)
|
|
341
|
+
|
|
342
|
+
> `kit.fromContract` 和 `api.call` 共同消费模块 `api/` 下的 `EndpointDef`,实现服务端↔客户端的全链路类型安全 + 运行时 Zod 校验。
|
|
343
|
+
|
|
344
|
+
### 服务端流程(本模块责任)
|
|
345
|
+
|
|
346
|
+
1. 从 `@h-ai/xx/api` 导入 `xxEndpoints`
|
|
347
|
+
2. `kit.fromContract(xxEndpoints.xxx, handler)` → 自动 Zod 校验入参 → handler 获得类型安全的 `input`
|
|
348
|
+
3. 需权限时在 handler 内调用 `kit.guard.require`
|
|
349
|
+
|
|
350
|
+
### 多模块契约示例
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// ─── 存储:预签名上传 ───
|
|
354
|
+
import { storageEndpoints } from '@h-ai/storage/api'
|
|
355
|
+
import { storage } from '$lib/server/init'
|
|
356
|
+
|
|
357
|
+
export const POST = kit.fromContract(storageEndpoints.presignUpload, async (input, event) => {
|
|
358
|
+
kit.guard.require(event.locals.session, 'storage:write')
|
|
359
|
+
const result = await storage.presign.putUrl(input.key, input)
|
|
360
|
+
if (!result.success) {
|
|
361
|
+
return kit.response.internalError(result.error.message)
|
|
362
|
+
}
|
|
363
|
+
return kit.response.ok({ url: result.data, key: input.key })
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
// ─── IAM:登录 ───
|
|
367
|
+
import { iamEndpoints } from '@h-ai/iam/api'
|
|
368
|
+
import { iam } from '$lib/server/init'
|
|
369
|
+
|
|
370
|
+
export const POST = kit.fromContract(iamEndpoints.login, async (input) => {
|
|
371
|
+
const result = await iam.auth.login(input)
|
|
372
|
+
if (!result.success) {
|
|
373
|
+
return kit.response.unauthorized(result.error.message)
|
|
374
|
+
}
|
|
375
|
+
return kit.response.ok(result.data)
|
|
376
|
+
})
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## 常见模式
|
|
380
|
+
|
|
381
|
+
### 契约模式(推荐新项目使用)
|
|
382
|
+
|
|
383
|
+
1. 在模块的 `api/` 目录定义 `EndpointDef`(Zod schema)
|
|
384
|
+
2. 服务端用 `kit.fromContract(endpoint, handler)` 处理
|
|
385
|
+
3. 客户端用 `api.call(endpoint, input)` 调用
|
|
386
|
+
4. 入参/出参全链路类型安全 + 运行时校验
|
|
387
|
+
|
|
388
|
+
### 双构建模式
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
// svelte.config.js
|
|
392
|
+
import { createAdapter } from '@h-ai/kit/adapter'
|
|
393
|
+
|
|
394
|
+
const config = {
|
|
395
|
+
kit: {
|
|
396
|
+
adapter: createAdapter(), // 自动选择 adapter-node 或 adapter-static
|
|
397
|
+
},
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## 浏览器侧使用
|
|
404
|
+
|
|
405
|
+
### 创建统一 API 客户端 — `kit.client`
|
|
406
|
+
|
|
407
|
+
> **所有浏览器端 API 请求必须通过 `apiFetch`,禁止直接 `fetch()`。**
|
|
408
|
+
|
|
409
|
+
在 `src/lib/utils/api.ts` 中一次性创建,全应用共享:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// src/lib/utils/api.ts
|
|
413
|
+
import { kit } from '@h-ai/kit'
|
|
414
|
+
|
|
415
|
+
const client = kit.client.create({ auth: true })
|
|
416
|
+
export const { apiFetch } = client
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
如需传输加密:
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
import { crypto } from '@h-ai/crypto'
|
|
423
|
+
import { kit } from '@h-ai/kit'
|
|
424
|
+
|
|
425
|
+
if (typeof window !== 'undefined') {
|
|
426
|
+
crypto.init()
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const client = kit.client.create({
|
|
430
|
+
transport: { crypto },
|
|
431
|
+
auth: true,
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
export const { apiFetch } = client
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
`apiFetch` 自动处理:
|
|
438
|
+
- **CSRF**:写方法自动读取 `hai_csrf` Cookie 并设置 `X-CSRF-Token`
|
|
439
|
+
- **Bearer Token**:自动从 localStorage 读取并注入 `Authorization` header
|
|
440
|
+
- **传输加密**:可选,首次写请求时自动密钥交换
|
|
441
|
+
|
|
442
|
+
### Token 存储 — `kit.auth.createTokenStore`
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { kit } from '@h-ai/kit'
|
|
446
|
+
|
|
447
|
+
const tokenStore = kit.auth.createTokenStore('hai_access_token')
|
|
448
|
+
tokenStore.set(tokens.accessToken)
|
|
449
|
+
tokenStore.get()
|
|
450
|
+
tokenStore.clear()
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### 选择指南:kit.client vs api-client
|
|
454
|
+
|
|
455
|
+
| 场景 | 推荐 | 原因 |
|
|
456
|
+
|------|------|------|
|
|
457
|
+
| SSR SvelteKit 应用(同源请求) | `kit.client.create().apiFetch` | 自动 CSRF、同源 Cookie 透传、传输加密 |
|
|
458
|
+
| SPA / 原生 App(跨域请求) | `api.init()` + `api.call()` | 完整 Token 管理、401 自动刷新 |
|
|
459
|
+
| 服务端 `+page.server.ts` | 直接调用模块 API | 无需 HTTP 自环 |
|
|
460
|
+
| 契约调用(类型安全) | `api.call(endpoint, input)` | EndpointDef 双向 Zod 校验 |
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## 导出路径
|
|
465
|
+
|
|
466
|
+
| 路径 | 用途 |
|
|
467
|
+
| ------------------- | ------------------------------------------------ |
|
|
468
|
+
| `@h-ai/kit` | 主入口:`kit` 命名空间、类型、`defineEndpoint` |
|
|
469
|
+
| `@h-ai/kit/client` | 浏览器端客户端(单独引用时) |
|
|
470
|
+
| `@h-ai/kit/adapter` | 双构建 adapter(`createAdapter()`) |
|
|
471
|
+
| `@h-ai/kit/vite` | Vite 插件 |
|
|
472
|
+
| `@h-ai/kit/a2a` | A2A 协议集成 |
|
|
473
|
+
| `@h-ai/kit/crypto` | 加密模块集成 |
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## 相关 Skills
|
|
478
|
+
|
|
479
|
+
- `hai-ui`:UI 组件库(svelte.config.js / app.css 配置)
|
|
480
|
+
- `hai-core`:全局配置、日志、HaiResult 基础能力
|
|
481
|
+
- `hai-iam`:Token 认证、角色权限模型
|
|
482
|
+
- `hai-api-client`:客户端契约调用(SPA / 原生 App 场景)
|
|
483
|
+
- `hai-crypto`:传输加密与密钥管理能力
|
|
484
|
+
- `hai-capacitor`:原生 App CORS 场景
|