@h-ai/kit 1.0.0-beta.2 → 1.0.0-beta.20
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 +190 -110
- package/README.ja.md +192 -112
- package/README.md +195 -114
- package/dist/adapter/index.d.ts +8 -7
- package/dist/adapter/index.js.map +1 -1
- package/dist/browser.d.ts +9 -26
- package/dist/browser.js +4 -4
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-JRQPMPQC.js → chunk-BCEBXG4X.js} +2 -2
- package/dist/chunk-BCEBXG4X.js.map +1 -0
- package/dist/{chunk-7EH47MTW.js → chunk-BVGXHIXR.js} +30 -9
- package/dist/chunk-BVGXHIXR.js.map +1 -0
- package/dist/{chunk-5VWPH7DO.js → chunk-GQZCU6PC.js} +14 -6
- package/dist/chunk-GQZCU6PC.js.map +1 -0
- package/dist/{chunk-YKJRQCWR.js → chunk-JL2C5ZGQ.js} +54 -20
- package/dist/chunk-JL2C5ZGQ.js.map +1 -0
- package/dist/{chunk-3OHUSKHI.js → chunk-WYGIKXHE.js} +23 -8
- package/dist/chunk-WYGIKXHE.js.map +1 -0
- package/dist/client/index.d.ts +6 -5
- package/dist/client/index.js +5 -5
- package/dist/client/index.js.map +1 -1
- package/dist/index.d.ts +18 -711
- package/dist/index.js +266 -149
- package/dist/index.js.map +1 -1
- package/dist/{kit-a2a-types-4dapztu2.d.ts → kit-a2a-types-sw7sI-ZT.d.ts} +3 -2
- package/dist/kit-config-BQKQnu8Y.d.ts +182 -0
- package/dist/kit-crypto-types-CqFENBqm.d.ts +47 -0
- package/dist/kit-types-Cr1cnB3M.d.ts +2051 -0
- package/dist/modules/a2a/index.d.ts +9 -5
- package/dist/modules/a2a/index.js +1 -1
- package/dist/modules/crypto/index.d.ts +8 -105
- package/dist/modules/crypto/index.js +3 -149
- package/dist/modules/crypto/index.js.map +1 -1
- package/package.json +22 -22
- package/dist/chunk-3OHUSKHI.js.map +0 -1
- package/dist/chunk-5VWPH7DO.js.map +0 -1
- package/dist/chunk-7EH47MTW.js.map +0 -1
- package/dist/chunk-JRQPMPQC.js.map +0 -1
- package/dist/chunk-YKJRQCWR.js.map +0 -1
- package/dist/kit-client-auth-BnhrkzZF.d.ts +0 -422
- package/dist/kit-crypto-types-CXr888wf.d.ts +0 -200
- package/dist/kit-i18n-EBzZKYN-.d.ts +0 -502
- package/dist/kit-types-BkNdyKj8.d.ts +0 -822
package/README.ja.md
CHANGED
|
@@ -4,94 +4,139 @@
|
|
|
4
4
|
|
|
5
5
|
[フレームワークのホーム](../../README.ja.md) · 関連モジュール:[@h-ai/core](../core/README.ja.md) · [@h-ai/crypto](../crypto/README.ja.md) · [@h-ai/iam](../iam/README.ja.md)
|
|
6
6
|
|
|
7
|
-
## 概要
|
|
8
|
-
|
|
9
7
|
`@h-ai/kit` は hai-framework の SvelteKit 統合モジュールで、共通のリクエストパイプラインと同一オリジンの endpoint ツールを提供します:
|
|
10
8
|
|
|
9
|
+
実行時の操作は `kit.xx` を使います。公開名前空間の型はサーバー側の `KitFunctions` とブラウザー側の `BrowserKitFunctions` です。ブラウザー条件入口では client、CRUD、ページ内 Token、既定の言語操作だけを利用できます。`AuthCookieWriter` はサーバー認証が現在のリクエストの Cookie に書き込む最小契約です。内部 helper を import せずに、再エクスポートしたメソッドや名前空間の宣言を生成できます。
|
|
10
|
+
|
|
11
11
|
- `kit.createHandle()`:セッション解析、ルートガード、middleware チェーン、任意の通信・Cookie 暗号化。
|
|
12
12
|
- `kit.handler()`:API endpoint の例外処理境界。
|
|
13
13
|
- `kit.guard` / `kit.response` / `kit.validate` / `kit.auth` / `kit.client` / `kit.crud`
|
|
14
14
|
|
|
15
15
|
> クロスプラットフォームの公開 HTTP contract は `@h-ai/api-contract` で定義し、`@h-ai/serv` にマウント、`@h-ai/api-client` の型付きクライアントで呼び出します。`@h-ai/kit` は業務 API contract を定義・提供しません。
|
|
16
16
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
- Handle Hook の合成(`createHandle` / `sequence`)
|
|
20
|
-
- 認証・ロール・権限ガード(`guard.require` / `guard.check`)
|
|
21
|
-
- 組み込み Logging / RateLimit / CORS middleware(`createHandle` 設定で有効化)
|
|
22
|
-
- 共通の API 応答とリクエスト検証(Zod)
|
|
23
|
-
- Bearer Token ツール(サーバー login/logout + ブラウザーの Token 保存)
|
|
24
|
-
- 自動 CSRF と通信暗号化に対応するブラウザークライアント
|
|
25
|
-
- 宣言的な CRUD リソース定義(`crud.define`)
|
|
26
|
-
- A2A 統合:既定の検出パスは `/.well-known/agent-card.json`。`authenticate` 設定時は null/undefined が 401 となり、認証未設定時のみ匿名を許可します。API Key の引数は完全な Agent Card の `securitySchemes.apiKey` から取得します。Hook と明示的 A2A ルートは JSON-RPC 解析エラー・SSE キャンセル時の解放処理を共有します。
|
|
27
|
-
- 2 種類のビルドモード(`createAdapter()`)
|
|
28
|
-
|
|
29
|
-
## 使い始める
|
|
17
|
+
## クイックスタート
|
|
30
18
|
|
|
31
19
|
`sequence` は SvelteKit の resolve オプションを保持します。ページ変換は内側から外側へ実行し、preload とシリアライズ対象ヘッダーのフィルターは最初の設定を優先します。
|
|
32
20
|
|
|
33
21
|
### インストール
|
|
34
22
|
|
|
35
23
|
```bash
|
|
36
|
-
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.
|
|
24
|
+
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.8
|
|
37
25
|
npm install svelte @sveltejs/kit zod
|
|
38
26
|
npm install -D @sveltejs/vite-plugin-svelte vite
|
|
39
27
|
```
|
|
40
28
|
|
|
41
|
-
###
|
|
42
|
-
|
|
43
|
-
- `@h-ai/iam`:A2A 用の認証・認可(任意)。A2A 機能を使う前に初期化。
|
|
44
|
-
- `@h-ai/crypto`:通信・Cookie 暗号化(任意)。暗号化を使う前に初期化。
|
|
45
|
-
|
|
46
|
-
### クイックスタート
|
|
47
|
-
|
|
48
|
-
#### プロジェクト設定
|
|
49
|
-
|
|
50
|
-
**svelte.config.js**:
|
|
51
|
-
|
|
52
|
-
```js
|
|
53
|
-
import adapter from '@sveltejs/adapter-node'
|
|
54
|
-
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
55
|
-
|
|
56
|
-
const config = {
|
|
57
|
-
preprocess: [vitePreprocess()],
|
|
58
|
-
compilerOptions: { runes: true },
|
|
59
|
-
kit: {
|
|
60
|
-
adapter: adapter(),
|
|
61
|
-
// 双构建模式:import { createAdapter } from '@h-ai/kit/adapter'
|
|
62
|
-
// adapter: await createAdapter(),
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export default config
|
|
67
|
-
```
|
|
29
|
+
### プロジェクト設定
|
|
68
30
|
|
|
69
31
|
**vite.config.ts**:
|
|
70
32
|
|
|
71
33
|
```ts
|
|
34
|
+
import adapter from '@sveltejs/adapter-node'
|
|
72
35
|
import { sveltekit } from '@sveltejs/kit/vite'
|
|
36
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
73
37
|
import tailwindcss from '@tailwindcss/vite'
|
|
74
38
|
import { defineConfig } from 'vite'
|
|
75
39
|
|
|
76
40
|
export default defineConfig({
|
|
77
|
-
plugins: [
|
|
78
|
-
|
|
79
|
-
|
|
41
|
+
plugins: [
|
|
42
|
+
sveltekit({
|
|
43
|
+
adapter: adapter(),
|
|
44
|
+
preprocess: [
|
|
45
|
+
vitePreprocess()
|
|
46
|
+
],
|
|
47
|
+
compilerOptions: { runes: true },
|
|
48
|
+
// 绝对路径保持 SSR 与浏览器端认证、加密路径一致。
|
|
49
|
+
paths: { base: '', relative: false },
|
|
50
|
+
}),
|
|
51
|
+
tailwindcss(),
|
|
52
|
+
],
|
|
53
|
+
optimizeDeps: {
|
|
54
|
+
exclude: ['bits-ui']
|
|
55
|
+
},
|
|
56
|
+
ssr: {
|
|
57
|
+
noExternal: ['@h-ai/ui', '@h-ai/kit'],
|
|
58
|
+
external: ['@h-ai/core', 'sm-crypto']
|
|
59
|
+
},
|
|
80
60
|
})
|
|
81
61
|
```
|
|
82
62
|
|
|
83
|
-
Crypto または Kit/IAM を使う場合、アプリの本番依存に `sm-crypto@0.5.
|
|
63
|
+
Crypto または Kit/IAM を使う場合、アプリの本番依存に `sm-crypto@0.5.8` を含めます。CLI 生成済みのプロジェクトでは設定済みです。手動導入は `pnpm add sm-crypto@0.5.8` を実行してください。adapter-node は直接の本番依存も参照するため、external 指定だけでは不十分です。
|
|
64
|
+
|
|
65
|
+
## 機能
|
|
66
|
+
|
|
67
|
+
### 用途・入力・出力
|
|
68
|
+
|
|
69
|
+
呼出し断片は本ページで初期化したサービスを使います。変数はアプリ側で用意する要求・Schema・リソース定義・設定を表します。初期化と失敗処理は各機能の例を参照し、HaiResult.success の確認と非同期処理の await を行ってください。
|
|
70
|
+
|
|
71
|
+
| 入口 | 用途 | 入力 | 出力 | 呼出し断片 |
|
|
72
|
+
| ------------------------------------------- | ---------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
|
73
|
+
| `kit.createHandle` | 要求管線 | `HandleConfig: auth/guards/logging/rateLimit/crypto/access` | `SvelteKit Handle` | `kit.createHandle({ logging: true })` |
|
|
74
|
+
| `kit.sequence` | Hook を合成 | `...Handle` | `Handle preserving resolve options` | `kit.sequence(i18nHandle, authHandle)` |
|
|
75
|
+
| `kit.handler` | 端点境界 | `(RequestEvent) => Response \| Promise<Response>` | `SvelteKit request handler` | `kit.handler(async () => kit.response.ok({ ready: true }))` |
|
|
76
|
+
| `kit.guard.check` | 権限確認 | `session, permission` | `boolean` | `kit.guard.check(session, "orders.read")` |
|
|
77
|
+
| `kit.guard.require` | 権限を要求 | `session, permission` | `void; denied access throws Response` | `kit.guard.require(session, "orders.write")` |
|
|
78
|
+
| `kit.guard.requireSensitive` | 操作用途を検証 | `initialized IAM, current RequestEvent request/cookies, operation` | `Promise<void>; invalid proof throws Response` | `kit.guard.requireSensitive(iam, event, "orders.approve")` |
|
|
79
|
+
| `kit.response.ok` | 成功応答 | `data, requestId?` | `Response with HTTP 200 and success data` | `kit.response.ok({ id: "o1" })` |
|
|
80
|
+
| `kit.response.created` | 作成応答 | `data, requestId?` | `Response with HTTP 201 and success data` | `kit.response.created({ id: "o1" })` |
|
|
81
|
+
| `kit.response.noContent` | 本文なし | `none` | `Response with HTTP 204` | `kit.response.noContent()` |
|
|
82
|
+
| `kit.response.error` | 指定エラー | `code, message, status?, requestId?, public details?` | `safe failure Response` | `kit.response.error("app:orders:001", "请求无效", 400)` |
|
|
83
|
+
| `kit.response.badRequest` | 不正な要求 | `message?, requestId?` | `HTTP 400 Response` | `kit.response.badRequest("Invalid request")` |
|
|
84
|
+
| `kit.response.unauthorized` | 未認証 | `message?, requestId?` | `HTTP 401 Response` | `kit.response.unauthorized()` |
|
|
85
|
+
| `kit.response.forbidden` | 権限なし | `message?, requestId?` | `HTTP 403 Response` | `kit.response.forbidden()` |
|
|
86
|
+
| `kit.response.notFound` | 見つからない | `message?, requestId?` | `HTTP 404 Response` | `kit.response.notFound()` |
|
|
87
|
+
| `kit.response.conflict` | 競合 | `message, requestId?` | `HTTP 409 Response` | `kit.response.conflict("Data changed")` |
|
|
88
|
+
| `kit.response.validationError` | 項目検証 | `{ field, message }[], requestId?` | `HTTP 422 Response; errors in safe error.cause` | `kit.response.validationError([{ field: "title", message: "请输入标题" }])` |
|
|
89
|
+
| `kit.response.internalError` | 内部エラー | `message?, requestId?` | `HTTP 500 Response with safe message` | `kit.response.internalError()` |
|
|
90
|
+
| `kit.response.redirect` | リダイレクト | `location, status?` | `redirect Response` | `kit.response.redirect("/auth/login")` |
|
|
91
|
+
| `kit.response.fromResult` | 領域結果を変換 | `HaiResult<T>, httpStatusMap?, requestId?` | `HTTP Response; preserves safe code/status/traceId` | `kit.response.fromResult(result)` |
|
|
92
|
+
| `kit.response.fromError` | 領域エラーを変換 | `HaiError, requestId?` | `safe HTTP failure Response` | `kit.response.fromError(result.error)` |
|
|
93
|
+
| `kit.validate.body` | 本文検証 | `Request, Zod schema` | `Promise<schema data>; invalid input throws Response` | `kit.validate.body(request, InputSchema)` |
|
|
94
|
+
| `kit.validate.query` | クエリー検証 | `URL, Zod schema` | `schema data; invalid input throws Response` | `kit.validate.query(url, QuerySchema)` |
|
|
95
|
+
| `kit.validate.params` | パス検証 | `Record<string,string>, Zod schema` | `schema data; invalid input throws Response` | `kit.validate.params(params, IdSchema)` |
|
|
96
|
+
| `kit.validate.IdParamSchema` | 資源 ID | `{ id: string }` | `Zod parsed ID object` | `kit.validate.IdParamSchema.parse({ id: "o1" })` |
|
|
97
|
+
| `kit.validate.PaginationQuerySchema` | ページング入力 | `page/pageSize/search query` | `normalized pagination object` | `kit.validate.PaginationQuerySchema.parse({ page: "1" })` |
|
|
98
|
+
| `kit.client.create` | 同一オリジン顧客 | `KitClientConfig: auth/transport/recoverSession?` | `KitClient { apiFetch, init, destroy, ready }` | `kit.client.create()` |
|
|
99
|
+
| `kit.client.createAuthActions` | 認証操作 | `{ basePath?, ...auth client options }?` | `AuthPanel-compatible actions` | `kit.client.createAuthActions({ basePath: "/api/auth" })` |
|
|
100
|
+
| `kit.client.installBrowserTransport` | 暗号転送を設定 | `public KitConfig, { crypto, basePath? }` | `void; installs same-origin global fetch wrapper when enabled` | `kit.client.installBrowserTransport(publicKitConfig, { crypto })` |
|
|
101
|
+
| `kit.client.installBrowserTransportFetch` | 独自転送の設定 | `ClientTransportConfig` | `void; installs browser global fetch wrapper` | `kit.client.installBrowserTransportFetch(transportOptions)` |
|
|
102
|
+
| `kit.auth.publicPaths` | 匿名端点一覧 | `none` | `string[]; prepend the host base path when needed` | `kit.auth.publicPaths` |
|
|
103
|
+
| `kit.auth.createApi` | 認証 handler 群 | `initialized IAM service` | `RequestHandler map for login/OTP/reset/config/etc.` | `kit.auth.createApi(iam)` |
|
|
104
|
+
| `kit.auth.login` | パスワード認証 | `Cookie writer, PasswordCredentials, request context?` | `Promise<HaiResult<AuthResult>>; writes token cookies on success` | `kit.auth.login(cookies, { identifier: "alice", password })` |
|
|
105
|
+
| `kit.auth.loginWithOtp` | OTP 認証 | `Cookie writer, OtpCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithOtp(cookies, otpCredentials)` |
|
|
106
|
+
| `kit.auth.loginWithLdap` | LDAP 認証 | `Cookie writer, LdapCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithLdap(cookies, ldapCredentials)` |
|
|
107
|
+
| `kit.auth.loginWithApiKey` | API キー認証 | `Cookie writer, ApiKeyCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithApiKey(cookies, apiKeyCredentials)` |
|
|
108
|
+
| `kit.auth.registerAndLogin` | 登録と認証 | `Cookie writer, RegisterOptions, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.registerAndLogin(cookies, registration)` |
|
|
109
|
+
| `kit.auth.logout` | ログアウト | `Cookie writer, accessToken?` | `Promise<HaiResult<void>>` | `kit.auth.logout(cookies, accessToken)` |
|
|
110
|
+
| `kit.auth.setBrowserToken` | ページ内 Token | `access token string` | `void; no Web Storage persistence` | `kit.auth.setBrowserToken(accessToken)` |
|
|
111
|
+
| `kit.auth.clearBrowserToken` | ページ Token を解除 | `none` | `void` | `kit.auth.clearBrowserToken()` |
|
|
112
|
+
| `kit.crud.define` | CRUD 資源の宣言 | `CrudResourceConfig` | `resource operation object` | `kit.crud.define(resourceDefinition)` |
|
|
113
|
+
| `kit.middleware.rateLimit` | 固定窓の制限 | `{ windowMs, maxRequests, store, ...options }` | `Middleware; 429 on quota and 503 on store failure` | `kit.middleware.rateLimit({ windowMs: 60000, maxRequests: 100, store })` |
|
|
114
|
+
| `kit.middleware.createCacheRateLimitStore` | 共有カウンター | `initialized shared cache, { namespace, allowMemory? }` | `RateLimitStore` | `kit.middleware.createCacheRateLimitStore(cache, { namespace: "orders" })` |
|
|
115
|
+
| `kit.middleware.createMemoryRateLimitStore` | 開発用カウンター | `none; development or explicit test only` | `MemoryRateLimitStore` | `kit.middleware.createMemoryRateLimitStore()` |
|
|
116
|
+
| `kit.createFeedbackHandler` | 管理された問題報告入口 | `{ feedback, resolveContext, ...options }` | `catch-all RequestHandler` | `kit.createFeedbackHandler({ feedback, resolveContext })` |
|
|
117
|
+
| `kit.i18n.setLocale` | 起動時の言語 | `locale string` | `void; do not change process default per request` | `kit.i18n.setLocale("zh-CN")` |
|
|
118
|
+
| `kit.i18n.runWithLocale` | 要求言語の分離 | `locale, callback; Node server only` | `same callback return value` | `kit.i18n.runWithLocale("en-US", () => resolve(event))` |
|
|
84
119
|
|
|
85
|
-
|
|
120
|
+
- Handle Hook の合成(`createHandle` / `sequence`)
|
|
121
|
+
- 認証・ロール・権限ガード(`guard.require` / `guard.check`)
|
|
122
|
+
- 組み込み Logging / RateLimit / CORS middleware(`createHandle` 設定で有効化)
|
|
123
|
+
- 共通の API 応答とリクエスト検証(Zod)
|
|
124
|
+
- Bearer Token ツール(サーバー login/logout + ブラウザーの Token 保存)
|
|
125
|
+
- 自動 CSRF と通信暗号化に対応するブラウザークライアント
|
|
126
|
+
- 宣言的な CRUD リソース定義(`crud.define`)
|
|
127
|
+
- A2A 統合:既定の検出パスは `/.well-known/agent-card.json`。`authenticate` 設定時は null/undefined が 401 となり、認証未設定時のみ匿名を許可します。API Key の引数は完全な Agent Card の `securitySchemes.apiKey` から取得します。Hook と明示的 A2A ルートは JSON-RPC 解析エラー・SSE キャンセル時の解放処理を共有します。
|
|
128
|
+
- 2 種類のビルドモード(`createAdapter()`)
|
|
86
129
|
|
|
87
|
-
|
|
130
|
+
### リクエストパイプラインの構築
|
|
131
|
+
|
|
132
|
+
`/yyy/`などの配下に配置する場合、SvelteKitの`paths.base`とガードが参照する実際のURLを揃えます。`$app/paths`の`resolve('/').replace(/\/$/, '')`(`paths.relative: false`を設定)を`protectedPaths`、`publicPaths`、`loginUrl`に付け、`${base}/api/*`のように指定してください。認証画面は``kit.client.createAuthActions({ basePath: `${base}/api/auth` })``を使用します。Kitはルートグループや配置接頭辞を含むアプリ内`/api/*`を識別し、未認証APIは401、画面は設定したログイン先へ遷移します。生成テンプレートにはこの接続を含みます。
|
|
88
133
|
|
|
89
134
|
以下の Hook 例では、アプリがデータベース、共有キャッシュ、IAM を初期化し、各結果を確認済みとします。リソースの終了はアプリが管理し、リクエストごとに初期化しません。
|
|
90
135
|
|
|
91
136
|
**src/hooks.server.ts**:
|
|
92
137
|
|
|
93
138
|
```typescript
|
|
94
|
-
import type { Handle } from '@sveltejs/kit'
|
|
139
|
+
import type { Handle } from '@sveltejs/kit/hooks'
|
|
95
140
|
import { cache } from '@h-ai/cache'
|
|
96
141
|
import { iam } from '@h-ai/iam'
|
|
97
142
|
import { kit } from '@h-ai/kit'
|
|
@@ -106,7 +151,11 @@ const haiHandle = kit.createHandle({
|
|
|
106
151
|
protectedPaths: ['/admin/*', '/api/*'],
|
|
107
152
|
publicPaths: ['/api/auth/*', '/api/public/*'],
|
|
108
153
|
},
|
|
109
|
-
rateLimit: {
|
|
154
|
+
rateLimit: {
|
|
155
|
+
windowMs: 60_000,
|
|
156
|
+
maxRequests: 100,
|
|
157
|
+
store: kit.middleware.createCacheRateLimitStore(cache, { namespace: 'app' })
|
|
158
|
+
},
|
|
110
159
|
logging: true,
|
|
111
160
|
})
|
|
112
161
|
|
|
@@ -115,7 +164,7 @@ export const handle: Handle = haiHandle
|
|
|
115
164
|
|
|
116
165
|
最初の確定時点から固定ウィンドウを開始し、後続リクエストはTTLを延長しません。storeは必須です。共有Redisの初期化成功を確認し、全ノードのnamespace/windowMs/maxRequestsを一致させます。キャッシュ障害や競合上限は503、超過は429で、get+setやローカル計数へフォールバックしません。createMemoryRateLimitStoreは開発または明示HAI_E2E/HAI_E2E_MOCKテスト用で、常駐タイマーを使わず参照解放で回収されます。1リクエスト最大64項目を遅延走査し、上限10000ウィンドウです。満杯時は有効な計数を追い出さず新キーを503にし、外部storeのライフサイクルは作成者が管理します。本番でテスト変数を設定しません。ヘッダー時刻は推定、実際の期間はRedis TTLで制御するため時計を同期します。キーは信頼済みadapterのgetClientAddressのみ使い、等価IPv6/IPv4表記を正規化します。生の転送ヘッダーを信頼せず、代理信頼はadapterで設定します。
|
|
117
166
|
|
|
118
|
-
|
|
167
|
+
### API endpoint
|
|
119
168
|
|
|
120
169
|
```typescript
|
|
121
170
|
import { kit } from '@h-ai/kit'
|
|
@@ -133,7 +182,7 @@ export const POST = kit.handler(async ({ request, locals }) => {
|
|
|
133
182
|
})
|
|
134
183
|
```
|
|
135
184
|
|
|
136
|
-
|
|
185
|
+
### ブラウザークライアント
|
|
137
186
|
|
|
138
187
|
`kit.client.create()` は SvelteKit の同一オリジン endpoint 向けです。Web・アプリ・ミニプログラムからクロスオリジンで公開 API を使う場合は `@h-ai/api-client` を使用してください。
|
|
139
188
|
|
|
@@ -147,7 +196,7 @@ export const { apiFetch } = client
|
|
|
147
196
|
|
|
148
197
|
```typescript
|
|
149
198
|
// 使用
|
|
150
|
-
import { apiFetch } from '
|
|
199
|
+
import { apiFetch } from '#lib/utils/api.js'
|
|
151
200
|
|
|
152
201
|
const response = await apiFetch('/api/users', { method: 'GET' })
|
|
153
202
|
```
|
|
@@ -157,21 +206,23 @@ const response = await apiFetch('/api/users', { method: 'GET' })
|
|
|
157
206
|
>
|
|
158
207
|
> 同一オリジンの SvelteKit endpoint は httpOnly Cookie を使い、サーバーで検証します。ブラウザーの Bearer Header が必要なら `kit.auth.setBrowserToken()` でページ内メモリーに保存してから `kit.client.create({ auth: true })` を使います。独自の `BrowserTokenStore` もメモリーまたはプラットフォームの安全なストレージを使用し、機密の認証情報を Web Storage に永続化しないでください。
|
|
159
208
|
|
|
160
|
-
|
|
209
|
+
### 同一オリジンの通信暗号化
|
|
161
210
|
|
|
162
211
|
kit の通信暗号化は `@h-ai/crypto` に委譲します。サーバーは `crypto.transport.createServer({ serverKeyRing, keyStore })` で管理器を作成し、ブラウザーは `kit.client.installBrowserTransport(kitConfig, { crypto })` の 1 行で同一オリジンの fetch ラッパーを導入します。サーバーには管理済み鍵環と共有 registry も渡します。
|
|
163
212
|
|
|
164
|
-
サブディレクトリでは `$app/paths` の `
|
|
213
|
+
サブディレクトリでは `$app/paths` の `resolve('/').replace(/\/$/, '')`(`paths.relative: false` を設定) を使用します。サーバーの `crypto.transport` に `basePath: base`、ブラウザーには `installBrowserTransport(kitConfig, { crypto, basePath: base })` を渡し、独立した `kit.client.create` の `transport.basePath` も同じ値にします。既定の空文字列はドメイン直下です。ホストのビルド時ディレクトリを使う実行時引数であり、YAML に重複して設定したり、転送ヘッダーから推測したりしません。鍵交換・除外・容量のパスは引き続き完全なリクエストパスです。CLI の SSR テンプレートは両側を設定します。
|
|
165
214
|
|
|
166
215
|
**src/hooks.server.ts**:
|
|
167
216
|
|
|
168
217
|
```ts
|
|
169
|
-
import {
|
|
170
|
-
import { appKitConfig } from '$lib/server/kit-config'
|
|
218
|
+
import { resolve } from '$app/paths'
|
|
171
219
|
import { cache } from '@h-ai/cache'
|
|
172
220
|
import { createRedisTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
|
|
173
221
|
import { iam } from '@h-ai/iam'
|
|
174
222
|
import { kit } from '@h-ai/kit'
|
|
223
|
+
import { appKitConfig } from '#lib/server/kit-config.js'
|
|
224
|
+
|
|
225
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
175
226
|
|
|
176
227
|
const cryptoReady = await crypto.init()
|
|
177
228
|
if (!cryptoReady.success)
|
|
@@ -209,13 +260,15 @@ export const handle = kit.createHandle({
|
|
|
209
260
|
|
|
210
261
|
```svelte
|
|
211
262
|
<script lang='ts'>
|
|
212
|
-
import { browser } from '$app/
|
|
213
|
-
import {
|
|
263
|
+
import { browser } from '$app/env'
|
|
264
|
+
import { resolve } from '$app/paths'
|
|
214
265
|
import { page } from '$app/state'
|
|
215
266
|
import { crypto } from '@h-ai/crypto'
|
|
216
267
|
import { kit } from '@h-ai/kit'
|
|
217
268
|
import { untrack } from 'svelte'
|
|
218
269
|
|
|
270
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
271
|
+
|
|
219
272
|
// 一次性安装:内部按 _kit.yml 决定是否启用 transport,并预热 crypto
|
|
220
273
|
if (browser) {
|
|
221
274
|
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), { crypto, basePath: base })
|
|
@@ -239,17 +292,15 @@ export const { apiFetch } = kit.client.create()
|
|
|
239
292
|
|
|
240
293
|
transport は既定で同一オリジンの `/api/*`、SvelteKit の `__data.json`、鍵交換 endpoint に適用します。ページ文書・静的ファイルは平文で `X-Client-Id` は不要です。アップロードなどの `multipart/form-data` はそのまま送信し、サーバーの `excludePaths` で明示的に許可してください。
|
|
241
294
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
空の POST も空平文の認証封装を送ります。GET/HEAD は X-Hai-Transport ヘッダー、204/304/HEAD など本文なし応答も同じヘッダーを使います。他の要求・応答は JSON 本文と X-Encrypted: true を使います。クライアントは元の要求コンテキストと HTTP status を照合し、マーカー欠落、旧形式、認証失敗では reject します。平文 endpoint は Serv/Kit の `encryption: disabled` ルールまたはホストの `excludePaths` で明示的に許可し、呼出側も通常の要求を選びます。API Client/Kit のラッパーは自身の `excludePaths` を使用します。CORS では X-Hai-Transport を許可・公開してください。鍵交換の公開鍵認証は TLS に依存し、IAM の業務認可は別に必要です。
|
|
295
|
+
転送は認証済み v2 封装を使い、署名と MAC を確認してから復号し、方法・パス・時刻・応答状態を束縛します。共有 registry が nonce を原子的に消費します。TLS・信頼する代理・業務認可・冪等性はホストの責任です。独自 CORS 入口は X-Hai-Transport を許可・公開してください。詳細は[転送プロトコル](../crypto/TRANSPORT-PROTOCOL.md)を参照してください。
|
|
245
296
|
|
|
246
297
|
`transport.maxPlaintextBytes` でリクエストとレスポンスの JSON 平文容量を共通設定します。単位は UTF-8 バイト、既定値は 1 MiB、有効範囲は 1 KiB~64 MiB の整数です。クライアントは鍵交換で上限を取得し、`resolveTransportBodyLimits()` が暗号文とプロキシ容量(平文の 2 倍 + 32 KiB)を算出します。鍵交換、公開鍵、認証ヘッダー、防リプレイ制約は独立です。超過時は元ストリームを停止して reader を解放します。要求は 413、応答は暗号化された 500 容量エラーとなり、`TransportBodyTooLargeError` / `hai:crypto:070` で識別できます。 [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
|
|
247
298
|
|
|
248
299
|
通常業務は `transport.maxPlaintextBytes`(auto/省略は 1 MiB)を維持します。Feedback の受理・証拠・添付・ZIP ダウンロードは `routePlaintextBytes` と `feedback.budget.maxTransportBytes` で別途宣言します。`*` は完全な1パス区間のみ一致し、ホストの明示値が優先です。ハンドシェイク、adapter-node、プロキシは経路の最大値を使用します。HTTP アダプターは個別宣言された 1 MiB 超の資料経路を最大2件、通常業務を別枠で16件まで許可し、応答完了・失敗時に解放します。混雑は503 / `hai:crypto:072` を返し、業務書込は自動再送しません。
|
|
249
300
|
|
|
250
|
-
|
|
301
|
+
Serv/Kit は要求の結合、署名、MAC を検証した後、時刻の許容範囲を超えた要求に暗号化した HTTP 400 を返します。クライアントは `TransportTimeError`(`hai:crypto:071`)をスローし、検証時のサーバー UTC 時刻を表示して、自動時刻同期を有効にするか手動で時刻を設定してから再読み込みするよう案内します。`serverTime` は Unix ミリ秒です。旧サーバーが値を返さない場合も時刻調整の案内を表示します。`api-client` は `VALIDATION_FAILED` に変換し、`cause.transportCode` と `cause.serverTime` を保持します。標準認証アクションは検証済みの通信時刻エラーを `HaiResult` に変換し、サーバー時刻と時刻調整の案内を AuthPanel に渡します。
|
|
251
302
|
|
|
252
|
-
|
|
303
|
+
### `_kit.yml` による共通 transport 設定
|
|
253
304
|
|
|
254
305
|
`@h-ai/kit` は `KitConfigSchema` / `resolveKitConfig()` を提供し、同じ `_kit.yml` から
|
|
255
306
|
`hooks.server.ts` とブラウザーの `kit.client.installBrowserTransport()` の両方を設定できます:
|
|
@@ -282,7 +333,7 @@ export const publicKitConfig = { transport: appKitConfig.transport }
|
|
|
282
333
|
`src/routes/+layout.server.ts` から上記ブラウザーレイアウトへ公開部分だけ渡します。ブラウザー側からサーバー設定をインポートしません。
|
|
283
334
|
|
|
284
335
|
```ts
|
|
285
|
-
import { publicKitConfig } from '
|
|
336
|
+
import { publicKitConfig } from '#lib/server/kit-config.js'
|
|
286
337
|
|
|
287
338
|
export function load() {
|
|
288
339
|
return { kitConfig: publicKitConfig }
|
|
@@ -293,28 +344,10 @@ export function load() {
|
|
|
293
344
|
サーバーのみで設定を読む場合は `core.config.validate('kit', KitConfigSchema)` も利用できます。
|
|
294
345
|
完全な `_kit.yml` はサーバーだけで読みます。内部の代理ネットワークやポリシーファイルのパスを含むため、`+layout.server.ts` は `{ kitConfig: publicKitConfig }` のような公開投影だけを返し、ブラウザーは transport を使用します。`?raw` で設定全体やポリシーをブラウザーへ含めず、秘密鍵も記載しないでください。
|
|
295
346
|
|
|
296
|
-
###
|
|
297
|
-
|
|
298
|
-
`kit.createHandle()` の設定項目:
|
|
299
|
-
|
|
300
|
-
| フィールド | 型 | 説明 |
|
|
301
|
-
| ------------ | ---------------------------------------- | --------------------------------------------------------------- |
|
|
302
|
-
| `auth` | `HandleAuthConfig` | 認証(verifyToken / protectedPaths / publicPaths / loginUrl) |
|
|
303
|
-
| `rateLimit` | `object \| false` | レート制限(windowMs / maxRequests) |
|
|
304
|
-
| `logging` | `boolean \| object` | リクエストログ(既定 `true`、`{ logBody: true }` で本文も記録) |
|
|
305
|
-
| `crypto` | `HookCryptoConfig` | 通信暗号化 + Cookie 暗号化 |
|
|
306
|
-
| `guards` | `GuardConfig[]` | 独自ガード(自動認証ガードの後に実行) |
|
|
307
|
-
| `middleware` | `Middleware[]` | 独自 middleware(組み込み middleware の後に実行) |
|
|
308
|
-
| `a2a` | `HandleA2AOperations \| HandleA2AConfig` | A2A 統合(Agent Card + JSON-RPC) |
|
|
309
|
-
| `onError` | `function` | 独自のエラー処理 |
|
|
310
|
-
|
|
311
|
-
リクエスト/レスポンス概要は `trace` レベルで記録します。ログは既定で有効、`logBody` は既定で無効です。クエリーは元の `request.url`、パスは SvelteKit の現在の `event.url.pathname` を使用します。追跡付き `event.url.search/searchParams` を読まないため、ログを有効にした静的ページも事前生成できます。クエリーと任意の JSON 本文ログでは password/token/secret、認証、API キーなどを秘匿します。本文は複製から読み、アプリ側で引き続き利用できます。
|
|
312
|
-
|
|
313
|
-
リクエストの自動監査は `audit.isInitialized` の場合だけ実行します。静的サイトや Audit を選択しないアプリは handle のために初期化する必要がありません。各リクエストで確認するため、handle 作成後のモジュール初期化にも対応します。Audit を選択したアプリは `audit.init()` の結果を確認し、失敗時は起動を止めます。明示的な `audit.log/capture` の未初期化エラーは維持し、初期化済みモジュールの実際の書込み失敗は `Request audit failed` として記録します。必須の業務監査では明示的な結果を確認し、業務トランザクションの規則を適用してください。
|
|
314
|
-
|
|
315
|
-
`crypto.encryptedCookies` を設定する場合、`crypto.cookieEncryptionKey` または `HAI_KIT_COOKIE_KEY` が必要です。鍵がなければ `createHandle()` ですぐエラーになります。保護対象 Cookie の平文・破損暗号文・復号失敗は未存在として扱い、暗号化失敗時も平文保存に切り替えません。
|
|
347
|
+
### 依存関係
|
|
316
348
|
|
|
317
|
-
|
|
349
|
+
- `@h-ai/iam`:A2A 用の認証・認可(任意)。A2A 機能を使う前に初期化。
|
|
350
|
+
- `@h-ai/crypto`:通信・Cookie 暗号化(任意)。暗号化を使う前に初期化。
|
|
318
351
|
|
|
319
352
|
### エクスポートパス
|
|
320
353
|
|
|
@@ -338,12 +371,6 @@ kit.auth.createApi(iam) の標準 handler と kit.auth.publicPaths を使用し
|
|
|
338
371
|
|
|
339
372
|
`kit.auth.logout(cookies, accessToken)` は `Promise<HaiResult<void>>` を返します。Token がなければ Cookie のみ消去して成功し、Token があるのに認証操作が未設定なら失敗します。サーバー結果にかかわらず Cookie は消去しますが、呼出側は失敗を HTTP 応答や UI に伝えます。ローカル消去はサーバー失効の確認ではありません。制御フロー例外は引き続き伝播します。
|
|
340
373
|
|
|
341
|
-
### エラー処理
|
|
342
|
-
|
|
343
|
-
- API endpoint は `kit.handler()` でラップすることを推奨。
|
|
344
|
-
- `kit.validate.body` / `kit.guard.require` の `Response` は `kit.handler` が API 制御フローとして処理します。ページ `load` では `kit.guard.check` と SvelteKit `error` を使い、検索条件は `safeParse` で検証してネイティブなページエラーに変換します。API Response をそのまま throw しません。
|
|
345
|
-
- 制御フロー以外の例外は `kit.handler()` が `500 INTERNAL_ERROR` 応答に変換。
|
|
346
|
-
|
|
347
374
|
### CRUD fields
|
|
348
375
|
|
|
349
376
|
宣言型 CRUD 項目は UI フォームの幅を指定する `colSpan: 'auto' | 1 | 2` に対応します。`validation.message` は文字列または `() => string` を受け取り、現在の言語で表示できます。レイアウトは `CrudPage.form` で設定し、サーバーでも検証してください。
|
|
@@ -354,7 +381,7 @@ Node リクエストと SSR は `core.i18n.runWithLocale(locale, () => handleReq
|
|
|
354
381
|
|
|
355
382
|
ブラウザーはアプリ全体の `setGlobalLocale` / `createLocaleStore` を使います。SSR の UI store 初期化と setter は自身の状態のみ変更し、メッセージはリクエストコンテキストを参照します。`runWithLocale` は Node 入口専用です。
|
|
356
383
|
|
|
357
|
-
|
|
384
|
+
### フィードバック入口とリクエスト関連付け
|
|
358
385
|
|
|
359
386
|
`kit.createFeedbackHandler` は `/api/feedback/[...path]/+server.ts` の GET/POST/PATCH に使用できます。serv と同じ api-contract schema を再利用し、入力ストリームの容量とクライアント認証情報の混入を制限します。resolveContext は検証済み locals.session から範囲・権限を作り、null は拒否します。既存 createHandle の認証・CSRF・通信暗号化を維持してください。
|
|
360
387
|
|
|
@@ -379,35 +406,21 @@ export const POST = handler
|
|
|
379
406
|
export const PATCH = handler
|
|
380
407
|
```
|
|
381
408
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
HTTP エラーには安全な翻訳文と追跡番号を含めます。ToastContainer は番号を既定で非表示にし、調査情報のアイコンから表示・コピーできます。同じ ID を error.traceId と X-Trace-Id に返します。requestId を traceId として共有し、ブラウザーとサーバーのログを関連付けます。脱機密化した例外・スタック・操作情報はログに残し、内部 cause/ext は公開しません。項目検証は翻訳済みの項目名とメッセージだけを返します。
|
|
385
|
-
|
|
386
|
-
HTTP/UI 境界には core.error.toPublic(error, traceId?)、再翻訳後の表示には core.error.formatMessage(error) を使い、ID を重複追加しません。ログには完全な Error を渡してください。DB ログにはドライバーコード、脱機密化したメッセージ・スタック、表・列を残し、SQL 本文、バインド値と接続認証情報を記録しません。
|
|
387
|
-
|
|
388
|
-
audit の初期化後、createHandle は検証済みセッションの身元/役割、プラットフォーム getClientAddress と要求 ID を業務 audit.log に関連付けます。request.execute はルート雛形・メソッド・状態・成功/失敗/拒否を記録し、守衛リダイレクトと SvelteKit HTTP 制御フローも含みます。要求本文・内部エラーは複製しません。capture.enabled で自動記録、capture.includeReads で成功読取を制御し、失敗した読取は既定で記録します。業務完了/失敗はアプリが明示的に記録し、自動記録を無効にしても業務 log は維持します。
|
|
389
|
-
|
|
390
|
-
## 任意の対話チャレンジ
|
|
409
|
+
### 任意の対話チャレンジ
|
|
391
410
|
|
|
392
411
|
kit.auth.createApi(iam).createCaptcha を POST /api/auth/captcha として公開します。kit.auth.publicPaths は対応済みです。kit.client.createAuthActions は captcha を保持し createCaptcha を提供し、AuthPanel が自動求解します。初回/期限切れ変更には /api/auth/password/renew も公開します。IAM がサーバーで証明を強制検証します。
|
|
393
412
|
|
|
394
413
|
方針・既定値・復旧範囲は次を参照: [IAM](../iam/README.ja.md)。
|
|
395
414
|
|
|
396
|
-
|
|
415
|
+
### 期限切れセッションの画面内復帰
|
|
397
416
|
|
|
398
417
|
`kit.client.create({ recoverSession })` によって現在の画面と入力を保持して再ログインできます。UI の `createSessionRecovery` / `SessionRecoveryDialog` を使い、ルートで公開方針と元の利用者を設定し、`recovery.recover` と標準認証 actions を渡して破棄時に cancel します。IAM は発行前に `expectedUserId` を確認し、通常の CAPTCHA・ロック・改密・アクセス規則を適用します。認証拒否が明確な要求だけ1回続行でき、取消・5xx・タイムアウト・結果不明の書込は再送しません。旧セッションの重要操作証明は再確認が必要な場合があります。標準認証 actions は自身の復帰再試行を無効にします。独自認証は apiFetch の第3引数 `{ recoverSession: false }` で再帰を防ぎます。terminalType(web/app/desktop/api、既定 web)は分類用で機器認証ではありません。
|
|
399
418
|
|
|
400
|
-
|
|
419
|
+
### ページ離脱時のセッション方針
|
|
401
420
|
|
|
402
421
|
`session.endOnPageHide` の既定値は `false` です。ホストは `actions.bindPageExit({ enabled: config.session.endOnPageHide })` を呼び、破棄時に戻り値の解除関数を実行します。標準 admin/H5/fullstack テンプレートは接続済みです。BFCache 以外の `pagehide`(再読み込み、文書全体の移動を含む)で POST `/auth/session/close` を最善努力で送信し、保持する access と関連 refresh を失効します。既存の CSRF/暗号化転送を使用し、`keepalive` を付け、自動更新しません。古い応答が新しいログインを上書きしないよう Cookie/ローカル資格情報を消去しません。明示的ログアウトは通常どおり失効と消去を行います。ブラウザーが離脱要求を送らない場合もあるため、業務に応じてサーバーの `idleTimeout`/`absoluteTimeout` を併用してください。閉じた瞬間の失効は保証できません。
|
|
403
422
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
設定 transport.maxPlaintextBytes は 1 KiB~64 MiB の明示整数または auto を受け付けます。auto/省略は undefined に正規化され、要件なしの既定は 1 MiB、フィードバックのホストでは crypto.resolveTransportBodyLimits の requiredPlaintextBytes で統合します。明示上限の不足は起動/梱包時の失敗とし、上書きしません。プロキシ・adapter-node・ハンドシェイク・実行時の予算を一致させ、添付・展開・行数の制限は独立して検証します。
|
|
407
|
-
|
|
408
|
-
重要操作では `reauthorize: () => sensitiveActions.authorize(operation)` を Kit apiFetch の第3引数、または typed client の options.context に渡します。セッション更新・復帰または明確な証明拒否の後、新しい用途証明で1回だけ続行します。取消時は追加の書込を送らず、コールバックがなければ旧証明を再送しません。
|
|
409
|
-
|
|
410
|
-
## リクエストごとのアクセス制御
|
|
423
|
+
### リクエストごとのアクセス制御
|
|
411
424
|
|
|
412
425
|
パスルールの `encryption` は required / disabled、省略時はホスト設定です。disabled でもアクセス条件と認証は検証し、フレームワークの暗号化信封は拒否します。required は独自 JSON エンドポイントにも適用でき、`excludePaths` より優先します。Crypto・管理鍵リング・registry が必要で、反対指定の重複は Handle 作成時に失敗します。crypto 対応 CLI テンプレートはサーバーとブラウザーを装配し、本番では db と共有 DB を使用します。非公開ポリシーと代理設定はブラウザーに渡さず、明文呼出側は公開の転送除外設定に従います。
|
|
413
426
|
|
|
@@ -417,6 +430,73 @@ kit.auth.createApi(iam).createCaptcha を POST /api/auth/captcha として公開
|
|
|
417
430
|
|
|
418
431
|
既定 `trustedProxies: auto` は通常のホストで転送ヘッダーを無視し、管理対象 Compose の非公開バックエンド構成だけでコンテナネットワークを採用します。独自の入口は実際の代理 IP/CIDR を指定し、`[]` で信頼を無効化します。解決後の `event.getClientAddress()`、ポリシー、IAM、監査は同じ接続元を使います。外側の代理・源 IP の喪失・環境変数と復旧は[リクエスト制御手引き](../../docs/usage/configuration/request-access.md)を参照してください。
|
|
419
432
|
|
|
433
|
+
## 設定
|
|
434
|
+
|
|
435
|
+
### 用途に応じた設定入口
|
|
436
|
+
|
|
437
|
+
| 設定入口 | 用途 | 入力 | 効果 | 例 |
|
|
438
|
+
| ----------------------------------------- | -------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------- |
|
|
439
|
+
| `server.host / server.port` | リスナー | host string, port integer; HOST/PORT override | resolved Kit server config; default 127.0.0.1:3000 | `server: { host: 127.0.0.1, port: 3000 }` |
|
|
440
|
+
| `transport` | 同一オリジン暗号転送 | false or keyExchangePath/excludePaths/byte/time/route budgets | public transport config for hook and browser; no private keys | `transport: { maxPlaintextBytes: auto }` |
|
|
441
|
+
| `trustedProxies` | 信頼する代理 | auto or explicit IP/CIDR list; [] disables forwarded headers | normalized client address for access policy | `trustedProxies: []` |
|
|
442
|
+
| `access` | 要求アクセス方針 | false or independent policy configuration | per-request IP/time/path checks; not business ownership | `access: false` |
|
|
443
|
+
| `createHandle.auth / guards / middleware` | ホストの認証管線 | verifyToken/operations, paths and guard/middleware callbacks | RequestEvent locals/session and rejection Response | `kit.createHandle({ auth: authOptions })` |
|
|
444
|
+
| `createHandle.rateLimit` | 共有レート制限 | windowMs, maxRequests and explicit atomic store | fixed window; 429 over quota, 503 on unavailable storage | `kit.createHandle({ rateLimit: limitOptions })` |
|
|
445
|
+
| `createHandle.crypto` | 非公開依存を注入 | initialized crypto, serverKeyRing/keyStore, encryptedCookies/key | authenticated payload/cookies; ring stays server-only | `kit.createHandle({ crypto: cryptoOptions })` |
|
|
446
|
+
| `client.create` | ブラウザー要求設定 | auth memory store, CSRF names, transport, recoverSession | same-origin apiFetch plus init/destroy/ready | `kit.client.create({ auth: true })` |
|
|
447
|
+
|
|
448
|
+
pnpm で Svelte アプリを構築する工程では、`patchedDependencies` に `deepmerge@4.3.1` の補丁を登録し、実際の patch ファイルも保持してください。DaisyUI を使う場合はスタイル補丁も必要です。CLI の生成工程には該当項目が含まれます。npm パッケージを導入しても、このリポジトリーの pnpm 補丁設定は自動伝播しません。手動接入では該当する[補丁ファイル](../../patches/)と登録を同期して依存を再インストールしてください。
|
|
449
|
+
|
|
450
|
+
`kit.createHandle()` の設定項目:
|
|
451
|
+
|
|
452
|
+
| フィールド | 型 | 説明 |
|
|
453
|
+
| ------------ | ---------------------------------------- | --------------------------------------------------------------- |
|
|
454
|
+
| `auth` | `HandleAuthConfig` | 認証(verifyToken / protectedPaths / publicPaths / loginUrl) |
|
|
455
|
+
| `rateLimit` | `object \| false` | レート制限(windowMs / maxRequests) |
|
|
456
|
+
| `logging` | `boolean \| object` | リクエストログ(既定 `true`、`{ logBody: true }` で本文も記録) |
|
|
457
|
+
| `crypto` | `HookCryptoConfig` | 通信暗号化 + Cookie 暗号化 |
|
|
458
|
+
| `guards` | `GuardConfig[]` | 独自ガード(自動認証ガードの後に実行) |
|
|
459
|
+
| `middleware` | `Middleware[]` | 独自 middleware(組み込み middleware の後に実行) |
|
|
460
|
+
| `a2a` | `HandleA2AOperations \| HandleA2AConfig` | A2A 統合(Agent Card + JSON-RPC) |
|
|
461
|
+
| `onError` | `function` | 独自のエラー処理 |
|
|
462
|
+
|
|
463
|
+
リクエスト/レスポンス概要は `trace` レベルで記録します。ログは既定で有効、`logBody` は既定で無効です。クエリーは元の `request.url`、パスは SvelteKit の現在の `event.url.pathname` を使用します。追跡付き `event.url.search/searchParams` を読まないため、ログを有効にした静的ページも事前生成できます。クエリーと任意の JSON 本文ログでは password/token/secret、認証、API キーなどを秘匿します。本文は複製から読み、アプリ側で引き続き利用できます。
|
|
464
|
+
|
|
465
|
+
リクエストの自動監査は `audit.isInitialized` の場合だけ実行します。静的サイトや Audit を選択しないアプリは handle のために初期化する必要がありません。各リクエストで確認するため、handle 作成後のモジュール初期化にも対応します。Audit を選択したアプリは `audit.init()` の結果を確認し、失敗時は起動を止めます。明示的な `audit.log/capture` の未初期化エラーは維持し、初期化済みモジュールの実際の書込み失敗は `Request audit failed` として記録します。必須の業務監査では明示的な結果を確認し、業務トランザクションの規則を適用してください。
|
|
466
|
+
|
|
467
|
+
`crypto.encryptedCookies` を設定する場合、`crypto.cookieEncryptionKey` または `HAI_KIT_COOKIE_KEY` が必要です。鍵がなければ `createHandle()` ですぐエラーになります。保護対象 Cookie の平文・破損暗号文・復号失敗は未存在として扱い、暗号化失敗時も平文保存に切り替えません。
|
|
468
|
+
|
|
469
|
+
鍵は 32 桁の16進文字列です。SM4-CBC と HMAC-SHA256 の鍵を別々に導出し、バージョン・Cookie 名・IV・暗号文を認証してから復号するため、改ざんや名前間の置換を拒否します。認証できない Cookie は無効として扱います。
|
|
470
|
+
|
|
471
|
+
認証 Cookie は `NODE_ENV` または `HAI_CORE_ENV` が `production` の場合に `Secure` を既定で有効にします。`HAI_E2E` / `HAI_E2E_MOCK` は Cookie の安全要件を緩和しません。本番のレート制限も両方の環境変数を認識し、明示的なテストフラグだけがメモリカウンターを許可します。
|
|
472
|
+
|
|
473
|
+
### 転送容量とモジュール予算
|
|
474
|
+
|
|
475
|
+
設定 transport.maxPlaintextBytes は 1 KiB~64 MiB の明示整数または auto を受け付けます。auto/省略は undefined に正規化され、要件なしの既定は 1 MiB、フィードバックのホストでは resolveTransportBodyLimits の requiredPlaintextBytes で統合します。明示上限の不足は起動/梱包時の失敗とし、上書きしません。プロキシ・adapter-node・ハンドシェイク・実行時の予算を一致させ、添付・展開・行数の制限は独立して検証します。
|
|
476
|
+
|
|
477
|
+
重要操作では `reauthorize: () => sensitiveActions.authorize(operation)` を Kit apiFetch の第3引数、または typed client の options.context に渡します。セッション更新・復帰または明確な証明拒否の後、新しい用途証明で1回だけ続行します。取消時は追加の書込を送らず、コールバックがなければ旧証明を再送しません。
|
|
478
|
+
|
|
479
|
+
## エラー処理
|
|
480
|
+
|
|
481
|
+
- API endpoint は `kit.handler()` でラップすることを推奨。
|
|
482
|
+
- `kit.validate.body` / `kit.guard.require` の `Response` は `kit.handler` が API 制御フローとして処理します。ページ `load` では `kit.guard.check` と SvelteKit `error` を使い、検索条件は `safeParse` で検証してネイティブなページエラーに変換します。API Response をそのまま throw しません。
|
|
483
|
+
- 制御フロー以外の例外は `kit.handler()` が `500 INTERNAL_ERROR` 応答に変換。
|
|
484
|
+
|
|
485
|
+
### エラー表示とログの関連付け
|
|
486
|
+
|
|
487
|
+
`kit.response.fromError/fromResult` は安全なフィールド検証 `cause`、明示的な `publicExt`(`ext` に変換)、`httpStatus` を保持します。内部原因や拡張情報は公開せず、モジュール失敗ごとに関連ログを一度だけ記録します。
|
|
488
|
+
|
|
489
|
+
HTTP/UI 境界で業務メッセージを公開するのは、明示された有効な4xx状態のみです。`httpStatus` が未指定または不正な場合は500として安全なメッセージを返します。独自の業務エラーには完全なエラー定義または明示的な状態を指定してください。
|
|
490
|
+
|
|
491
|
+
`kit.response.validationError(errors)` も同じ許可リストを使い、`error.cause` に翻訳済みの `field/message` だけを返します。拒否された値、SQL、追加項目は公開せず、`details.errors` は使いません。
|
|
492
|
+
応答ヘルパー、認証ガード、レート制限は `HaiCommonError` と `httpStatus`・`traceId`・`X-Trace-Id` を共用します。未認証は `UNAUTHORIZED.code`、権限不足は `FORBIDDEN.code`、頻度超過は `RATE_LIMIT_EXCEEDED.code`(429)。制限ストアの障害は安全な503応答を返し、原因はサーバーログに保持します。
|
|
493
|
+
|
|
494
|
+
HTTP エラーには安全な翻訳文と追跡番号を含めます。ToastContainer は番号を既定で非表示にし、調査情報のアイコンから表示・コピーできます。同じ ID を error.traceId と X-Trace-Id に返します。requestId を traceId として共有し、ブラウザーとサーバーのログを関連付けます。脱機密化した例外・スタック・操作情報はログに残し、内部 cause/ext は公開しません。項目検証は翻訳済みの項目名とメッセージだけを返します。
|
|
495
|
+
|
|
496
|
+
HTTP/UI 境界には core.error.toPublic(error, traceId?)、再翻訳後の表示には core.error.formatMessage(error) を使い、ID を重複追加しません。ログには完全な Error を渡してください。DB ログにはドライバーコード、脱機密化したメッセージ・スタック、表・列を残し、SQL 本文、バインド値と接続認証情報を記録しません。
|
|
497
|
+
|
|
498
|
+
audit の初期化後、createHandle は検証済みセッションの身元/役割、プラットフォーム getClientAddress と要求 ID を業務 audit.log に関連付けます。request.execute はルート雛形・メソッド・状態・成功/失敗/拒否を記録し、守衛リダイレクトと SvelteKit HTTP 制御フローも含みます。要求本文・内部エラーは複製しません。capture.enabled で自動記録、capture.includeReads で成功読取を制御し、失敗した読取は既定で記録します。業務完了/失敗はアプリが明示的に記録し、自動記録を無効にしても業務 log は維持します。
|
|
499
|
+
|
|
420
500
|
## 開発とテスト
|
|
421
501
|
|
|
422
502
|
```bash
|