@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.
Files changed (43) hide show
  1. package/README.en.md +190 -110
  2. package/README.ja.md +192 -112
  3. package/README.md +195 -114
  4. package/dist/adapter/index.d.ts +8 -7
  5. package/dist/adapter/index.js.map +1 -1
  6. package/dist/browser.d.ts +9 -26
  7. package/dist/browser.js +4 -4
  8. package/dist/browser.js.map +1 -1
  9. package/dist/{chunk-JRQPMPQC.js → chunk-BCEBXG4X.js} +2 -2
  10. package/dist/chunk-BCEBXG4X.js.map +1 -0
  11. package/dist/{chunk-7EH47MTW.js → chunk-BVGXHIXR.js} +30 -9
  12. package/dist/chunk-BVGXHIXR.js.map +1 -0
  13. package/dist/{chunk-5VWPH7DO.js → chunk-GQZCU6PC.js} +14 -6
  14. package/dist/chunk-GQZCU6PC.js.map +1 -0
  15. package/dist/{chunk-YKJRQCWR.js → chunk-JL2C5ZGQ.js} +54 -20
  16. package/dist/chunk-JL2C5ZGQ.js.map +1 -0
  17. package/dist/{chunk-3OHUSKHI.js → chunk-WYGIKXHE.js} +23 -8
  18. package/dist/chunk-WYGIKXHE.js.map +1 -0
  19. package/dist/client/index.d.ts +6 -5
  20. package/dist/client/index.js +5 -5
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/index.d.ts +18 -711
  23. package/dist/index.js +266 -149
  24. package/dist/index.js.map +1 -1
  25. package/dist/{kit-a2a-types-4dapztu2.d.ts → kit-a2a-types-sw7sI-ZT.d.ts} +3 -2
  26. package/dist/kit-config-BQKQnu8Y.d.ts +182 -0
  27. package/dist/kit-crypto-types-CqFENBqm.d.ts +47 -0
  28. package/dist/kit-types-Cr1cnB3M.d.ts +2051 -0
  29. package/dist/modules/a2a/index.d.ts +9 -5
  30. package/dist/modules/a2a/index.js +1 -1
  31. package/dist/modules/crypto/index.d.ts +8 -105
  32. package/dist/modules/crypto/index.js +3 -149
  33. package/dist/modules/crypto/index.js.map +1 -1
  34. package/package.json +22 -22
  35. package/dist/chunk-3OHUSKHI.js.map +0 -1
  36. package/dist/chunk-5VWPH7DO.js.map +0 -1
  37. package/dist/chunk-7EH47MTW.js.map +0 -1
  38. package/dist/chunk-JRQPMPQC.js.map +0 -1
  39. package/dist/chunk-YKJRQCWR.js.map +0 -1
  40. package/dist/kit-client-auth-BnhrkzZF.d.ts +0 -422
  41. package/dist/kit-crypto-types-CXr888wf.d.ts +0 -200
  42. package/dist/kit-i18n-EBzZKYN-.d.ts +0 -502
  43. package/dist/kit-types-BkNdyKj8.d.ts +0 -822
package/README.en.md CHANGED
@@ -4,88 +4,133 @@
4
4
 
5
5
  [Framework home](../../README.en.md) · Related modules: [@h-ai/core](../core/README.en.md) · [@h-ai/crypto](../crypto/README.en.md) · [@h-ai/iam](../iam/README.en.md)
6
6
 
7
- ## Introduction
8
-
9
7
  `@h-ai/kit` integrates SvelteKit with hai-framework, providing a shared request pipeline and same-origin endpoint utilities:
10
8
 
11
- ## Core features
12
-
13
- - Handle hook composition (`createHandle` / `sequence`)
14
- - Authentication/role/permission guards (`guard.require` / `guard.check`)
15
- - Built-in Logging / RateLimit / CORS middleware, enabled through `createHandle` configuration
16
- - Shared API responses and request validation (Zod)
17
- - Bearer token utilities (server login/logout + browser token storage)
18
- - Browser client with automatic CSRF and transport encryption support
19
- - Declarative CRUD resources (`crud.define`)
20
- - A2A integration: discovery defaults to `/.well-known/agent-card.json`. With `authenticate` configured, null/undefined returns 401; anonymous access is allowed only without authentication configured. API Key parameters come from the complete Agent Card's `securitySchemes.apiKey`. Hooks and explicit A2A routes share JSON-RPC parse-error and SSE cancellation cleanup logic.
21
- - Dual build mode (`createAdapter()`)
9
+ Use runtime operations through `kit.xx`. Public namespace types are `KitFunctions` on the server and `BrowserKitFunctions` in the browser. The browser condition exposes only clients, CRUD, page tokens, and default locale operations. `AuthCookieWriter` describes the current request's minimal server authentication cookie writer. Re-exported methods and namespaces support consumer declaration emission without importing internal helpers.
22
10
 
23
- ## Getting started
11
+ ## Quick Start
24
12
 
25
13
  `sequence` preserves SvelteKit resolve options: page transformations run from inner to outer, while the first preload and serialized-header filter win. This preserves the i18n HTML language replacement.
26
14
 
27
15
  ### Installation
28
16
 
29
17
  ```bash
30
- npm install @h-ai/kit @h-ai/core sm-crypto@0.5.7
18
+ npm install @h-ai/kit @h-ai/core sm-crypto@0.5.8
31
19
  npm install svelte @sveltejs/kit zod
32
20
  npm install -D @sveltejs/vite-plugin-svelte vite
33
21
  ```
34
22
 
35
- ### Dependencies
36
-
37
- - `@h-ai/iam`: optional authentication/authorization for A2A; initialize before using kit's A2A features.
38
- - `@h-ai/crypto`: optional transport/cookie encryption; initialize before using encryption in kit.
39
-
40
- ### Quick start
41
-
42
- #### Project configuration
43
-
44
- **svelte.config.js**:
45
-
46
- ```js
47
- import adapter from '@sveltejs/adapter-node'
48
- import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
49
-
50
- const config = {
51
- preprocess: [vitePreprocess()],
52
- compilerOptions: { runes: true },
53
- kit: {
54
- adapter: adapter(),
55
- // 双构建模式:import { createAdapter } from '@h-ai/kit/adapter'
56
- // adapter: await createAdapter(),
57
- },
58
- }
59
-
60
- export default config
61
- ```
23
+ ### Project configuration
62
24
 
63
25
  **vite.config.ts**:
64
26
 
65
27
  ```ts
28
+ import adapter from '@sveltejs/adapter-node'
66
29
  import { sveltekit } from '@sveltejs/kit/vite'
30
+ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
67
31
  import tailwindcss from '@tailwindcss/vite'
68
32
  import { defineConfig } from 'vite'
69
33
 
70
34
  export default defineConfig({
71
- plugins: [sveltekit(), tailwindcss()],
72
- optimizeDeps: { exclude: ['bits-ui'] },
73
- ssr: { noExternal: ['@h-ai/ui', '@h-ai/kit'], external: ['@h-ai/core', 'sm-crypto'] },
35
+ plugins: [
36
+ sveltekit({
37
+ adapter: adapter(),
38
+ preprocess: [
39
+ vitePreprocess()
40
+ ],
41
+ compilerOptions: { runes: true },
42
+ // 绝对路径保持 SSR 与浏览器端认证、加密路径一致。
43
+ paths: { base: '', relative: false },
44
+ }),
45
+ tailwindcss(),
46
+ ],
47
+ optimizeDeps: {
48
+ exclude: ['bits-ui']
49
+ },
50
+ ssr: {
51
+ noExternal: ['@h-ai/ui', '@h-ai/kit'],
52
+ external: ['@h-ai/core', 'sm-crypto']
53
+ },
74
54
  })
75
55
  ```
76
56
 
77
- When using Crypto or Kit/IAM, include `sm-crypto@0.5.7` in application production dependencies. CLI projects already do this; for manual setup, run `pnpm add sm-crypto@0.5.7`. The external option alone is insufficient: adapter-node also uses direct production dependencies to preserve native package loading.
57
+ When using Crypto or Kit/IAM, include `sm-crypto@0.5.8` in application production dependencies. CLI projects already do this; for manual setup, run `pnpm add sm-crypto@0.5.8`. The external option alone is insufficient: adapter-node also uses direct production dependencies to preserve native package loading.
58
+
59
+ ## Features
60
+
61
+ ### Purpose, inputs and outputs
62
+
63
+ Call fragments reuse the initialized service from this page. Named variables stand for application-provided requests, schemas, resource definitions or options. Follow the corresponding examples for setup and failure handling. Check HaiResult.success and await asynchronous calls.
64
+
65
+ | Entry | Use case | Input | Output | Call fragment |
66
+ | ------------------------------------------- | ----------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------- |
67
+ | `kit.createHandle` | Request pipeline | `HandleConfig: auth/guards/logging/rateLimit/crypto/access` | `SvelteKit Handle` | `kit.createHandle({ logging: true })` |
68
+ | `kit.sequence` | Compose hooks | `...Handle` | `Handle preserving resolve options` | `kit.sequence(i18nHandle, authHandle)` |
69
+ | `kit.handler` | Endpoint boundary | `(RequestEvent) => Response \| Promise<Response>` | `SvelteKit request handler` | `kit.handler(async () => kit.response.ok({ ready: true }))` |
70
+ | `kit.guard.check` | Check permission | `session, permission` | `boolean` | `kit.guard.check(session, "orders.read")` |
71
+ | `kit.guard.require` | Require permission | `session, permission` | `void; denied access throws Response` | `kit.guard.require(session, "orders.write")` |
72
+ | `kit.guard.requireSensitive` | Verify operation purpose | `initialized IAM, current RequestEvent request/cookies, operation` | `Promise<void>; invalid proof throws Response` | `kit.guard.requireSensitive(iam, event, "orders.approve")` |
73
+ | `kit.response.ok` | Success | `data, requestId?` | `Response with HTTP 200 and success data` | `kit.response.ok({ id: "o1" })` |
74
+ | `kit.response.created` | Created | `data, requestId?` | `Response with HTTP 201 and success data` | `kit.response.created({ id: "o1" })` |
75
+ | `kit.response.noContent` | No content | `none` | `Response with HTTP 204` | `kit.response.noContent()` |
76
+ | `kit.response.error` | Custom error | `code, message, status?, requestId?, public details?` | `safe failure Response` | `kit.response.error("app:orders:001", "请求无效", 400)` |
77
+ | `kit.response.badRequest` | Bad request | `message?, requestId?` | `HTTP 400 Response` | `kit.response.badRequest("Invalid request")` |
78
+ | `kit.response.unauthorized` | Unauthorized | `message?, requestId?` | `HTTP 401 Response` | `kit.response.unauthorized()` |
79
+ | `kit.response.forbidden` | Forbidden | `message?, requestId?` | `HTTP 403 Response` | `kit.response.forbidden()` |
80
+ | `kit.response.notFound` | Not found | `message?, requestId?` | `HTTP 404 Response` | `kit.response.notFound()` |
81
+ | `kit.response.conflict` | Conflict | `message, requestId?` | `HTTP 409 Response` | `kit.response.conflict("Data changed")` |
82
+ | `kit.response.validationError` | Field validation | `{ field, message }[], requestId?` | `HTTP 422 Response; errors in safe error.cause` | `kit.response.validationError([{ field: "title", message: "请输入标题" }])` |
83
+ | `kit.response.internalError` | Internal failure | `message?, requestId?` | `HTTP 500 Response with safe message` | `kit.response.internalError()` |
84
+ | `kit.response.redirect` | Redirect | `location, status?` | `redirect Response` | `kit.response.redirect("/auth/login")` |
85
+ | `kit.response.fromResult` | Map domain result | `HaiResult<T>, httpStatusMap?, requestId?` | `HTTP Response; preserves safe code/status/traceId` | `kit.response.fromResult(result)` |
86
+ | `kit.response.fromError` | Map domain error | `HaiError, requestId?` | `safe HTTP failure Response` | `kit.response.fromError(result.error)` |
87
+ | `kit.validate.body` | Body validation | `Request, Zod schema` | `Promise<schema data>; invalid input throws Response` | `kit.validate.body(request, InputSchema)` |
88
+ | `kit.validate.query` | Query validation | `URL, Zod schema` | `schema data; invalid input throws Response` | `kit.validate.query(url, QuerySchema)` |
89
+ | `kit.validate.params` | Route validation | `Record<string,string>, Zod schema` | `schema data; invalid input throws Response` | `kit.validate.params(params, IdSchema)` |
90
+ | `kit.validate.IdParamSchema` | Resource ID | `{ id: string }` | `Zod parsed ID object` | `kit.validate.IdParamSchema.parse({ id: "o1" })` |
91
+ | `kit.validate.PaginationQuerySchema` | Pagination | `page/pageSize/search query` | `normalized pagination object` | `kit.validate.PaginationQuerySchema.parse({ page: "1" })` |
92
+ | `kit.client.create` | Same-origin client | `KitClientConfig: auth/transport/recoverSession?` | `KitClient { apiFetch, init, destroy, ready }` | `kit.client.create()` |
93
+ | `kit.client.createAuthActions` | Auth actions | `{ basePath?, ...auth client options }?` | `AuthPanel-compatible actions` | `kit.client.createAuthActions({ basePath: "/api/auth" })` |
94
+ | `kit.client.installBrowserTransport` | Install transport | `public KitConfig, { crypto, basePath? }` | `void; installs same-origin global fetch wrapper when enabled` | `kit.client.installBrowserTransport(publicKitConfig, { crypto })` |
95
+ | `kit.client.installBrowserTransportFetch` | Custom transport installation | `ClientTransportConfig` | `void; installs browser global fetch wrapper` | `kit.client.installBrowserTransportFetch(transportOptions)` |
96
+ | `kit.auth.publicPaths` | Anonymous endpoints | `none` | `string[]; prepend the host base path when needed` | `kit.auth.publicPaths` |
97
+ | `kit.auth.createApi` | Auth handlers | `initialized IAM service` | `RequestHandler map for login/OTP/reset/config/etc.` | `kit.auth.createApi(iam)` |
98
+ | `kit.auth.login` | Password login | `Cookie writer, PasswordCredentials, request context?` | `Promise<HaiResult<AuthResult>>; writes token cookies on success` | `kit.auth.login(cookies, { identifier: "alice", password })` |
99
+ | `kit.auth.loginWithOtp` | OTP login | `Cookie writer, OtpCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithOtp(cookies, otpCredentials)` |
100
+ | `kit.auth.loginWithLdap` | LDAP login | `Cookie writer, LdapCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithLdap(cookies, ldapCredentials)` |
101
+ | `kit.auth.loginWithApiKey` | API key login | `Cookie writer, ApiKeyCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithApiKey(cookies, apiKeyCredentials)` |
102
+ | `kit.auth.registerAndLogin` | Register and login | `Cookie writer, RegisterOptions, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.registerAndLogin(cookies, registration)` |
103
+ | `kit.auth.logout` | Logout | `Cookie writer, accessToken?` | `Promise<HaiResult<void>>` | `kit.auth.logout(cookies, accessToken)` |
104
+ | `kit.auth.setBrowserToken` | Page memory token | `access token string` | `void; no Web Storage persistence` | `kit.auth.setBrowserToken(accessToken)` |
105
+ | `kit.auth.clearBrowserToken` | Clear page token | `none` | `void` | `kit.auth.clearBrowserToken()` |
106
+ | `kit.crud.define` | Declare CRUD | `CrudResourceConfig` | `resource operation object` | `kit.crud.define(resourceDefinition)` |
107
+ | `kit.middleware.rateLimit` | Fixed-window limit | `{ windowMs, maxRequests, store, ...options }` | `Middleware; 429 on quota and 503 on store failure` | `kit.middleware.rateLimit({ windowMs: 60000, maxRequests: 100, store })` |
108
+ | `kit.middleware.createCacheRateLimitStore` | Shared counter | `initialized shared cache, { namespace, allowMemory? }` | `RateLimitStore` | `kit.middleware.createCacheRateLimitStore(cache, { namespace: "orders" })` |
109
+ | `kit.middleware.createMemoryRateLimitStore` | Development counter | `none; development or explicit test only` | `MemoryRateLimitStore` | `kit.middleware.createMemoryRateLimitStore()` |
110
+ | `kit.createFeedbackHandler` | Controlled feedback route | `{ feedback, resolveContext, ...options }` | `catch-all RequestHandler` | `kit.createFeedbackHandler({ feedback, resolveContext })` |
111
+ | `kit.i18n.setLocale` | Startup locale | `locale string` | `void; do not change process default per request` | `kit.i18n.setLocale("zh-CN")` |
112
+ | `kit.i18n.runWithLocale` | Request locale isolation | `locale, callback; Node server only` | `same callback return value` | `kit.i18n.runWithLocale("en-US", () => resolve(event))` |
78
113
 
79
- #### Build the request pipeline
114
+ - Handle hook composition (`createHandle` / `sequence`)
115
+ - Authentication/role/permission guards (`guard.require` / `guard.check`)
116
+ - Built-in Logging / RateLimit / CORS middleware, enabled through `createHandle` configuration
117
+ - Shared API responses and request validation (Zod)
118
+ - Bearer token utilities (server login/logout + browser token storage)
119
+ - Browser client with automatic CSRF and transport encryption support
120
+ - Declarative CRUD resources (`crud.define`)
121
+ - A2A integration: discovery defaults to `/.well-known/agent-card.json`. With `authenticate` configured, null/undefined returns 401; anonymous access is allowed only without authentication configured. API Key parameters come from the complete Agent Card's `securitySchemes.apiKey`. Hooks and explicit A2A routes share JSON-RPC parse-error and SSE cancellation cleanup logic.
122
+ - Dual build mode (`createAdapter()`)
123
+
124
+ ### Build the request pipeline
80
125
 
81
- When mounting an application under `/yyy/`, keep SvelteKit's `kit.paths.base` and the actual URLs used by guards consistent. Prefix `protectedPaths`, `publicPaths` and `loginUrl` with `base` from `$app/paths`, for example `${base}/api/*`. Authentication pages use ``kit.client.createAuthActions({ basePath: `${base}/api/auth` })``. Kit recognizes application `/api/*` routes through route groups and mount prefixes: unauthenticated APIs return 401, while pages follow the configured login redirect. Generated templates include this wiring.
126
+ When mounting an application under `/yyy/`, keep SvelteKit's `paths.base` and the actual URLs used by guards consistent. Prefix `protectedPaths`, `publicPaths` and `loginUrl` with `resolve('/').replace(/\/$/, '')` from `$app/paths` with `paths.relative: false`, for example `${base}/api/*`. Authentication pages use ``kit.client.createAuthActions({ basePath: `${base}/api/auth` })``. Kit recognizes application `/api/*` routes through route groups and mount prefixes: unauthenticated APIs return 401, while pages follow the configured login redirect. Generated templates include this wiring.
82
127
 
83
128
  The Hook examples assume the application has initialized its database, shared cache and IAM and checked every result. The application owns resource cleanup; do not initialize modules per request.
84
129
 
85
130
  **src/hooks.server.ts**:
86
131
 
87
132
  ```typescript
88
- import type { Handle } from '@sveltejs/kit'
133
+ import type { Handle } from '@sveltejs/kit/hooks'
89
134
  import { cache } from '@h-ai/cache'
90
135
  import { iam } from '@h-ai/iam'
91
136
  import { kit } from '@h-ai/kit'
@@ -100,7 +145,11 @@ const haiHandle = kit.createHandle({
100
145
  protectedPaths: ['/admin/*', '/api/*'],
101
146
  publicPaths: ['/api/auth/*', '/api/public/*'],
102
147
  },
103
- rateLimit: { windowMs: 60_000, maxRequests: 100, store: kit.middleware.createCacheRateLimitStore(cache, { namespace: 'app' }) },
148
+ rateLimit: {
149
+ windowMs: 60_000,
150
+ maxRequests: 100,
151
+ store: kit.middleware.createCacheRateLimitStore(cache, { namespace: 'app' })
152
+ },
104
153
  logging: true,
105
154
  })
106
155
 
@@ -109,7 +158,7 @@ export const handle: Handle = haiHandle
109
158
 
110
159
  Rate limits use fixed windows beginning at the first successful commit; later requests preserve the TTL. A store is required. Initialize shared Redis and check its result before serving; all nodes use the same namespace/windowMs/maxRequests. Cache read/commit failure or exhausted contention returns 503, while exceeding the limit returns 429, without get+set or local fallback. createMemoryRateLimitStore is for development or explicit HAI_E2E/HAI_E2E_MOCK tests; it has no timer and can be garbage collected when released; each request scans at most 64 entries, capacity is 10000 windows, new keys receive 503 at capacity without evicting active counts, and external stores remain owned by their creators. Never enable test flags in production. Reset headers estimate node time; Redis TTL controls the actual window, so synchronize clocks. The default key uses the trusted adapter getClientAddress, normalizes equivalent IPv6/mapped IPv4 addresses and ignores raw forwarded headers; configure proxy trust in the deployment adapter.
111
160
 
112
- #### API endpoints
161
+ ### API endpoints
113
162
 
114
163
  ```typescript
115
164
  import { kit } from '@h-ai/kit'
@@ -127,7 +176,7 @@ export const POST = kit.handler(async ({ request, locals }) => {
127
176
  })
128
177
  ```
129
178
 
130
- #### Browser client
179
+ ### Browser client
131
180
 
132
181
  `kit.client.create()` targets SvelteKit same-origin endpoints. Use `@h-ai/api-client` for cross-origin access to public APIs from Web, apps, or mini programs.
133
182
 
@@ -141,7 +190,7 @@ export const { apiFetch } = client
141
190
 
142
191
  ```typescript
143
192
  // 使用
144
- import { apiFetch } from '$lib/utils/api'
193
+ import { apiFetch } from '#lib/utils/api.js'
145
194
 
146
195
  const response = await apiFetch('/api/users', { method: 'GET' })
147
196
  ```
@@ -151,21 +200,23 @@ const response = await apiFetch('/api/users', { method: 'GET' })
151
200
  >
152
201
  > Use httpOnly cookies for same-origin SvelteKit endpoints and verify them on the server. For browser Bearer headers, set an in-memory token with `kit.auth.setBrowserToken()` before `kit.client.create({ auth: true })`. Custom `BrowserTokenStore` implementations must use memory or platform secure storage; never persist sensitive credentials in Web Storage.
153
202
 
154
- #### Same-origin transport encryption
203
+ ### Same-origin transport encryption
155
204
 
156
205
  kit delegates transport encryption to `@h-ai/crypto`. The server creates a manager with `crypto.transport.createServer({ serverKeyRing, keyStore })`; the browser installs a same-origin fetch wrapper with one call to `kit.client.installBrowserTransport(kitConfig, { crypto })`. Servers must also inject a managed key ring and shared registry.
157
206
 
158
- For subdirectory hosting, read `base` from `$app/paths`: pass `basePath: base` in server `crypto.transport`, use `installBrowserTransport(kitConfig, { crypto, basePath: base })` in the browser, and set the same `transport.basePath` for a standalone `kit.client.create`. The default empty string means the domain root. This runtime argument comes from the host build path, without a duplicate YAML setting or inference from forwarding headers. Exchange paths, exclusions and route budgets still use full request paths. CLI SSR templates assemble both sides.
207
+ For subdirectory hosting, read `resolve('/').replace(/\/$/, '')` from `$app/paths` with `paths.relative: false`: pass `basePath: base` in server `crypto.transport`, use `installBrowserTransport(kitConfig, { crypto, basePath: base })` in the browser, and set the same `transport.basePath` for a standalone `kit.client.create`. The default empty string means the domain root. This runtime argument comes from the host build path, without a duplicate YAML setting or inference from forwarding headers. Exchange paths, exclusions and route budgets still use full request paths. CLI SSR templates assemble both sides.
159
208
 
160
209
  **src/hooks.server.ts**:
161
210
 
162
211
  ```ts
163
- import { base } from '$app/paths'
164
- import { appKitConfig } from '$lib/server/kit-config'
212
+ import { resolve } from '$app/paths'
165
213
  import { cache } from '@h-ai/cache'
166
214
  import { createRedisTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
167
215
  import { iam } from '@h-ai/iam'
168
216
  import { kit } from '@h-ai/kit'
217
+ import { appKitConfig } from '#lib/server/kit-config.js'
218
+
219
+ const base = resolve('/').replace(/\/$/, '')
169
220
 
170
221
  const cryptoReady = await crypto.init()
171
222
  if (!cryptoReady.success)
@@ -203,13 +254,15 @@ export const handle = kit.createHandle({
203
254
 
204
255
  ```svelte
205
256
  <script lang='ts'>
206
- import { browser } from '$app/environment'
207
- import { base } from '$app/paths'
257
+ import { browser } from '$app/env'
258
+ import { resolve } from '$app/paths'
208
259
  import { page } from '$app/state'
209
260
  import { crypto } from '@h-ai/crypto'
210
261
  import { kit } from '@h-ai/kit'
211
262
  import { untrack } from 'svelte'
212
263
 
264
+ const base = resolve('/').replace(/\/$/, '')
265
+
213
266
  // 一次性安装:内部按 _kit.yml 决定是否启用 transport,并预热 crypto
214
267
  if (browser) {
215
268
  kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), { crypto, basePath: base })
@@ -233,17 +286,15 @@ Servers must explicitly inject the same managed `serverKeyRing` and a shared `ke
233
286
 
234
287
  Transport applies by default to same-origin `/api/*` endpoints, SvelteKit `__data.json` page data, and key exchange itself. Page documents and static assets remain plaintext and do not require `X-Client-Id`. `multipart/form-data` requests such as uploads are sent unchanged; explicitly allow their paths through server `excludePaths`.
235
288
 
236
- Only authenticated protocol v2 is accepted. Independent random SM4-CBC and HMAC-SM3 keys are wrapped with SM2; verify the sender signature and MAC before CBC decryption. Clients sign requests and the selected server key version signs responses. Authentication covers version, direction, serverKeyId, clientId, a random 128-bit requestId acting as the nonce, issuedAt, HTTP method, the SM3 pathHash of pathname plus the original search, and response status. Requests have a five-minute lifetime and at most 30 seconds of future clock skew. The shared registry atomically consumes (clientId, requestId), retaining it until at least issuedAt plus six minutes to cover node skew; storage failures reject requests. Synchronize node clocks and use a Redis noeviction/durable policy that preserves live nonces. Transport replay prevention does not replace business idempotency keys.
237
-
238
- Empty POSTs still send an authenticated envelope for empty plaintext. GET/HEAD carry it in X-Hai-Transport; bodyless responses such as 204/304/HEAD use the same header. Other requests/responses carry a JSON envelope and X-Encrypted: true. The client must match the original request context and HTTP status; missing markers, old formats and authentication failures reject. Serv/Kit hosts explicitly allow plaintext through `encryption: disabled` path rules or host `excludePaths`. Callers must select ordinary requests; API Client/Kit wrappers use their own `excludePaths`. CORS must allow and expose X-Hai-Transport. TLS authenticates handshake keys; this protocol does not replace IAM authorization.
289
+ Transport uses authenticated v2 envelopes, verifying signature/MAC before decryption and binding method, path, time and response status. The shared registry atomically consumes nonces. The host still owns TLS, trusted proxies, authorization and idempotency. Custom CORS hosts must allow and expose X-Hai-Transport. See the [Transport protocol](../crypto/TRANSPORT-PROTOCOL.md) for fields, bodyless responses, clock windows and replay protection.
239
290
 
240
291
  Set `transport.maxPlaintextBytes` for both request and response JSON plaintext, measured in UTF-8 bytes: default 1 MiB, integer range 1 KiB–64 MiB. Clients learn the server limit during key exchange. `resolveTransportBodyLimits()` derives encrypted/proxy bytes as twice the plaintext limit plus 32 KiB. Handshake, public-key, header and replay constraints remain independent. Oversized streams are cancelled and readers released: requests return 413; oversized responses return an encrypted 500 capacity error, recognized as `TransportBodyTooLargeError` / `hai:crypto:070`. [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
241
292
 
242
293
  Ordinary routes retain `transport.maxPlaintextBytes` (auto/omitted: 1 MiB). Declare Feedback report intake, evidence, attachments and ZIP download capacity separately using `routePlaintextBytes` and `feedback.budget.maxTransportBytes`; `*` matches one complete path segment and explicit host route values take precedence. Handshakes, adapter-node and proxies use the maximum route capacity. HTTP adapters admit at most 2 explicitly declared material requests above 1 MiB and a separate 16 ordinary business requests, releasing admission after the entire response or failure. Busy admission returns 503 / `hai:crypto:072`; business writes are not automatically replayed.
243
294
 
244
- serv/kit return an encrypted HTTP 400 time error for an out-of-window message only after request binding, signature and MAC verification. The client throws `TransportTimeError` (`hai:crypto:071`) with advice to synchronize device/server clocks and refresh. `api-client` maps it to `VALIDATION_FAILED` and preserves `cause.transportCode`. Requests are not replayed automatically and replay windows remain unchanged; tampered or unauthenticated messages retain the generic authentication failure.
295
+ Serv/Kit return an encrypted HTTP 400 for an out-of-window request after validating request binding, signature and MAC. The client throws `TransportTimeError` (`hai:crypto:071`), displays the server’s UTC time at validation, and asks the user to enable automatic synchronization or set the device time manually before refreshing. `serverTime` is Unix milliseconds. Clients still show synchronization guidance when an older server omits it. `api-client` maps the error to `VALIDATION_FAILED` and preserves `cause.transportCode` and `cause.serverTime`. Standard authentication actions convert authenticated transport time errors to `HaiResult` with the server time and synchronization guidance for AuthPanel.
245
296
 
246
- #### Shared transport configuration with `_kit.yml`
297
+ ### Shared transport configuration with `_kit.yml`
247
298
 
248
299
  `@h-ai/kit` provides `KitConfigSchema` / `resolveKitConfig()` so a single `_kit.yml`
249
300
  can drive both `hooks.server.ts` and browser `kit.client.installBrowserTransport()`:
@@ -276,7 +327,7 @@ export const publicKitConfig = { transport: appKitConfig.transport }
276
327
  Pass the public projection through `src/routes/+layout.server.ts` to the browser layout above, without importing the server configuration into browser code:
277
328
 
278
329
  ```ts
279
- import { publicKitConfig } from '$lib/server/kit-config'
330
+ import { publicKitConfig } from '#lib/server/kit-config.js'
280
331
 
281
332
  export function load() {
282
333
  return { kitConfig: publicKitConfig }
@@ -287,28 +338,10 @@ export function load() {
287
338
  For server-only configuration, use `core.config.validate('kit', KitConfigSchema)` as well.
288
339
  Read the complete `_kit.yml` on the server; it can contain private proxy networks and policy-file paths. Return only an explicit public projection such as `{ kitConfig: publicKitConfig }` from `+layout.server.ts`, and consume its transport fields in the browser. Never bundle the full configuration or policy file with `?raw`, and keep secrets outside these files.
289
340
 
290
- ### Configuration
291
-
292
- `kit.createHandle()` options:
293
-
294
- | Field | Type | Description |
295
- | ------------ | ---------------------------------------- | ---------------------------------------------------------------------- |
296
- | `auth` | `HandleAuthConfig` | Authentication (verifyToken / protectedPaths / publicPaths / loginUrl) |
297
- | `rateLimit` | `object \| false` | Rate limiting (windowMs / maxRequests) |
298
- | `logging` | `boolean \| object` | Request logging (default `true`; `{ logBody: true }` logs the body) |
299
- | `crypto` | `HookCryptoConfig` | Transport + cookie encryption |
300
- | `guards` | `GuardConfig[]` | Custom guards (after automatic auth guards) |
301
- | `middleware` | `Middleware[]` | Custom middleware (after built-in middleware) |
302
- | `a2a` | `HandleA2AOperations \| HandleA2AConfig` | A2A integration (Agent Card + JSON-RPC) |
303
- | `onError` | `function` | Custom error handling |
304
-
305
- Request/response summaries use the `trace` level. Logging is enabled by default and `logBody` defaults to false. Query values come from the original `request.url`, while the path remains SvelteKit's current `event.url.pathname`. Logging does not access the tracked `event.url.search/searchParams`, so static pages can prerender with logging enabled. Password/token/secret, authorization and API-key fields are redacted in queries and optional JSON-body logs. The body is read from a clone and remains available to application code.
306
-
307
- Automatic request auditing runs only when `audit.isInitialized`. Static sites and applications without Audit need not initialize it for this handle. State is checked per request, so modules may initialize after the handle is created. Applications selecting Audit must still check `audit.init()` and stop startup on failure. Explicit `audit.log/capture` calls retain their uninitialized errors, and initialized modules still report actual write failures as `Request audit failed`. Mandatory business auditing must check explicit results and enforce the application's transaction rules.
308
-
309
- With `crypto.encryptedCookies`, also provide `crypto.cookieEncryptionKey` or `HAI_KIT_COOKIE_KEY`. Missing keys fail immediately in `createHandle()`. Plaintext, damaged ciphertext, or decryption failures for protected cookies are treated as absent; encryption failures never fall back to writing plaintext.
341
+ ### Dependencies
310
342
 
311
- Authentication cookies default to `Secure` when either `NODE_ENV` or `HAI_CORE_ENV` is `production`; `HAI_E2E` / `HAI_E2E_MOCK` do not relax cookie security. Production rate limiting recognizes both flags; only explicit test switches permit memory counters.
343
+ - `@h-ai/iam`: optional authentication/authorization for A2A; initialize before using kit's A2A features.
344
+ - `@h-ai/crypto`: optional transport/cookie encryption; initialize before using encryption in kit.
312
345
 
313
346
  ### Export paths
314
347
 
@@ -332,12 +365,6 @@ Export standard handlers from kit.auth.createApi(iam), use kit.auth.publicPaths
332
365
 
333
366
  `kit.auth.logout(cookies, accessToken)` returns `Promise<HaiResult<void>>`. Without a token it only clears cookies and succeeds; with a token but no configured auth operations it fails. Local cookies are cleared for every server outcome. Callers must preserve failed results in their HTTP response or UI; local clearing does not confirm server revocation. Underlying control-flow exceptions still propagate.
334
367
 
335
- ### Error handling
336
-
337
- - Wrap API endpoints with `kit.handler()`.
338
- - `kit.validate.body` / `kit.guard.require` throw `Response`, handled by `kit.handler` as API control flow. Page `load` uses `kit.guard.check` with SvelteKit `error`; parse query schemas with `safeParse` and raise native page errors instead of throwing API responses.
339
- - `kit.handler()` converts non-control-flow exceptions into `500 INTERNAL_ERROR` responses.
340
-
341
368
  ### CRUD fields
342
369
 
343
370
  Declarative CRUD fields support `colSpan: 'auto' | 1 | 2` for UI form widths. `validation.message` accepts a string or `() => string` for current-locale validation messages. Configure layout through `CrudPage.form`; validate independently on the server.
@@ -356,7 +383,7 @@ Wrap Node requests and SSR in `core.i18n.runWithLocale(locale, () => handleReque
356
383
 
357
384
  Browsers keep application-level `setGlobalLocale` / `createLocaleStore`. During SSR, UI store initialization and setters only update the store; messages read the request scope. `runWithLocale` requires the Node entry; browser translations use explicit locale options or application state.
358
385
 
359
- ## Feedback endpoint and request correlation
386
+ ### Feedback endpoint and request correlation
360
387
 
361
388
  `kit.createFeedbackHandler` can serve GET/POST/PATCH in `/api/feedback/[...path]/+server.ts`. It reuses the same api-contract schemas as serv, bounds the input stream, and rejects client identity/scope fields. resolveContext maps an already verified locals.session to server-controlled scope and permissions; null denies access. Keep the existing createHandle authentication, CSRF, and transport encryption.
362
389
 
@@ -381,33 +408,21 @@ export const POST = handler
381
408
  export const PATCH = handler
382
409
  ```
383
410
 
384
- ## Error messages and correlated logs
385
-
386
- HTTP errors carry safe localized messages and a reference ID. ToastContainer hides the ID by default and reveals it through its diagnostic icon. The same ID remains in `error.traceId` and `X-Trace-Id`. The framework reuses the request ID to correlate browser and server logs. Sanitized exceptions, stacks and operation context remain in logs; internal cause/ext are not exposed. Field validation returns localized field names and messages only.
387
-
388
- Use `core.error.toPublic(error, traceId?)` at HTTP/UI boundaries and `core.error.formatMessage(error)` after translating messages; formatting is idempotent. Pass complete Error objects to logs instead of discarding their context. Database failures log driver codes, sanitized messages/stacks and table/column context; SQL text, bound values and connection credentials are excluded.
389
-
390
- When audit is initialized, createHandle binds verified session identity/roles, the platform getClientAddress result and request ID for domain audit.log calls. Automatic request.execute events contain the route template, method, status and success/failure/denial, including guard redirects and SvelteKit HTTP control flow. Bodies and error details are not copied. capture.enabled controls automatic capture; capture.includeReads enables successful reads. Failed reads remain recorded by default. Applications still explicitly record domain completion/failure events; disabling automatic capture does not disable domain logs.
391
-
392
- ## Optional interactive challenges
411
+ ### Optional interactive challenges
393
412
 
394
413
  Export kit.auth.createApi(iam).createCaptcha as POST /api/auth/captcha; kit.auth.publicPaths includes it. kit.client.createAuthActions preserves captcha and supplies createCaptcha for automatic AuthPanel solving. Export /api/auth/password/renew for initial/expired password changes. IAM enforces proofs server-side.
395
414
 
396
415
  See policy, defaults and recovery boundaries in [IAM](../iam/README.en.md).
397
416
 
398
- ## Recovering an expired session in place
417
+ ### Recovering an expired session in place
399
418
 
400
419
  Set `kit.client.create({ recoverSession })` to sign in again without discarding the current page. Use UI `createSessionRecovery` / `SessionRecoveryDialog`: configure the public policy and original user in the root layout, pass `recovery.recover` as the callback, provide standard authentication actions, and cancel on teardown. IAM checks login `expectedUserId` before issuing a session; captcha, lockout, password renewal and access rules remain active. Only an explicitly authentication-rejected request is retried once. Cancellation, 5xx, timeouts and ambiguous writes are not replayed. Sensitive-operation proofs from an old session may need a new confirmation. Standard authentication actions disable recovery for their own calls. Custom authentication requests should pass `{ recoverSession: false }` as the third apiFetch argument to avoid recursive dialogs. terminalType (web/app/desktop/api; default web) selects a session group and does not attest hardware identity.
401
420
 
402
- ## Page exit session policy
421
+ ### Page exit session policy
403
422
 
404
423
  `session.endOnPageHide` defaults to `false`. Hosts call `actions.bindPageExit({ enabled: config.session.endOnPageHide })` and invoke its returned cleanup on unmount; standard admin/H5/fullstack templates already bind it. A non-BFCache `pagehide` (including reload and full document navigation) makes a best effort POST to `/auth/session/close`, revoking the held access and related refresh through the existing CSRF/encrypted transport with `keepalive` and no automatic refresh. This response does not clear cookies/local credentials, avoiding a late old response overwriting a new login. Explicit logout still revokes and clears current credentials normally. Browsers may omit page exit requests; configure server `idleTimeout`/`absoluteTimeout` as appropriate. Immediate revocation on browser close is not guaranteed.
405
424
 
406
- ## Transport capacity and module budgets
407
-
408
- For a sensitive call, supply `reauthorize: () => sensitiveActions.authorize(operation)` in the third Kit apiFetch argument or typed-client options.context. After session rotation/recovery or an explicit proof rejection, obtain a new purpose-bound proof before one retry. Cancellation sends no further write; without a callback, an old proof is never replayed.
409
-
410
- ## Access checks on every request
425
+ ### Access checks on every request
411
426
 
412
427
  Path rules accept `encryption: required` or `disabled`; omission inherits the host. Disabled endpoints skip application encryption while retaining access and identity checks, and reject framework envelopes. Required mode can protect custom JSON endpoints and overrides `excludePaths`; Crypto, a managed key ring and a registry are required. Conflicting modes fail when creating the Handle. Crypto-enabled CLI templates install both server and browser transport; use db and a shared database in production. Private policy and proxy configuration remain server-side; plaintext callers select their protocol using the public transport exclusions.
413
428
 
@@ -417,7 +432,72 @@ Use `access: { file: './config/security/request-access.yml' }` for a separate YA
417
432
 
418
433
  Default `trustedProxies: auto` ignores forwarded headers on ordinary hosts and adopts container networks only for private managed Compose proxy topologies. Custom ingress uses explicit proxy IPs/CIDRs; `[]` disables trust. Resolved `event.getClientAddress()`, policy, IAM and audit share the same address. See the [request access handbook](../../docs/usage/configuration/request-access.md) for edge proxies, lost source addresses, environment overrides and rollback.
419
434
 
420
- ## Development and testing
435
+ ## Configuration
436
+
437
+ ### Choose configuration by task
438
+
439
+ | Configuration entry | Scenario | Input | Effect | Example |
440
+ | ----------------------------------------- | ---------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------- |
441
+ | `server.host / server.port` | Server listener | 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 }` |
442
+ | `transport` | Same-origin transport | false or keyExchangePath/excludePaths/byte/time/route budgets | public transport config for hook and browser; no private keys | `transport: { maxPlaintextBytes: auto }` |
443
+ | `trustedProxies` | Trusted peer | auto or explicit IP/CIDR list; [] disables forwarded headers | normalized client address for access policy | `trustedProxies: []` |
444
+ | `access` | Request access policy | false or independent policy configuration | per-request IP/time/path checks; not business ownership | `access: false` |
445
+ | `createHandle.auth / guards / middleware` | Host authentication pipeline | verifyToken/operations, paths and guard/middleware callbacks | RequestEvent locals/session and rejection Response | `kit.createHandle({ auth: authOptions })` |
446
+ | `createHandle.rateLimit` | Shared rate limit | windowMs, maxRequests and explicit atomic store | fixed window; 429 over quota, 503 on unavailable storage | `kit.createHandle({ rateLimit: limitOptions })` |
447
+ | `createHandle.crypto` | Inject private dependencies | initialized crypto, serverKeyRing/keyStore, encryptedCookies/key | authenticated payload/cookies; ring stays server-only | `kit.createHandle({ crypto: cryptoOptions })` |
448
+ | `client.create` | Browser request options | auth memory store, CSRF names, transport, recoverSession | same-origin apiFetch plus init/destroy/ready | `kit.client.create({ auth: true })` |
449
+
450
+ When building Svelte applications with pnpm, register the `deepmerge@4.3.1` patch in the project's `patchedDependencies` and keep the patch file. Retain the DaisyUI style patch when that package is used. CLI-generated projects include applicable entries. Installing a framework npm package does not propagate this repository's pnpm patch configuration; manual integrations must copy the relevant [patch files](../../patches/) and registrations before reinstalling dependencies.
451
+
452
+ `kit.createHandle()` options:
453
+
454
+ | Field | Type | Description |
455
+ | ------------ | ---------------------------------------- | ---------------------------------------------------------------------- |
456
+ | `auth` | `HandleAuthConfig` | Authentication (verifyToken / protectedPaths / publicPaths / loginUrl) |
457
+ | `rateLimit` | `object \| false` | Rate limiting (windowMs / maxRequests) |
458
+ | `logging` | `boolean \| object` | Request logging (default `true`; `{ logBody: true }` logs the body) |
459
+ | `crypto` | `HookCryptoConfig` | Transport + cookie encryption |
460
+ | `guards` | `GuardConfig[]` | Custom guards (after automatic auth guards) |
461
+ | `middleware` | `Middleware[]` | Custom middleware (after built-in middleware) |
462
+ | `a2a` | `HandleA2AOperations \| HandleA2AConfig` | A2A integration (Agent Card + JSON-RPC) |
463
+ | `onError` | `function` | Custom error handling |
464
+
465
+ Request/response summaries use the `trace` level. Logging is enabled by default and `logBody` defaults to false. Query values come from the original `request.url`, while the path remains SvelteKit's current `event.url.pathname`. Logging does not access the tracked `event.url.search/searchParams`, so static pages can prerender with logging enabled. Password/token/secret, authorization and API-key fields are redacted in queries and optional JSON-body logs. The body is read from a clone and remains available to application code.
466
+
467
+ Automatic request auditing runs only when `audit.isInitialized`. Static sites and applications without Audit need not initialize it for this handle. State is checked per request, so modules may initialize after the handle is created. Applications selecting Audit must still check `audit.init()` and stop startup on failure. Explicit `audit.log/capture` calls retain their uninitialized errors, and initialized modules still report actual write failures as `Request audit failed`. Mandatory business auditing must check explicit results and enforce the application's transaction rules.
468
+
469
+ With `crypto.encryptedCookies`, also provide `crypto.cookieEncryptionKey` or `HAI_KIT_COOKIE_KEY`. Missing keys fail immediately in `createHandle()`. Plaintext, damaged ciphertext, or decryption failures for protected cookies are treated as absent; encryption failures never fall back to writing plaintext.
470
+
471
+ The key must contain 32 hexadecimal characters. Cookies use separately derived SM4-CBC and HMAC-SHA256 keys, authenticating the version, cookie name, IV, and ciphertext before decryption to reject tampering and cross-name substitution. Legacy unauthenticated cookies become invalid after upgrading and require signing in again; there is no legacy fallback.
472
+
473
+ Authentication cookies default to `Secure` when either `NODE_ENV` or `HAI_CORE_ENV` is `production`; `HAI_E2E` / `HAI_E2E_MOCK` do not relax cookie security. Production rate limiting recognizes both flags; only explicit test switches permit memory counters.
474
+
475
+ ### Transport capacity and module budgets
476
+
477
+ For a sensitive call, supply `reauthorize: () => sensitiveActions.authorize(operation)` in the third Kit apiFetch argument or typed-client options.context. After session rotation/recovery or an explicit proof rejection, obtain a new purpose-bound proof before one retry. Cancellation sends no further write; without a callback, an old proof is never replayed.
478
+
479
+ ## Error Handling
480
+
481
+ - Wrap API endpoints with `kit.handler()`.
482
+ - `kit.validate.body` / `kit.guard.require` throw `Response`, handled by `kit.handler` as API control flow. Page `load` uses `kit.guard.check` with SvelteKit `error`; parse query schemas with `safeParse` and raise native page errors instead of throwing API responses.
483
+ - `kit.handler()` converts non-control-flow exceptions into `500 INTERNAL_ERROR` responses.
484
+
485
+ ### Error messages and correlated logs
486
+
487
+ `kit.response.fromError/fromResult` preserve safe field-validation `cause`, explicitly public `publicExt` (mapped to `ext`), and `httpStatus`. Internal causes and extensions remain private. Each module failure produces one correlated log entry.
488
+
489
+ HTTP/UI boundaries expose business messages only for explicit valid 4xx statuses. Missing or invalid `httpStatus` is treated as 500 with a safe message. Custom business errors must carry a complete error definition or an explicit status.
490
+
491
+ `kit.response.validationError(errors)` uses the same allowlist: `error.cause` contains only localized `field/message` pairs. It does not echo rejected values, SQL or additional fields, and no longer uses `details.errors`.
492
+ Shortcut responses, authentication guards and rate limiting use `HaiCommonError` codes with `httpStatus`, `traceId` and `X-Trace-Id`. Unauthenticated requests use `UNAUTHORIZED.code`, denied permissions use `FORBIDDEN.code`, and throttled requests use `RATE_LIMIT_EXCEEDED.code` (429). Rate limit storage failures return a safe 503 and retain the original cause in server logs.
493
+
494
+ HTTP errors carry safe localized messages and a reference ID. ToastContainer hides the ID by default and reveals it through its diagnostic icon. The same ID remains in `error.traceId` and `X-Trace-Id`. The framework reuses the request ID to correlate browser and server logs. Sanitized exceptions, stacks and operation context remain in logs; internal cause/ext are not exposed. Field validation returns localized field names and messages only.
495
+
496
+ Use `core.error.toPublic(error, traceId?)` at HTTP/UI boundaries and `core.error.formatMessage(error)` after translating messages; formatting is idempotent. Pass complete Error objects to logs instead of discarding their context. Database failures log driver codes, sanitized messages/stacks and table/column context; SQL text, bound values and connection credentials are excluded.
497
+
498
+ When audit is initialized, createHandle binds verified session identity/roles, the platform getClientAddress result and request ID for domain audit.log calls. Automatic request.execute events contain the route template, method, status and success/failure/denial, including guard redirects and SvelteKit HTTP control flow. Bodies and error details are not copied. capture.enabled controls automatic capture; capture.includeReads enables successful reads. Failed reads remain recorded by default. Applications still explicitly record domain completion/failure events; disabling automatic capture does not disable domain logs.
499
+
500
+ ## Development
421
501
 
422
502
  ```bash
423
503
  pnpm --filter @h-ai/kit test