@h-ai/kit 1.0.0-beta.17 → 1.0.0-beta.19
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 +53 -33
- package/README.ja.md +53 -33
- package/README.md +54 -33
- 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-WCMEGLBL.js → chunk-GQZCU6PC.js} +11 -5
- package/dist/chunk-GQZCU6PC.js.map +1 -0
- package/dist/{chunk-5JW3VW4Q.js → chunk-JL2C5ZGQ.js} +38 -11
- package/dist/chunk-JL2C5ZGQ.js.map +1 -0
- package/dist/{chunk-6KS67QUS.js → chunk-WYGIKXHE.js} +20 -7
- 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 +264 -149
- package/dist/index.js.map +1 -1
- package/dist/{kit-a2a-types-ikgvWS3T.d.ts → kit-a2a-types-CeDikRAX.d.ts} +3 -2
- package/dist/kit-config-CCksTmi5.d.ts +182 -0
- package/dist/kit-crypto-types-CqFENBqm.d.ts +47 -0
- package/dist/kit-types-BUDaKGB0.d.ts +2057 -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-5JW3VW4Q.js.map +0 -1
- package/dist/chunk-6KS67QUS.js.map +0 -1
- package/dist/chunk-7EH47MTW.js.map +0 -1
- package/dist/chunk-JRQPMPQC.js.map +0 -1
- package/dist/chunk-WCMEGLBL.js.map +0 -1
- package/dist/kit-client-auth-DeWmc-1u.d.ts +0 -406
- package/dist/kit-crypto-types-Brd0l8Hv.d.ts +0 -204
- package/dist/kit-i18n-CjwdGeKY.d.ts +0 -506
- package/dist/kit-types-CnfBC89D.d.ts +0 -832
package/README.en.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
`@h-ai/kit` integrates SvelteKit with hai-framework, providing a shared request pipeline and same-origin endpoint utilities:
|
|
10
10
|
|
|
11
|
+
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.
|
|
12
|
+
|
|
11
13
|
## Core features
|
|
12
14
|
|
|
13
15
|
- Handle hook composition (`createHandle` / `sequence`)
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
### Installation
|
|
28
30
|
|
|
29
31
|
```bash
|
|
30
|
-
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.
|
|
32
|
+
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.8
|
|
31
33
|
npm install svelte @sveltejs/kit zod
|
|
32
34
|
npm install -D @sveltejs/vite-plugin-svelte vite
|
|
33
35
|
```
|
|
@@ -41,44 +43,43 @@ npm install -D @sveltejs/vite-plugin-svelte vite
|
|
|
41
43
|
|
|
42
44
|
#### Project configuration
|
|
43
45
|
|
|
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
|
-
```
|
|
62
|
-
|
|
63
46
|
**vite.config.ts**:
|
|
64
47
|
|
|
65
48
|
```ts
|
|
49
|
+
import adapter from '@sveltejs/adapter-node'
|
|
66
50
|
import { sveltekit } from '@sveltejs/kit/vite'
|
|
51
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
67
52
|
import tailwindcss from '@tailwindcss/vite'
|
|
68
53
|
import { defineConfig } from 'vite'
|
|
69
54
|
|
|
70
55
|
export default defineConfig({
|
|
71
|
-
plugins: [
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
plugins: [
|
|
57
|
+
sveltekit({
|
|
58
|
+
adapter: adapter(),
|
|
59
|
+
preprocess: [
|
|
60
|
+
vitePreprocess()
|
|
61
|
+
],
|
|
62
|
+
compilerOptions: { runes: true },
|
|
63
|
+
// 绝对路径保持 SSR 与浏览器端认证、加密路径一致。
|
|
64
|
+
paths: { base: '', relative: false },
|
|
65
|
+
}),
|
|
66
|
+
tailwindcss(),
|
|
67
|
+
],
|
|
68
|
+
optimizeDeps: {
|
|
69
|
+
exclude: ['bits-ui']
|
|
70
|
+
},
|
|
71
|
+
ssr: {
|
|
72
|
+
noExternal: ['@h-ai/ui', '@h-ai/kit'],
|
|
73
|
+
external: ['@h-ai/core', 'sm-crypto']
|
|
74
|
+
},
|
|
74
75
|
})
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
When using Crypto or Kit/IAM, include `sm-crypto@0.5.
|
|
78
|
+
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.
|
|
78
79
|
|
|
79
80
|
#### Build the request pipeline
|
|
80
81
|
|
|
81
|
-
When mounting an application under `/yyy/`, keep SvelteKit's `
|
|
82
|
+
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
83
|
|
|
83
84
|
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
85
|
|
|
@@ -100,7 +101,11 @@ const haiHandle = kit.createHandle({
|
|
|
100
101
|
protectedPaths: ['/admin/*', '/api/*'],
|
|
101
102
|
publicPaths: ['/api/auth/*', '/api/public/*'],
|
|
102
103
|
},
|
|
103
|
-
rateLimit: {
|
|
104
|
+
rateLimit: {
|
|
105
|
+
windowMs: 60_000,
|
|
106
|
+
maxRequests: 100,
|
|
107
|
+
store: kit.middleware.createCacheRateLimitStore(cache, { namespace: 'app' })
|
|
108
|
+
},
|
|
104
109
|
logging: true,
|
|
105
110
|
})
|
|
106
111
|
|
|
@@ -141,7 +146,7 @@ export const { apiFetch } = client
|
|
|
141
146
|
|
|
142
147
|
```typescript
|
|
143
148
|
// 使用
|
|
144
|
-
import { apiFetch } from '
|
|
149
|
+
import { apiFetch } from '#lib/utils/api.js'
|
|
145
150
|
|
|
146
151
|
const response = await apiFetch('/api/users', { method: 'GET' })
|
|
147
152
|
```
|
|
@@ -155,17 +160,19 @@ const response = await apiFetch('/api/users', { method: 'GET' })
|
|
|
155
160
|
|
|
156
161
|
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
162
|
|
|
158
|
-
For subdirectory hosting, read `
|
|
163
|
+
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
164
|
|
|
160
165
|
**src/hooks.server.ts**:
|
|
161
166
|
|
|
162
167
|
```ts
|
|
163
|
-
import {
|
|
164
|
-
import { appKitConfig } from '$lib/server/kit-config'
|
|
168
|
+
import { resolve } from '$app/paths'
|
|
165
169
|
import { cache } from '@h-ai/cache'
|
|
166
170
|
import { createRedisTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
|
|
167
171
|
import { iam } from '@h-ai/iam'
|
|
168
172
|
import { kit } from '@h-ai/kit'
|
|
173
|
+
import { appKitConfig } from '#lib/server/kit-config.js'
|
|
174
|
+
|
|
175
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
169
176
|
|
|
170
177
|
const cryptoReady = await crypto.init()
|
|
171
178
|
if (!cryptoReady.success)
|
|
@@ -203,13 +210,15 @@ export const handle = kit.createHandle({
|
|
|
203
210
|
|
|
204
211
|
```svelte
|
|
205
212
|
<script lang='ts'>
|
|
206
|
-
import { browser } from '$app/
|
|
207
|
-
import {
|
|
213
|
+
import { browser } from '$app/env'
|
|
214
|
+
import { resolve } from '$app/paths'
|
|
208
215
|
import { page } from '$app/state'
|
|
209
216
|
import { crypto } from '@h-ai/crypto'
|
|
210
217
|
import { kit } from '@h-ai/kit'
|
|
211
218
|
import { untrack } from 'svelte'
|
|
212
219
|
|
|
220
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
221
|
+
|
|
213
222
|
// 一次性安装:内部按 _kit.yml 决定是否启用 transport,并预热 crypto
|
|
214
223
|
if (browser) {
|
|
215
224
|
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), { crypto, basePath: base })
|
|
@@ -276,7 +285,7 @@ export const publicKitConfig = { transport: appKitConfig.transport }
|
|
|
276
285
|
Pass the public projection through `src/routes/+layout.server.ts` to the browser layout above, without importing the server configuration into browser code:
|
|
277
286
|
|
|
278
287
|
```ts
|
|
279
|
-
import { publicKitConfig } from '
|
|
288
|
+
import { publicKitConfig } from '#lib/server/kit-config.js'
|
|
280
289
|
|
|
281
290
|
export function load() {
|
|
282
291
|
return { kitConfig: publicKitConfig }
|
|
@@ -308,6 +317,10 @@ Automatic request auditing runs only when `audit.isInitialized`. Static sites an
|
|
|
308
317
|
|
|
309
318
|
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.
|
|
310
319
|
|
|
320
|
+
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.
|
|
321
|
+
|
|
322
|
+
Unused Crypto Helpers without a runtime entry or real consumers have been removed, along with the `CryptoServiceLike`, `EncryptedCookieConfig`, `CryptoCsrfConfig`, and `WebhookVerifyConfig` type exports. Protect cookies through the `kit.createHandle` configuration above; use the actual public `@h-ai/crypto` contract for other cryptographic operations.
|
|
323
|
+
|
|
311
324
|
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.
|
|
312
325
|
|
|
313
326
|
### Export paths
|
|
@@ -383,6 +396,13 @@ export const PATCH = handler
|
|
|
383
396
|
|
|
384
397
|
## Error messages and correlated logs
|
|
385
398
|
|
|
399
|
+
`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.
|
|
400
|
+
|
|
401
|
+
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.
|
|
402
|
+
|
|
403
|
+
`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`.
|
|
404
|
+
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.
|
|
405
|
+
|
|
386
406
|
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
407
|
|
|
388
408
|
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.
|
package/README.ja.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
`@h-ai/kit` は hai-framework の SvelteKit 統合モジュールで、共通のリクエストパイプラインと同一オリジンの endpoint ツールを提供します:
|
|
10
10
|
|
|
11
|
+
実行時の操作は `kit.xx` を使います。公開名前空間の型はサーバー側の `KitFunctions` とブラウザー側の `BrowserKitFunctions` です。ブラウザー条件入口では client、CRUD、ページ内 Token、既定の言語操作だけを利用できます。`AuthCookieWriter` はサーバー認証が現在のリクエストの Cookie に書き込む最小契約です。内部 helper を import せずに、再エクスポートしたメソッドや名前空間の宣言を生成できます。
|
|
12
|
+
|
|
11
13
|
- `kit.createHandle()`:セッション解析、ルートガード、middleware チェーン、任意の通信・Cookie 暗号化。
|
|
12
14
|
- `kit.handler()`:API endpoint の例外処理境界。
|
|
13
15
|
- `kit.guard` / `kit.response` / `kit.validate` / `kit.auth` / `kit.client` / `kit.crud`
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
### インストール
|
|
34
36
|
|
|
35
37
|
```bash
|
|
36
|
-
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.
|
|
38
|
+
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.8
|
|
37
39
|
npm install svelte @sveltejs/kit zod
|
|
38
40
|
npm install -D @sveltejs/vite-plugin-svelte vite
|
|
39
41
|
```
|
|
@@ -47,44 +49,43 @@ npm install -D @sveltejs/vite-plugin-svelte vite
|
|
|
47
49
|
|
|
48
50
|
#### プロジェクト設定
|
|
49
51
|
|
|
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
|
-
```
|
|
68
|
-
|
|
69
52
|
**vite.config.ts**:
|
|
70
53
|
|
|
71
54
|
```ts
|
|
55
|
+
import adapter from '@sveltejs/adapter-node'
|
|
72
56
|
import { sveltekit } from '@sveltejs/kit/vite'
|
|
57
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
73
58
|
import tailwindcss from '@tailwindcss/vite'
|
|
74
59
|
import { defineConfig } from 'vite'
|
|
75
60
|
|
|
76
61
|
export default defineConfig({
|
|
77
|
-
plugins: [
|
|
78
|
-
|
|
79
|
-
|
|
62
|
+
plugins: [
|
|
63
|
+
sveltekit({
|
|
64
|
+
adapter: adapter(),
|
|
65
|
+
preprocess: [
|
|
66
|
+
vitePreprocess()
|
|
67
|
+
],
|
|
68
|
+
compilerOptions: { runes: true },
|
|
69
|
+
// 绝对路径保持 SSR 与浏览器端认证、加密路径一致。
|
|
70
|
+
paths: { base: '', relative: false },
|
|
71
|
+
}),
|
|
72
|
+
tailwindcss(),
|
|
73
|
+
],
|
|
74
|
+
optimizeDeps: {
|
|
75
|
+
exclude: ['bits-ui']
|
|
76
|
+
},
|
|
77
|
+
ssr: {
|
|
78
|
+
noExternal: ['@h-ai/ui', '@h-ai/kit'],
|
|
79
|
+
external: ['@h-ai/core', 'sm-crypto']
|
|
80
|
+
},
|
|
80
81
|
})
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
Crypto または Kit/IAM を使う場合、アプリの本番依存に `sm-crypto@0.5.
|
|
84
|
+
Crypto または Kit/IAM を使う場合、アプリの本番依存に `sm-crypto@0.5.8` を含めます。CLI 生成済みのプロジェクトでは設定済みです。手動導入は `pnpm add sm-crypto@0.5.8` を実行してください。adapter-node は直接の本番依存も参照するため、external 指定だけでは不十分です。
|
|
84
85
|
|
|
85
86
|
#### リクエストパイプラインの構築
|
|
86
87
|
|
|
87
|
-
`/yyy/`などの配下に配置する場合、SvelteKitの`
|
|
88
|
+
`/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
89
|
|
|
89
90
|
以下の Hook 例では、アプリがデータベース、共有キャッシュ、IAM を初期化し、各結果を確認済みとします。リソースの終了はアプリが管理し、リクエストごとに初期化しません。
|
|
90
91
|
|
|
@@ -106,7 +107,11 @@ const haiHandle = kit.createHandle({
|
|
|
106
107
|
protectedPaths: ['/admin/*', '/api/*'],
|
|
107
108
|
publicPaths: ['/api/auth/*', '/api/public/*'],
|
|
108
109
|
},
|
|
109
|
-
rateLimit: {
|
|
110
|
+
rateLimit: {
|
|
111
|
+
windowMs: 60_000,
|
|
112
|
+
maxRequests: 100,
|
|
113
|
+
store: kit.middleware.createCacheRateLimitStore(cache, { namespace: 'app' })
|
|
114
|
+
},
|
|
110
115
|
logging: true,
|
|
111
116
|
})
|
|
112
117
|
|
|
@@ -147,7 +152,7 @@ export const { apiFetch } = client
|
|
|
147
152
|
|
|
148
153
|
```typescript
|
|
149
154
|
// 使用
|
|
150
|
-
import { apiFetch } from '
|
|
155
|
+
import { apiFetch } from '#lib/utils/api.js'
|
|
151
156
|
|
|
152
157
|
const response = await apiFetch('/api/users', { method: 'GET' })
|
|
153
158
|
```
|
|
@@ -161,17 +166,19 @@ const response = await apiFetch('/api/users', { method: 'GET' })
|
|
|
161
166
|
|
|
162
167
|
kit の通信暗号化は `@h-ai/crypto` に委譲します。サーバーは `crypto.transport.createServer({ serverKeyRing, keyStore })` で管理器を作成し、ブラウザーは `kit.client.installBrowserTransport(kitConfig, { crypto })` の 1 行で同一オリジンの fetch ラッパーを導入します。サーバーには管理済み鍵環と共有 registry も渡します。
|
|
163
168
|
|
|
164
|
-
サブディレクトリでは `$app/paths` の `
|
|
169
|
+
サブディレクトリでは `$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
170
|
|
|
166
171
|
**src/hooks.server.ts**:
|
|
167
172
|
|
|
168
173
|
```ts
|
|
169
|
-
import {
|
|
170
|
-
import { appKitConfig } from '$lib/server/kit-config'
|
|
174
|
+
import { resolve } from '$app/paths'
|
|
171
175
|
import { cache } from '@h-ai/cache'
|
|
172
176
|
import { createRedisTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
|
|
173
177
|
import { iam } from '@h-ai/iam'
|
|
174
178
|
import { kit } from '@h-ai/kit'
|
|
179
|
+
import { appKitConfig } from '#lib/server/kit-config.js'
|
|
180
|
+
|
|
181
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
175
182
|
|
|
176
183
|
const cryptoReady = await crypto.init()
|
|
177
184
|
if (!cryptoReady.success)
|
|
@@ -209,13 +216,15 @@ export const handle = kit.createHandle({
|
|
|
209
216
|
|
|
210
217
|
```svelte
|
|
211
218
|
<script lang='ts'>
|
|
212
|
-
import { browser } from '$app/
|
|
213
|
-
import {
|
|
219
|
+
import { browser } from '$app/env'
|
|
220
|
+
import { resolve } from '$app/paths'
|
|
214
221
|
import { page } from '$app/state'
|
|
215
222
|
import { crypto } from '@h-ai/crypto'
|
|
216
223
|
import { kit } from '@h-ai/kit'
|
|
217
224
|
import { untrack } from 'svelte'
|
|
218
225
|
|
|
226
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
227
|
+
|
|
219
228
|
// 一次性安装:内部按 _kit.yml 决定是否启用 transport,并预热 crypto
|
|
220
229
|
if (browser) {
|
|
221
230
|
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), { crypto, basePath: base })
|
|
@@ -282,7 +291,7 @@ export const publicKitConfig = { transport: appKitConfig.transport }
|
|
|
282
291
|
`src/routes/+layout.server.ts` から上記ブラウザーレイアウトへ公開部分だけ渡します。ブラウザー側からサーバー設定をインポートしません。
|
|
283
292
|
|
|
284
293
|
```ts
|
|
285
|
-
import { publicKitConfig } from '
|
|
294
|
+
import { publicKitConfig } from '#lib/server/kit-config.js'
|
|
286
295
|
|
|
287
296
|
export function load() {
|
|
288
297
|
return { kitConfig: publicKitConfig }
|
|
@@ -314,6 +323,10 @@ export function load() {
|
|
|
314
323
|
|
|
315
324
|
`crypto.encryptedCookies` を設定する場合、`crypto.cookieEncryptionKey` または `HAI_KIT_COOKIE_KEY` が必要です。鍵がなければ `createHandle()` ですぐエラーになります。保護対象 Cookie の平文・破損暗号文・復号失敗は未存在として扱い、暗号化失敗時も平文保存に切り替えません。
|
|
316
325
|
|
|
326
|
+
鍵は 32 桁の16進文字列です。SM4-CBC と HMAC-SHA256 の鍵を別々に導出し、バージョン・Cookie 名・IV・暗号文を認証してから復号するため、改ざんや名前間の置換を拒否します。更新後は認証のない旧形式 Cookie が無効となり、再ログインが必要です。旧形式へのフォールバックはありません。
|
|
327
|
+
|
|
328
|
+
実行入口と実際の利用者がない Crypto Helpers を削除し、`CryptoServiceLike`、`EncryptedCookieConfig`、`CryptoCsrfConfig`、`WebhookVerifyConfig` の型公開も廃止しました。Cookie の保護には上記の `kit.createHandle` 設定を使い、ほかの暗号操作には `@h-ai/crypto` の実際の公開契約を使用してください。
|
|
329
|
+
|
|
317
330
|
認証 Cookie は `NODE_ENV` または `HAI_CORE_ENV` が `production` の場合に `Secure` を既定で有効にします。`HAI_E2E` / `HAI_E2E_MOCK` は Cookie の安全要件を緩和しません。本番のレート制限も両方の環境変数を認識し、明示的なテストフラグだけがメモリカウンターを許可します。
|
|
318
331
|
|
|
319
332
|
### エクスポートパス
|
|
@@ -381,6 +394,13 @@ export const PATCH = handler
|
|
|
381
394
|
|
|
382
395
|
## エラー表示とログの関連付け
|
|
383
396
|
|
|
397
|
+
`kit.response.fromError/fromResult` は安全なフィールド検証 `cause`、明示的な `publicExt`(`ext` に変換)、`httpStatus` を保持します。内部原因や拡張情報は公開せず、モジュール失敗ごとに関連ログを一度だけ記録します。
|
|
398
|
+
|
|
399
|
+
HTTP/UI 境界で業務メッセージを公開するのは、明示された有効な4xx状態のみです。`httpStatus` が未指定または不正な場合は500として安全なメッセージを返します。独自の業務エラーには完全なエラー定義または明示的な状態を指定してください。
|
|
400
|
+
|
|
401
|
+
`kit.response.validationError(errors)` も同じ許可リストを使い、`error.cause` に翻訳済みの `field/message` だけを返します。拒否された値、SQL、追加項目は公開せず、`details.errors` は使いません。
|
|
402
|
+
応答ヘルパー、認証ガード、レート制限は `HaiCommonError` と `httpStatus`・`traceId`・`X-Trace-Id` を共用します。未認証は `UNAUTHORIZED.code`、権限不足は `FORBIDDEN.code`、頻度超過は `RATE_LIMIT_EXCEEDED.code`(429)。制限ストアの障害は安全な503応答を返し、原因はサーバーログに保持します。
|
|
403
|
+
|
|
384
404
|
HTTP エラーには安全な翻訳文と追跡番号を含めます。ToastContainer は番号を既定で非表示にし、調査情報のアイコンから表示・コピーできます。同じ ID を error.traceId と X-Trace-Id に返します。requestId を traceId として共有し、ブラウザーとサーバーのログを関連付けます。脱機密化した例外・スタック・操作情報はログに残し、内部 cause/ext は公開しません。項目検証は翻訳済みの項目名とメッセージだけを返します。
|
|
385
405
|
|
|
386
406
|
HTTP/UI 境界には core.error.toPublic(error, traceId?)、再翻訳後の表示には core.error.formatMessage(error) を使い、ID を重複追加しません。ログには完全な Error を渡してください。DB ログにはドライバーコード、脱機密化したメッセージ・スタック、表・列を残し、SQL 本文、バインド値と接続認証情報を記録しません。
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
`@h-ai/kit` 是 hai-framework 的 SvelteKit 集成模块,提供统一的请求管道与同源 endpoint 工具:
|
|
10
10
|
|
|
11
|
+
运行时统一通过 `kit.xx` 使用。根入口公开 `KitFunctions`(服务端)和 `BrowserKitFunctions`(浏览器)命名空间类型,浏览器条件入口的 `kit` 仅包含客户端、CRUD、页面 Token 与默认语言操作。`AuthCookieWriter` 是服务端认证对当前请求 Cookie 的最小写入契约。导出这些方法或命名空间时,可直接生成消费者声明,无需导入内部 helper。
|
|
12
|
+
|
|
11
13
|
- `kit.createHandle()`:会话解析、路由守卫、中间件链、可选传输加密与 Cookie 加密
|
|
12
14
|
- `kit.handler()`:API 端点异常边界
|
|
13
15
|
- `kit.guard` / `kit.response` / `kit.validate` / `kit.auth` / `kit.client` / `kit.crud`
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
### 安装
|
|
34
36
|
|
|
35
37
|
```bash
|
|
36
|
-
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.
|
|
38
|
+
npm install @h-ai/kit @h-ai/core sm-crypto@0.5.8
|
|
37
39
|
npm install svelte @sveltejs/kit zod
|
|
38
40
|
npm install -D @sveltejs/vite-plugin-svelte vite
|
|
39
41
|
```
|
|
@@ -47,44 +49,43 @@ npm install -D @sveltejs/vite-plugin-svelte vite
|
|
|
47
49
|
|
|
48
50
|
#### 项目配置
|
|
49
51
|
|
|
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
|
-
```
|
|
68
|
-
|
|
69
52
|
**vite.config.ts**:
|
|
70
53
|
|
|
71
54
|
```ts
|
|
55
|
+
import adapter from '@sveltejs/adapter-node'
|
|
72
56
|
import { sveltekit } from '@sveltejs/kit/vite'
|
|
57
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
73
58
|
import tailwindcss from '@tailwindcss/vite'
|
|
74
59
|
import { defineConfig } from 'vite'
|
|
75
60
|
|
|
76
61
|
export default defineConfig({
|
|
77
|
-
plugins: [
|
|
78
|
-
|
|
79
|
-
|
|
62
|
+
plugins: [
|
|
63
|
+
sveltekit({
|
|
64
|
+
adapter: adapter(),
|
|
65
|
+
preprocess: [
|
|
66
|
+
vitePreprocess()
|
|
67
|
+
],
|
|
68
|
+
compilerOptions: { runes: true },
|
|
69
|
+
// 绝对路径保持 SSR 与浏览器端认证、加密路径一致。
|
|
70
|
+
paths: { base: '', relative: false },
|
|
71
|
+
}),
|
|
72
|
+
tailwindcss(),
|
|
73
|
+
],
|
|
74
|
+
optimizeDeps: {
|
|
75
|
+
exclude: ['bits-ui']
|
|
76
|
+
},
|
|
77
|
+
ssr: {
|
|
78
|
+
noExternal: ['@h-ai/ui', '@h-ai/kit'],
|
|
79
|
+
external: ['@h-ai/core', 'sm-crypto']
|
|
80
|
+
},
|
|
80
81
|
})
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
使用 Crypto 或依赖它的 Kit/IAM 时,应用生产依赖需包含 `sm-crypto@0.5.
|
|
84
|
+
使用 Crypto 或依赖它的 Kit/IAM 时,应用生产依赖需包含 `sm-crypto@0.5.8`。CLI 已配好这一项;手工接入时运行 `pnpm add sm-crypto@0.5.8`。仅设置 external 不够,adapter-node 还需要这项直接依赖声明才能保留原包加载。
|
|
84
85
|
|
|
85
86
|
#### 搭建请求管道
|
|
86
87
|
|
|
87
|
-
应用部署在 `/yyy/` 等子目录时,SvelteKit 的 `
|
|
88
|
+
应用部署在 `/yyy/` 等子目录时,SvelteKit 的 `paths.base` 与守卫使用的实际 URL 应保持一致:将 `resolve('/').replace(/\/$/, '')`(配置 `paths.relative: false`) 加到 `protectedPaths`、`publicPaths` 和 `loginUrl`,例如 `${base}/api/*`。认证页面使用 ``kit.client.createAuthActions({ basePath: `${base}/api/auth` })``。Kit 根据应用路由识别 `/api/*`,包括路由分组和挂载前缀;未认证的 API 返回 401,页面按配置跳转登录。生成模板已处理这些路径。
|
|
88
89
|
|
|
89
90
|
以下 Hook 片段假设应用已完成数据库、共享缓存和 IAM 初始化,并检查每项初始化结果。关闭资源由应用统一负责;不在每个请求中初始化模块。
|
|
90
91
|
|
|
@@ -106,7 +107,11 @@ const haiHandle = kit.createHandle({
|
|
|
106
107
|
protectedPaths: ['/admin/*', '/api/*'],
|
|
107
108
|
publicPaths: ['/api/auth/*', '/api/public/*'],
|
|
108
109
|
},
|
|
109
|
-
rateLimit: {
|
|
110
|
+
rateLimit: {
|
|
111
|
+
windowMs: 60_000,
|
|
112
|
+
maxRequests: 100,
|
|
113
|
+
store: kit.middleware.createCacheRateLimitStore(cache, { namespace: 'app' })
|
|
114
|
+
},
|
|
110
115
|
logging: true,
|
|
111
116
|
})
|
|
112
117
|
|
|
@@ -147,7 +152,7 @@ export const { apiFetch } = client
|
|
|
147
152
|
|
|
148
153
|
```typescript
|
|
149
154
|
// 使用
|
|
150
|
-
import { apiFetch } from '
|
|
155
|
+
import { apiFetch } from '#lib/utils/api.js'
|
|
151
156
|
|
|
152
157
|
const response = await apiFetch('/api/users', { method: 'GET' })
|
|
153
158
|
```
|
|
@@ -161,17 +166,19 @@ const response = await apiFetch('/api/users', { method: 'GET' })
|
|
|
161
166
|
|
|
162
167
|
kit 的传输加密也统一委托 `@h-ai/crypto`:服务端通过 `crypto.transport.createServer({ serverKeyRing, keyStore })` 创建管理器,浏览器端通过 `kit.client.installBrowserTransport(kitConfig, { crypto })` 一行安装同源 fetch 包装。服务端还须注入受管密钥环和共享 registry。
|
|
163
168
|
|
|
164
|
-
|
|
169
|
+
部署到子目录时,配置 `paths.relative: false`,从 `$app/paths` 以 `resolve('/').replace(/\/$/, '')` 得到 `base`:服务端 `crypto.transport` 传入 `basePath: base`,浏览器调用 `installBrowserTransport(kitConfig, { crypto, basePath: base })`;独立 `kit.client.create` 的 `transport.basePath` 采用同一值。默认空字符串表示域名根目录。这个运行时参数来自宿主的构建目录,不在 YAML 另设一份,也不从转发头推断。`keyExchangePath`、`excludePaths` 和路由容量仍使用完整请求路径。CLI 的 SSR 模板已同步装配。
|
|
165
170
|
|
|
166
171
|
**src/hooks.server.ts**:
|
|
167
172
|
|
|
168
173
|
```ts
|
|
169
|
-
import {
|
|
170
|
-
import { appKitConfig } from '$lib/server/kit-config'
|
|
174
|
+
import { resolve } from '$app/paths'
|
|
171
175
|
import { cache } from '@h-ai/cache'
|
|
172
176
|
import { createRedisTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
|
|
173
177
|
import { iam } from '@h-ai/iam'
|
|
174
178
|
import { kit } from '@h-ai/kit'
|
|
179
|
+
import { appKitConfig } from '#lib/server/kit-config.js'
|
|
180
|
+
|
|
181
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
175
182
|
|
|
176
183
|
const cryptoReady = await crypto.init()
|
|
177
184
|
if (!cryptoReady.success)
|
|
@@ -209,13 +216,15 @@ export const handle = kit.createHandle({
|
|
|
209
216
|
|
|
210
217
|
```svelte
|
|
211
218
|
<script lang='ts'>
|
|
212
|
-
import { browser } from '$app/
|
|
213
|
-
import {
|
|
219
|
+
import { browser } from '$app/env'
|
|
220
|
+
import { resolve } from '$app/paths'
|
|
214
221
|
import { page } from '$app/state'
|
|
215
222
|
import { crypto } from '@h-ai/crypto'
|
|
216
223
|
import { kit } from '@h-ai/kit'
|
|
217
224
|
import { untrack } from 'svelte'
|
|
218
225
|
|
|
226
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
227
|
+
|
|
219
228
|
// 一次性安装:内部按 _kit.yml 决定是否启用 transport,并预热 crypto
|
|
220
229
|
if (browser) {
|
|
221
230
|
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), { crypto, basePath: base })
|
|
@@ -282,7 +291,7 @@ export const publicKitConfig = { transport: appKitConfig.transport }
|
|
|
282
291
|
在 `src/routes/+layout.server.ts` 将公共投影交给上述浏览器布局,不导入服务端配置文件:
|
|
283
292
|
|
|
284
293
|
```ts
|
|
285
|
-
import { publicKitConfig } from '
|
|
294
|
+
import { publicKitConfig } from '#lib/server/kit-config.js'
|
|
286
295
|
|
|
287
296
|
export function load() {
|
|
288
297
|
return { kitConfig: publicKitConfig }
|
|
@@ -316,6 +325,10 @@ export function load() {
|
|
|
316
325
|
|
|
317
326
|
配置 `crypto.encryptedCookies` 时必须同时提供 `crypto.cookieEncryptionKey`,或设置 `HAI_KIT_COOKIE_KEY`。缺少密钥会在 `createHandle()` 时立即报错;受保护 Cookie 的明文、损坏密文或解密失败都按不存在处理,加密失败也不会回退写入明文。
|
|
318
327
|
|
|
328
|
+
密钥必须是 32 位十六进制字符串。Cookie 使用独立派生的 SM4-CBC/HMAC-SHA256 密钥,认证版本、Cookie 名、IV 与密文后才解密,防止修改或跨名称替换。升级后旧版无认证 Cookie 被视为无效,用户需重新登录;不回退接受旧格式。
|
|
329
|
+
|
|
330
|
+
原先没有运行入口和真实消费者的 Crypto Helpers 已删除,`CryptoServiceLike`、`EncryptedCookieConfig`、`CryptoCsrfConfig`、`WebhookVerifyConfig` 类型也不再导出。Cookie 保护通过上述 `kit.createHandle` 配置接入;其他加密操作使用 `@h-ai/crypto` 的真实公开契约。
|
|
331
|
+
|
|
319
332
|
认证 Cookie 的 `Secure` 默认值在 `NODE_ENV` 或 `HAI_CORE_ENV` 为 `production` 时开启,`HAI_E2E` / `HAI_E2E_MOCK` 不降低 Cookie 安全要求。生产限流同样识别这两个环境标志;仅显式测试开关可放宽内存计数器限制。
|
|
320
333
|
|
|
321
334
|
### 导出路径
|
|
@@ -383,6 +396,14 @@ export const PATCH = handler
|
|
|
383
396
|
|
|
384
397
|
## 错误提示与关联日志
|
|
385
398
|
|
|
399
|
+
`kit.response.fromError/fromResult` 保留安全的字段校验 `cause`、显式公开的 `publicExt`(映射为 `ext`)和 `httpStatus`;任意内部 cause/ext 不公开。每次模块失败只写一条关联日志。
|
|
400
|
+
|
|
401
|
+
HTTP/UI 边界只对明确的有效 4xx 状态公开业务提示;错误缺少 `httpStatus` 或状态非法时按 500 返回安全提示。手写业务错误须提供完整错误定义或显式状态,不能依赖缺省状态展示原文。
|
|
402
|
+
|
|
403
|
+
`kit.response.validationError(errors)` 也沿用同一白名单:响应 `error.cause` 只含本地化的 `field/message`,不回显 rejectedValue、SQL 或其他额外字段,不再使用 `details.errors`。
|
|
404
|
+
|
|
405
|
+
快捷响应、认证守卫和限流均使用 `HaiCommonError` 标准错误码,附带 `httpStatus`、`traceId` 与 `X-Trace-Id`。未认证返回 `UNAUTHORIZED.code`,无权限返回 `FORBIDDEN.code`,限流返回 `RATE_LIMIT_EXCEEDED.code`(429);限流存储故障返回安全的 503,原始原因只记服务端日志。
|
|
406
|
+
|
|
386
407
|
HTTP 错误返回本地化安全文案及追踪编号;ToastContainer 默认隐藏编号,由排查信息图标展开和复制。响应同时包含 `error.traceId` 和 `X-Trace-Id`。框架复用本次 requestId 作为 traceId,前后端日志自动关联。底层异常、堆栈和操作上下文经凭据脱敏后写入日志,不通过 cause/ext 向用户公开。字段校验只返回本地化的字段名和错误。
|
|
387
408
|
|
|
388
409
|
`core.error.toPublic(error, traceId?)` 用于接口/UI 边界;`core.error.formatMessage(error)` 用于重新翻译后的提示,重复调用不会重复追加标识。日志传入完整 Error 对象,不能只记录通用标题或提前 String(error) 丢失上下文。数据库失败记录驱动错误码、消息、堆栈及表/列;SQL 原文、绑定值和连接凭据不进入日志。
|
package/dist/adapter/index.d.ts
CHANGED
|
@@ -8,14 +8,14 @@ import * as _sveltejs_kit from '@sveltejs/kit';
|
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```js
|
|
11
|
-
* //
|
|
11
|
+
* // vite.config.ts
|
|
12
12
|
* import { createAdapter } from '@h-ai/kit/adapter'
|
|
13
|
+
* import { sveltekit } from '@sveltejs/kit/vite'
|
|
14
|
+
* import { defineConfig } from 'vite'
|
|
13
15
|
*
|
|
14
|
-
* export default {
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* },
|
|
18
|
-
* }
|
|
16
|
+
* export default defineConfig({
|
|
17
|
+
* plugins: [sveltekit({ adapter: await createAdapter() })],
|
|
18
|
+
* })
|
|
19
19
|
* ```
|
|
20
20
|
*
|
|
21
21
|
* @module kit-adapter
|
|
@@ -56,4 +56,5 @@ interface AdapterOptions {
|
|
|
56
56
|
*/
|
|
57
57
|
declare function createAdapter(options?: AdapterOptions): Promise<_sveltejs_kit.Adapter>;
|
|
58
58
|
|
|
59
|
-
export {
|
|
59
|
+
export { createAdapter };
|
|
60
|
+
export type { AdapterOptions };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/kit-adapter.ts"],"names":[],"mappings":";;;AA0DA,eAAsB,cAAc,OAAA,EAA0B;AAC5D,EAAA,MAAM;AAAA,IACJ,YAAA,GAAe,OAAA;AAAA,IACf,cAAA,GAAiB,cAAA;AAAA,IACjB,cAAA,GAAiB;AAAA,GACnB,GAAI,WAAW,EAAC;AAEhB,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,YAAA,IAAgB,MAAA;AAEzC,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,MAAM,EAAE,OAAA,EAAS,aAAA,EAAc,GAAI,MAAM,OAAO,0BAA0B,CAAA;AAC1E,IAAA,OAAO,aAAA,CAAc;AAAA,MACnB,KAAA,EAAO,cAAA;AAAA,MACP,MAAA,EAAQ,cAAA;AAAA,MACR,QAAA,EAAU,cAAA;AAAA,MACV,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,EACH;AAGA,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAY,GAAI,MAAM,OAAO,wBAAwB,CAAA;AACtE,EAAA,OAAO,WAAA,CAAY;AAAA,IACjB,GAAA,EAAK;AAAA,GACN,CAAA;AACH","file":"index.js","sourcesContent":["/**\n * SvelteKit 双构建模式 — 适配器工厂\n *\n * 根据 `VITE_ADAPTER` 环境变量自动选择 adapter-node 或 adapter-static,\n * 使同一个 SvelteKit 应用能同时产出 SSR(Web + API)和 SPA(H5/App)两种构建产物。\n *\n * @example\n * ```js\n * //
|
|
1
|
+
{"version":3,"sources":["../../src/kit-adapter.ts"],"names":[],"mappings":";;;AA0DA,eAAsB,cAAc,OAAA,EAA0B;AAC5D,EAAA,MAAM;AAAA,IACJ,YAAA,GAAe,OAAA;AAAA,IACf,cAAA,GAAiB,cAAA;AAAA,IACjB,cAAA,GAAiB;AAAA,GACnB,GAAI,WAAW,EAAC;AAEhB,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,YAAA,IAAgB,MAAA;AAEzC,EAAA,IAAI,SAAS,QAAA,EAAU;AACrB,IAAA,MAAM,EAAE,OAAA,EAAS,aAAA,EAAc,GAAI,MAAM,OAAO,0BAA0B,CAAA;AAC1E,IAAA,OAAO,aAAA,CAAc;AAAA,MACnB,KAAA,EAAO,cAAA;AAAA,MACP,MAAA,EAAQ,cAAA;AAAA,MACR,QAAA,EAAU,cAAA;AAAA,MACV,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,EACH;AAGA,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAY,GAAI,MAAM,OAAO,wBAAwB,CAAA;AACtE,EAAA,OAAO,WAAA,CAAY;AAAA,IACjB,GAAA,EAAK;AAAA,GACN,CAAA;AACH","file":"index.js","sourcesContent":["/**\n * SvelteKit 双构建模式 — 适配器工厂\n *\n * 根据 `VITE_ADAPTER` 环境变量自动选择 adapter-node 或 adapter-static,\n * 使同一个 SvelteKit 应用能同时产出 SSR(Web + API)和 SPA(H5/App)两种构建产物。\n *\n * @example\n * ```js\n * // vite.config.ts\n * import { createAdapter } from '@h-ai/kit/adapter'\n * import { sveltekit } from '@sveltejs/kit/vite'\n * import { defineConfig } from 'vite'\n *\n * export default defineConfig({\n * plugins: [sveltekit({ adapter: await createAdapter() })],\n * })\n * ```\n *\n * @module kit-adapter\n */\n\nimport process from 'node:process'\n\n/**\n * 双构建适配器配置\n * @example 模块内部上下文\n * ```ts\n * const options: AdapterOptions = {}\n * const adapter = await createAdapter(options)\n * void adapter\n * ```\n */\nexport interface AdapterOptions {\n /** adapter-node 输出目录,默认 `'build'` */\n nodeBuildDir?: string\n /** adapter-static 输出目录,默认 `'build-static'` */\n staticBuildDir?: string\n /** adapter-static 的 SPA fallback 页面,默认 `'index.html'` */\n staticFallback?: string\n}\n\n/**\n * 根据环境变量 `VITE_ADAPTER` 选择 SvelteKit adapter\n *\n * - `VITE_ADAPTER=node` → `@sveltejs/adapter-node`(SSR + API)\n * - `VITE_ADAPTER=static` → `@sveltejs/adapter-static`(SPA,可被 Capacitor 包装)\n * - 未设置时默认使用 `node`\n *\n * **注意**:adapter-node / adapter-static 仍然需要在应用的 devDependencies 中声明。\n * 本函数只做动态 import + 统一配置。\n * @param options - Node/static 输出目录和 SPA fallback;应用仍需安装对应适配器。\n * @returns 按 VITE_ADAPTER 选择的 SvelteKit adapter;缺失依赖时拒绝 Promise。\n * @example 模块内部上下文\n * ```ts\n * const adapter = await createAdapter({})\n * void adapter\n * ```\n */\nexport async function createAdapter(options?: AdapterOptions) {\n const {\n nodeBuildDir = 'build',\n staticBuildDir = 'build-static',\n staticFallback = 'index.html',\n } = options ?? {}\n\n const mode = process.env.VITE_ADAPTER ?? 'node'\n\n if (mode === 'static') {\n const { default: adapterStatic } = await import('@sveltejs/adapter-static')\n return adapterStatic({\n pages: staticBuildDir,\n assets: staticBuildDir,\n fallback: staticFallback,\n precompress: false,\n })\n }\n\n // 默认 node\n const { default: adapterNode } = await import('@sveltejs/adapter-node')\n return adapterNode({\n out: nodeBuildDir,\n })\n}\n"]}
|