@h-ai/kit 0.1.0-alpha.13 → 0.1.0-alpha.16
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.md +96 -32
- package/dist/browser.d.ts +8 -6
- package/dist/browser.js +8 -6
- package/dist/browser.js.map +1 -1
- package/dist/chunk-BM7IP4W5.js +211 -0
- package/dist/chunk-BM7IP4W5.js.map +1 -0
- package/dist/chunk-EMSCTFJJ.js +256 -0
- package/dist/chunk-EMSCTFJJ.js.map +1 -0
- package/dist/{chunk-FRYJMRTP.js → chunk-KJUUEH7I.js} +30 -3
- package/dist/chunk-KJUUEH7I.js.map +1 -0
- package/dist/{chunk-BRZK7V5O.js → chunk-MI65L22O.js} +64 -89
- package/dist/chunk-MI65L22O.js.map +1 -0
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +20 -2
- package/dist/client/index.js.map +1 -1
- package/dist/{index-BotXLTck.d.ts → index-M00CsNUR.d.ts} +149 -10
- package/dist/index.d.ts +20 -81
- package/dist/index.js +244 -278
- package/dist/index.js.map +1 -1
- package/dist/{kit-types-BHlDNNpd.d.ts → kit-types-XUMggtk_.d.ts} +14 -5
- package/dist/modules/a2a/index.d.ts +2 -2
- package/dist/modules/crypto/index.d.ts +167 -52
- package/dist/modules/crypto/index.js +2 -3
- package/dist/modules/crypto/index.js.map +1 -1
- package/package.json +5 -4
- package/dist/chunk-BRZK7V5O.js.map +0 -1
- package/dist/chunk-FRYJMRTP.js.map +0 -1
- package/dist/chunk-GRYHRXVC.js +0 -274
- package/dist/chunk-GRYHRXVC.js.map +0 -1
- package/dist/chunk-XTZUGWBY.js +0 -137
- package/dist/chunk-XTZUGWBY.js.map +0 -1
- package/dist/kit-crypto-types-C-gEvx04.d.ts +0 -253
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { resolveTransportPath, DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, shouldUseTransportForUrl, kitM } from './chunk-MI65L22O.js';
|
|
2
|
+
import process from 'process';
|
|
3
|
+
import { err } from '@h-ai/core';
|
|
4
|
+
import { TRANSPORT_PROTOCOL } from '@h-ai/crypto';
|
|
5
|
+
|
|
6
|
+
var DEFAULT_TOKEN_COOKIE_NAME = "hai_access_token";
|
|
7
|
+
var authState = {
|
|
8
|
+
cookieName: DEFAULT_TOKEN_COOKIE_NAME,
|
|
9
|
+
operations: null
|
|
10
|
+
};
|
|
11
|
+
function configureAuth(config) {
|
|
12
|
+
if (config.cookieName)
|
|
13
|
+
authState.cookieName = config.cookieName;
|
|
14
|
+
if (config.operations)
|
|
15
|
+
authState.operations = config.operations;
|
|
16
|
+
}
|
|
17
|
+
var defaultBrowserTokenStore = createTokenStore();
|
|
18
|
+
function getBearerTokenFromRequest(request) {
|
|
19
|
+
const authorization = request.headers.get("authorization");
|
|
20
|
+
if (!authorization)
|
|
21
|
+
return null;
|
|
22
|
+
const [scheme, token] = authorization.split(" ");
|
|
23
|
+
if (!scheme || !token)
|
|
24
|
+
return null;
|
|
25
|
+
if (scheme.toLowerCase() !== "bearer")
|
|
26
|
+
return null;
|
|
27
|
+
const trimmed = token.trim();
|
|
28
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
29
|
+
}
|
|
30
|
+
function getAccessToken(request, cookies) {
|
|
31
|
+
return getBearerTokenFromRequest(request) ?? cookies?.get(authState.cookieName) ?? null;
|
|
32
|
+
}
|
|
33
|
+
function setToken(cookies, token, maxAge) {
|
|
34
|
+
cookies.set(authState.cookieName, token, {
|
|
35
|
+
path: "/",
|
|
36
|
+
httpOnly: true,
|
|
37
|
+
sameSite: "lax",
|
|
38
|
+
secure: process.env.NODE_ENV === "production",
|
|
39
|
+
maxAge
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function clearToken(cookies) {
|
|
43
|
+
cookies.delete(authState.cookieName, { path: "/" });
|
|
44
|
+
}
|
|
45
|
+
function getAuthOperations() {
|
|
46
|
+
if (!authState.operations)
|
|
47
|
+
return null;
|
|
48
|
+
return typeof authState.operations === "function" ? authState.operations() : authState.operations;
|
|
49
|
+
}
|
|
50
|
+
function authNotConfiguredError() {
|
|
51
|
+
return err({
|
|
52
|
+
code: "KIT_AUTH_NOT_CONFIGURED",
|
|
53
|
+
message: kitM("kit_authNotConfigured")
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function runAuthLogin(cookies, credentials, run) {
|
|
57
|
+
const ops = getAuthOperations();
|
|
58
|
+
if (!ops)
|
|
59
|
+
return authNotConfiguredError();
|
|
60
|
+
const result = await run(ops, credentials);
|
|
61
|
+
if (result.success) {
|
|
62
|
+
setToken(cookies, result.data.tokens.accessToken, result.data.tokens.expiresIn);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
async function login(cookies, credentials) {
|
|
67
|
+
return runAuthLogin(cookies, credentials, (ops, input) => ops.login(input));
|
|
68
|
+
}
|
|
69
|
+
async function loginWithOtp(cookies, credentials) {
|
|
70
|
+
return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithOtp(input));
|
|
71
|
+
}
|
|
72
|
+
async function loginWithLdap(cookies, credentials) {
|
|
73
|
+
return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithLdap(input));
|
|
74
|
+
}
|
|
75
|
+
async function loginWithApiKey(cookies, credentials) {
|
|
76
|
+
return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithApiKey(input));
|
|
77
|
+
}
|
|
78
|
+
async function registerAndLogin(cookies, options) {
|
|
79
|
+
return runAuthLogin(cookies, options, (ops, input) => ops.registerAndLogin(input));
|
|
80
|
+
}
|
|
81
|
+
async function logout(cookies, accessToken) {
|
|
82
|
+
const ops = getAuthOperations();
|
|
83
|
+
if (accessToken && ops) {
|
|
84
|
+
await ops.logout(accessToken);
|
|
85
|
+
}
|
|
86
|
+
clearToken(cookies);
|
|
87
|
+
}
|
|
88
|
+
function createTokenStore(key = DEFAULT_TOKEN_COOKIE_NAME) {
|
|
89
|
+
return {
|
|
90
|
+
get() {
|
|
91
|
+
if (typeof window === "undefined") {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return window.localStorage.getItem(key);
|
|
95
|
+
},
|
|
96
|
+
set(token) {
|
|
97
|
+
if (typeof window === "undefined") {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
window.localStorage.setItem(key, token);
|
|
101
|
+
},
|
|
102
|
+
clear() {
|
|
103
|
+
if (typeof window === "undefined") {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
window.localStorage.removeItem(key);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function setBrowserToken(token) {
|
|
111
|
+
defaultBrowserTokenStore.set(token);
|
|
112
|
+
}
|
|
113
|
+
function clearBrowserToken() {
|
|
114
|
+
defaultBrowserTokenStore.clear();
|
|
115
|
+
}
|
|
116
|
+
var DEFAULT_KEY_EXCHANGE_URL = `/api${TRANSPORT_PROTOCOL.DEFAULT_KEY_EXCHANGE_PATH}`;
|
|
117
|
+
function getCookie(name) {
|
|
118
|
+
if (typeof document === "undefined")
|
|
119
|
+
return void 0;
|
|
120
|
+
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
|
|
121
|
+
return match?.[1];
|
|
122
|
+
}
|
|
123
|
+
function canEncryptRequestBody(body) {
|
|
124
|
+
return body === void 0 || body === null || typeof body === "string";
|
|
125
|
+
}
|
|
126
|
+
function canUseTransportForRequest(request) {
|
|
127
|
+
const method = request.method.toUpperCase();
|
|
128
|
+
if (method === "GET" || method === "HEAD") {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
const headers = new Headers(request.headers);
|
|
132
|
+
const contentType = headers.get("Content-Type") ?? "";
|
|
133
|
+
if (contentType.includes("application/json")) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return request.body === null;
|
|
137
|
+
}
|
|
138
|
+
function getCurrentOrigin() {
|
|
139
|
+
if (typeof window !== "undefined" && window.location?.origin) {
|
|
140
|
+
return window.location.origin;
|
|
141
|
+
}
|
|
142
|
+
return "http://localhost";
|
|
143
|
+
}
|
|
144
|
+
function toRequest(input, init) {
|
|
145
|
+
if (input instanceof Request)
|
|
146
|
+
return init ? new Request(input, init) : input;
|
|
147
|
+
return new Request(input, init);
|
|
148
|
+
}
|
|
149
|
+
function installBrowserTransportFetch(transportConfig) {
|
|
150
|
+
if (typeof window === "undefined" || !transportConfig)
|
|
151
|
+
return;
|
|
152
|
+
const fetchState = globalThis;
|
|
153
|
+
if (fetchState.__haiKitTransportFetchInstalled)
|
|
154
|
+
return;
|
|
155
|
+
const originalFetch = fetchState.__haiKitOriginalFetch ?? globalThis.fetch.bind(globalThis);
|
|
156
|
+
fetchState.__haiKitOriginalFetch = originalFetch;
|
|
157
|
+
const origin = getCurrentOrigin();
|
|
158
|
+
const keyExchangeUrl = new URL(transportConfig.keyExchangeUrl ?? DEFAULT_KEY_EXCHANGE_URL, origin).toString();
|
|
159
|
+
const keyExchangePath = resolveTransportPath(keyExchangeUrl, origin);
|
|
160
|
+
const transportClient = transportConfig.crypto.transport.createClient({
|
|
161
|
+
keyExchangeUrl,
|
|
162
|
+
fetch: originalFetch
|
|
163
|
+
});
|
|
164
|
+
const wrappedFetch = async (input, init) => {
|
|
165
|
+
const request = toRequest(input, init);
|
|
166
|
+
const requestUrl = new URL(request.url, origin);
|
|
167
|
+
const headers = new Headers(request.headers);
|
|
168
|
+
if (!shouldUseTransportForUrl(requestUrl, {
|
|
169
|
+
origin,
|
|
170
|
+
keyExchangePath,
|
|
171
|
+
excludePaths: transportConfig.excludePaths
|
|
172
|
+
})) {
|
|
173
|
+
return originalFetch(input, init);
|
|
174
|
+
}
|
|
175
|
+
if (headers.has(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER))
|
|
176
|
+
return originalFetch(input, init);
|
|
177
|
+
if (!canUseTransportForRequest(request))
|
|
178
|
+
return originalFetch(input, init);
|
|
179
|
+
return transportClient.encryptedFetch(request);
|
|
180
|
+
};
|
|
181
|
+
globalThis.fetch = wrappedFetch;
|
|
182
|
+
window.fetch = wrappedFetch;
|
|
183
|
+
fetchState.__haiKitTransportFetchInstalled = true;
|
|
184
|
+
}
|
|
185
|
+
function installBrowserTransport(config, deps) {
|
|
186
|
+
if (typeof window === "undefined" || config.transport === false)
|
|
187
|
+
return;
|
|
188
|
+
void deps.crypto.init();
|
|
189
|
+
installBrowserTransportFetch({
|
|
190
|
+
crypto: deps.crypto,
|
|
191
|
+
keyExchangeUrl: config.transport.keyExchangePath,
|
|
192
|
+
excludePaths: [...config.transport.excludePaths]
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function createKitClient(config = {}) {
|
|
196
|
+
const {
|
|
197
|
+
transport: transportConfig,
|
|
198
|
+
csrfCookieName = "hai_csrf",
|
|
199
|
+
csrfHeaderName = "X-CSRF-Token",
|
|
200
|
+
auth: authConfig
|
|
201
|
+
} = config;
|
|
202
|
+
const authStore = authConfig ? authConfig === true ? createTokenStore() : authConfig : null;
|
|
203
|
+
const transportOrigin = getCurrentOrigin();
|
|
204
|
+
const transportKeyExchangeUrl = transportConfig?.keyExchangeUrl ?? DEFAULT_KEY_EXCHANGE_URL;
|
|
205
|
+
const transportKeyExchangePath = transportConfig ? resolveTransportPath(transportKeyExchangeUrl, transportOrigin) : DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH;
|
|
206
|
+
const transportClient = transportConfig ? transportConfig.crypto.transport.createClient({
|
|
207
|
+
keyExchangeUrl: transportKeyExchangeUrl,
|
|
208
|
+
fetch
|
|
209
|
+
}) : null;
|
|
210
|
+
async function apiFetch(url, init = {}) {
|
|
211
|
+
const method = (init.method ?? "GET").toUpperCase();
|
|
212
|
+
const isWriteMethod = !["GET", "HEAD", "OPTIONS"].includes(method);
|
|
213
|
+
const canUseTransport = canEncryptRequestBody(init.body);
|
|
214
|
+
const headers = new Headers(init.headers);
|
|
215
|
+
if (authStore) {
|
|
216
|
+
const token = authStore.get();
|
|
217
|
+
if (token) {
|
|
218
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (isWriteMethod) {
|
|
222
|
+
const csrfToken = getCookie(csrfCookieName);
|
|
223
|
+
if (csrfToken) {
|
|
224
|
+
headers.set(csrfHeaderName, csrfToken);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (transportClient && canUseTransport && shouldUseTransportForUrl(url, {
|
|
228
|
+
origin: transportOrigin,
|
|
229
|
+
keyExchangePath: transportKeyExchangePath,
|
|
230
|
+
excludePaths: transportConfig?.excludePaths
|
|
231
|
+
})) {
|
|
232
|
+
return transportClient.encryptedFetch(url, { ...init, headers });
|
|
233
|
+
}
|
|
234
|
+
return fetch(url, { ...init, headers });
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
apiFetch,
|
|
238
|
+
get ready() {
|
|
239
|
+
return !transportClient || transportClient.ready();
|
|
240
|
+
},
|
|
241
|
+
async init() {
|
|
242
|
+
if (!transportClient)
|
|
243
|
+
return;
|
|
244
|
+
const result = await transportClient.init();
|
|
245
|
+
if (!result.success)
|
|
246
|
+
throw new Error(result.error.message);
|
|
247
|
+
},
|
|
248
|
+
destroy() {
|
|
249
|
+
transportClient?.destroy();
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export { clearBrowserToken, configureAuth, createKitClient, createTokenStore, getAccessToken, installBrowserTransport, installBrowserTransportFetch, login, loginWithApiKey, loginWithLdap, loginWithOtp, logout, registerAndLogin, setBrowserToken };
|
|
255
|
+
//# sourceMappingURL=chunk-EMSCTFJJ.js.map
|
|
256
|
+
//# sourceMappingURL=chunk-EMSCTFJJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/kit-auth.ts","../src/client/kit-client.ts"],"names":[],"mappings":"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","file":"chunk-EMSCTFJJ.js","sourcesContent":["/**\n * @h-ai/kit — Bearer 认证工具\n *\n * 提供服务端认证 Cookie 管理(login / logout)与浏览器端 Token 存储工具。\n * @module kit-auth\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ApiKeyCredentials, AuthResult, LdapCredentials, OtpCredentials, PasswordCredentials, RegisterOptions } from '@h-ai/iam'\nimport type { AuthOperations, AuthOperationsProvider } from './kit-types.js'\nimport process from 'node:process'\nimport { err } from '@h-ai/core'\nimport { kitM } from './kit-i18n.js'\n\n/** 默认 Token Cookie 名 */\nconst DEFAULT_TOKEN_COOKIE_NAME = 'hai_access_token'\n\n/**\n * 应用级认证配置(由 createHandle 一次性初始化,生命周期同进程)\n *\n * 此状态非请求级:仅在 createHandle() 时写入一次,后续所有请求只读访问。\n */\nconst authState = {\n cookieName: DEFAULT_TOKEN_COOKIE_NAME,\n operations: null as AuthOperationsProvider | null,\n}\n\n/**\n * 配置认证参数\n *\n * 通常由 `createHandle` 在初始化时自动调用,无需手动调用。\n */\nexport function configureAuth(config: { cookieName?: string, operations?: AuthOperationsProvider }): void {\n if (config.cookieName)\n authState.cookieName = config.cookieName\n if (config.operations)\n authState.operations = config.operations\n}\n\n/**\n * 获取当前配置的 Token Cookie 名\n */\nexport function getTokenCookieName(): string {\n return authState.cookieName\n}\n\ninterface CookieReader {\n get: (name: string) => string | undefined\n}\n\ninterface CookieWriter {\n set: (name: string, value: string, options: {\n path: string\n httpOnly: boolean\n sameSite: 'lax' | 'strict' | 'none'\n secure: boolean\n maxAge?: number\n }) => void\n delete: (name: string, options: { path: string }) => void\n}\n\n/** 浏览器 Token 存储器 */\nexport interface BrowserTokenStore {\n get: () => string | null\n set: (token: string) => void\n clear: () => void\n}\n\nconst defaultBrowserTokenStore = createTokenStore()\n\n// ─── 内部工具(仅 kit 包内使用) ───\n\n/**\n * 从请求 Authorization 头提取 Bearer Token。\n */\nexport function getBearerTokenFromRequest(request: Request): string | null {\n const authorization = request.headers.get('authorization')\n if (!authorization)\n return null\n\n const [scheme, token] = authorization.split(' ')\n if (!scheme || !token)\n return null\n\n if (scheme.toLowerCase() !== 'bearer')\n return null\n\n const trimmed = token.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\n/**\n * 从请求中统一提取 Access Token(Bearer 优先,其次配置的 Cookie 名)。\n */\nexport function getAccessToken(request: Request, cookies?: CookieReader): string | null {\n return getBearerTokenFromRequest(request) ?? cookies?.get(authState.cookieName) ?? null\n}\n\n// ─── 服务端认证 Cookie 管理 ───\n\n/**\n * 写入 Access Token Cookie(内部使用,由 login 自动调用)。\n */\nfunction setToken(cookies: CookieWriter, token: string, maxAge?: number): void {\n cookies.set(authState.cookieName, token, {\n path: '/',\n httpOnly: true,\n sameSite: 'lax',\n secure: process.env.NODE_ENV === 'production',\n maxAge,\n })\n}\n\n/**\n * 清除 Access Token Cookie(内部使用,由 logout 自动调用)。\n */\nfunction clearToken(cookies: CookieWriter): void {\n cookies.delete(authState.cookieName, { path: '/' })\n}\n\n// ─── 服务端高级认证 API(通过 kit.auth 暴露) ───\n\n/**\n * 获取已注入的认证操作;未配置时返回 null。\n *\n * 公共 API 不应 throw,因此此处用 null 信号配合上层短路返回 HaiResult。\n */\nfunction getAuthOperations(): AuthOperations | null {\n if (!authState.operations)\n return null\n return typeof authState.operations === 'function'\n ? authState.operations()\n : authState.operations\n}\n\n/**\n * 构造 \"auth 未配置\" 的标准 HaiResult 错误。\n *\n * 用于公共认证 API 在 `authState.operations` 未注入时返回,\n * 避免向调用方抛出未捕获异常。\n */\nfunction authNotConfiguredError(): HaiResult<never> {\n return err({\n code: 'KIT_AUTH_NOT_CONFIGURED',\n message: kitM('kit_authNotConfigured'),\n })\n}\n\n/**\n * 执行已配置的认证登录操作,并在成功时写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - 认证凭证\n * @param run - 具体 IAM 认证操作\n */\nasync function runAuthLogin<TCredentials>(\n cookies: CookieWriter,\n credentials: TCredentials,\n run: (ops: AuthOperations, credentials: TCredentials) => Promise<HaiResult<AuthResult>>,\n): Promise<HaiResult<AuthResult>> {\n const ops = getAuthOperations()\n if (!ops)\n return authNotConfiguredError()\n\n const result = await run(ops, credentials)\n if (result.success) {\n setToken(cookies, result.data.tokens.accessToken, result.data.tokens.expiresIn)\n }\n return result\n}\n\n/**\n * 服务端登录(密码):内部调用 iam.auth.login,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - 密码凭证(identifier + password)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n *\n * @example\n * ```ts\n * const result = await kit.auth.login(cookies, { identifier, password })\n * if (!result.success) return kit.response.fromError(result.error, IamErrorHttpStatus)\n * const { user, roles, permissions } = result.data\n * ```\n */\nexport async function login(\n cookies: CookieWriter,\n credentials: PasswordCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.login(input))\n}\n\n/**\n * 服务端登录(OTP 验证码):内部调用 iam.auth.loginWithOtp,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - OTP 凭证(identifier + code)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n *\n * @example\n * ```ts\n * const result = await kit.auth.loginWithOtp(cookies, { identifier, code })\n * ```\n */\nexport async function loginWithOtp(\n cookies: CookieWriter,\n credentials: OtpCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithOtp(input))\n}\n\n/**\n * 服务端登录(LDAP):内部调用 iam.auth.loginWithLdap,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - LDAP 凭证(username + password)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n *\n * @example\n * ```ts\n * const result = await kit.auth.loginWithLdap(cookies, { username, password })\n * ```\n */\nexport async function loginWithLdap(\n cookies: CookieWriter,\n credentials: LdapCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithLdap(input))\n}\n\n/**\n * 服务端登录(API Key):内部调用 iam.auth.loginWithApiKey,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - API Key 凭证(key)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n *\n * @example\n * ```ts\n * const result = await kit.auth.loginWithApiKey(cookies, { key: apiKey })\n * if (!result.success) return kit.response.fromError(result.error, IamErrorHttpStatus)\n * ```\n */\nexport async function loginWithApiKey(\n cookies: CookieWriter,\n credentials: ApiKeyCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithApiKey(input))\n}\n\n/**\n * 服务端注册并登录:内部调用 iam.auth.registerAndLogin,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param options - 注册选项(username、password、email 等)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n *\n * @example\n * ```ts\n * const result = await kit.auth.registerAndLogin(cookies, { username, email, password })\n * ```\n */\nexport async function registerAndLogin(\n cookies: CookieWriter,\n options: RegisterOptions,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, options, (ops, input) => ops.registerAndLogin(input))\n}\n\n/**\n * 服务端登出:内部调用 iam.auth.logout 使会话失效,并清除 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param accessToken - 访问令牌(为 null/undefined 时仅清除 Cookie)\n *\n * @example\n * ```ts\n * await kit.auth.logout(cookies, locals.accessToken)\n * return kit.response.ok(null)\n * ```\n */\nexport async function logout(\n cookies: CookieWriter,\n accessToken?: string | null,\n): Promise<void> {\n // 即使 auth 未配置也需要清除 Cookie,保证登出语义幂等\n const ops = getAuthOperations()\n if (accessToken && ops) {\n await ops.logout(accessToken)\n }\n clearToken(cookies)\n}\n\n// ─── 浏览器端 Token 存储(通过 kit.auth 暴露) ───\n\n/**\n * 创建浏览器端 Token 存储器(localStorage)。\n *\n * @param key - localStorage 键名,默认 `'hai_access_token'`\n */\nexport function createTokenStore(key = DEFAULT_TOKEN_COOKIE_NAME): BrowserTokenStore {\n return {\n get(): string | null {\n if (typeof window === 'undefined') {\n return null\n }\n return window.localStorage.getItem(key)\n },\n set(token: string): void {\n if (typeof window === 'undefined') {\n return\n }\n window.localStorage.setItem(key, token)\n },\n clear(): void {\n if (typeof window === 'undefined') {\n return\n }\n window.localStorage.removeItem(key)\n },\n }\n}\n\n/**\n * 写入浏览器端 Access Token。\n */\nexport function setBrowserToken(token: string): void {\n defaultBrowserTokenStore.set(token)\n}\n\n/**\n * 清除浏览器端 Access Token。\n */\nexport function clearBrowserToken(): void {\n defaultBrowserTokenStore.clear()\n}\n","/**\n * @h-ai/kit — 统一客户端\n *\n * 将 CSRF Token 附加与可选传输加密封装为统一 `apiFetch`。\n * 应用层只需调用一次 `createKitClient()`,后续 API 调用即可复用同一套安全能力。\n * @module kit-client\n */\n\nimport type { CryptoFunctions, TransportClient } from '@h-ai/crypto'\nimport type { BrowserTokenStore } from '../kit-auth.js'\nimport type { KitConfig } from '../kit-config.js'\nimport { TRANSPORT_PROTOCOL } from '@h-ai/crypto'\nimport { createTokenStore } from '../kit-auth.js'\nimport { DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, resolveTransportPath, shouldUseTransportForUrl } from '../modules/crypto/kit-transport-paths.js'\n\nconst DEFAULT_KEY_EXCHANGE_URL = `/api${TRANSPORT_PROTOCOL.DEFAULT_KEY_EXCHANGE_PATH}`\n\n// ─── 类型 ───\n\n/**\n * 传输加密客户端配置\n */\nexport interface ClientTransportConfig {\n /** @h-ai/crypto 服务实例 */\n crypto: CryptoFunctions\n /** 密钥交换端点 URL(默认 `'/api/_hai/key-exchange'`) */\n keyExchangeUrl?: string\n /** 排除路径(命中后强制走明文),需与服务端 transport.excludePaths 保持一致 */\n excludePaths?: string[]\n}\n\n/**\n * Kit 客户端配置\n */\nexport interface KitClientConfig {\n /**\n * 传输加密配置。\n * - 不提供 / undefined:不启用传输加密,仅 CSRF\n * - 提供对象:启用传输加密\n */\n transport?: ClientTransportConfig\n /** CSRF Cookie 名称(默认 `'hai_csrf'`) */\n csrfCookieName?: string\n /** CSRF Header 名称(默认 `'X-CSRF-Token'`) */\n csrfHeaderName?: string\n /**\n * 认证配置:自动从浏览器存储读取 Access Token 并注入请求头。\n * - `true`:使用默认 localStorage 存储(key = `hai_access_token`)\n * - `BrowserTokenStore`:使用自定义存储\n * - 不提供 / `false`:不自动注入\n */\n auth?: boolean | BrowserTokenStore\n}\n\n/**\n * Kit 客户端实例\n */\nexport interface KitClient {\n /** 统一 API fetch(写请求自动附加 CSRF,启用 transport 时自动加解密) */\n apiFetch: (url: string, init?: RequestInit) => Promise<Response>\n /** 传输加密是否就绪(未启用传输加密时始终为 true) */\n readonly ready: boolean\n /** 手动触发密钥交换(transport 启用时) */\n init: () => Promise<void>\n /** 销毁密钥状态 */\n destroy: () => void\n}\n\n// ─── 工具函数 ───\n\n/**\n * 从 document.cookie 中读取指定名称的 Cookie 值\n *\n * @param name - Cookie 名称\n * @returns Cookie 值;不存在或运行在服务端时返回 undefined\n */\nfunction getCookie(name: string): string | undefined {\n if (typeof document === 'undefined')\n return undefined\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match?.[1]\n}\n\n/**\n * 判断当前请求体是否可以作为文本载荷进行传输加密。\n *\n * 传输加密中间件会把解密后的明文重新注入 `Request`,当前仅能可靠保留\n * JSON 字符串请求体。`FormData` / `Blob` / `ArrayBuffer` 等二进制或浏览器\n * 自动生成边界的请求体必须保持原样发送,否则文件上传会被序列化成 `{}`。\n *\n * @param body fetch 请求体\n * @returns 是否支持客户端传输加密\n */\nfunction canEncryptRequestBody(body: BodyInit | null | undefined): boolean {\n return body === undefined || body === null || typeof body === 'string'\n}\n\nfunction canUseTransportForRequest(request: Request): boolean {\n const method = request.method.toUpperCase()\n if (method === 'GET' || method === 'HEAD') {\n return true\n }\n\n const headers = new Headers(request.headers)\n const contentType = headers.get('Content-Type') ?? ''\n if (contentType.includes('application/json')) {\n return true\n }\n\n return request.body === null\n}\n\nfunction getCurrentOrigin(): string {\n if (typeof window !== 'undefined' && window.location?.origin) {\n return window.location.origin\n }\n\n return 'http://localhost'\n}\n\nfunction toRequest(input: RequestInfo | URL, init?: RequestInit): Request {\n if (input instanceof Request)\n return init ? new Request(input, init) : input\n\n return new Request(input, init)\n}\n\ntype BrowserTransportFetchState = typeof globalThis & {\n __haiKitOriginalFetch?: typeof fetch\n __haiKitTransportFetchInstalled?: boolean\n}\n\n/**\n * 在浏览器启动时安装同源 transport fetch 包装。\n *\n * 该包装作用于同源 `/api/*` 与 SvelteKit `__data.json` 请求,页面文档、\n * 静态资源以及 multipart/form-data 等上传请求保持原样。应用只需安装一次,\n * 后续 `fetch` / SvelteKit 内部 data fetch / `kit.client.create()` 明文发送路径\n * 都会共用这层传输加密能力。\n */\nexport function installBrowserTransportFetch(transportConfig?: ClientTransportConfig): void {\n if (typeof window === 'undefined' || !transportConfig)\n return\n\n const fetchState = globalThis as BrowserTransportFetchState\n if (fetchState.__haiKitTransportFetchInstalled)\n return\n\n const originalFetch = fetchState.__haiKitOriginalFetch ?? globalThis.fetch.bind(globalThis)\n fetchState.__haiKitOriginalFetch = originalFetch\n\n const origin = getCurrentOrigin()\n const keyExchangeUrl = new URL(transportConfig.keyExchangeUrl ?? DEFAULT_KEY_EXCHANGE_URL, origin).toString()\n const keyExchangePath = resolveTransportPath(keyExchangeUrl, origin)\n const transportClient = transportConfig.crypto.transport.createClient({\n keyExchangeUrl,\n fetch: originalFetch,\n })\n\n const wrappedFetch: typeof fetch = async (input, init) => {\n const request = toRequest(input, init)\n const requestUrl = new URL(request.url, origin)\n const headers = new Headers(request.headers)\n\n if (!shouldUseTransportForUrl(requestUrl, {\n origin,\n keyExchangePath,\n excludePaths: transportConfig.excludePaths,\n })) {\n return originalFetch(input, init)\n }\n\n if (headers.has(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER))\n return originalFetch(input, init)\n\n if (!canUseTransportForRequest(request))\n return originalFetch(input, init)\n\n return transportClient.encryptedFetch(request)\n }\n\n globalThis.fetch = wrappedFetch\n window.fetch = wrappedFetch\n fetchState.__haiKitTransportFetchInstalled = true\n}\n\n/**\n * 用 `config/_kit.yml` 解析后的 {@link KitConfig} 一次性安装浏览器端传输加密。\n *\n * 行为:\n * - `config.transport === false` 时直接返回(不启用传输加密);\n * - 否则启动 {@link installBrowserTransportFetch},并预热 {@link CryptoFunctions} 模块。\n *\n * 应用层(SvelteKit `+layout.svelte` 或浏览器入口)只需调用此函数一次,\n * 之后所有同源 `/api/*` 与 `__data.json` 请求都会自动走传输加密。\n *\n * @param config 已解析的 kit 顶层配置\n * @param deps 外部依赖\n * @param deps.crypto `@h-ai/crypto` 模块实例\n *\n * @example\n * ```ts\n * import { browser } from '$app/environment'\n * import { crypto } from '@h-ai/crypto'\n * import { kit } from '@h-ai/kit'\n * import { adminConsoleKitConfig } from '$lib/config/kit-config.js'\n *\n * if (browser) {\n * kit.client.installBrowserTransport(adminConsoleKitConfig, { crypto })\n * }\n * ```\n */\nexport function installBrowserTransport(\n config: KitConfig,\n deps: { crypto: CryptoFunctions },\n): void {\n if (typeof window === 'undefined' || config.transport === false)\n return\n\n // crypto.init() 当前为同步副作用(signature 是 async 仅为接口一致性),\n // 此处不 await,以便 `+layout.svelte` 顶层模块加载阶段就完成初始化。\n void deps.crypto.init()\n\n installBrowserTransportFetch({\n crypto: deps.crypto,\n keyExchangeUrl: config.transport.keyExchangePath,\n excludePaths: [...config.transport.excludePaths],\n })\n}\n\n// ─── 主函数 ───\n\n/**\n * 创建 Kit 客户端\n *\n * 返回统一的 `apiFetch`,内部自动完成:\n * 1. CSRF Token 附加(写方法自动读取 Cookie + 设置 Header)\n * 2. 密钥交换(首次写请求时 lazy init,transport 启用时)\n * 3. 请求体加密(transport 启用时)\n * 4. 响应体解密(transport 启用时)\n *\n * @param config - 客户端配置\n * @returns KitClient 实例\n *\n * @example\n * ```ts\n * const client = createKitClient({ transport: { crypto } })\n * const res = await client.apiFetch('/api/users', { method: 'POST', body: '{}' })\n * ```\n */\nexport function createKitClient(config: KitClientConfig = {}): KitClient {\n const {\n transport: transportConfig,\n csrfCookieName = 'hai_csrf',\n csrfHeaderName = 'X-CSRF-Token',\n auth: authConfig,\n } = config\n\n // ── Auth token store ──\n const authStore: BrowserTokenStore | null = authConfig\n ? (authConfig === true ? createTokenStore() : authConfig)\n : null\n\n // ── 传输加密状态 ──\n const transportOrigin = getCurrentOrigin()\n const transportKeyExchangeUrl = transportConfig?.keyExchangeUrl ?? DEFAULT_KEY_EXCHANGE_URL\n const transportKeyExchangePath = transportConfig\n ? resolveTransportPath(transportKeyExchangeUrl, transportOrigin)\n : DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH\n const transportClient: TransportClient | null = transportConfig\n ? transportConfig.crypto.transport.createClient({\n keyExchangeUrl: transportKeyExchangeUrl,\n fetch,\n })\n : null\n\n /**\n * 统一 API fetch\n */\n async function apiFetch(url: string, init: RequestInit = {}): Promise<Response> {\n const method = (init.method ?? 'GET').toUpperCase()\n const isWriteMethod = !['GET', 'HEAD', 'OPTIONS'].includes(method)\n const canUseTransport = canEncryptRequestBody(init.body)\n\n const headers = new Headers(init.headers)\n\n // ── Auth ──\n if (authStore) {\n const token = authStore.get()\n if (token) {\n headers.set('Authorization', `Bearer ${token}`)\n }\n }\n\n // ── CSRF ──\n if (isWriteMethod) {\n const csrfToken = getCookie(csrfCookieName)\n if (csrfToken) {\n headers.set(csrfHeaderName, csrfToken)\n }\n }\n\n // ── 传输加密 ──\n if (\n transportClient\n && canUseTransport\n && shouldUseTransportForUrl(url, {\n origin: transportOrigin,\n keyExchangePath: transportKeyExchangePath,\n excludePaths: transportConfig?.excludePaths,\n })\n ) {\n return transportClient.encryptedFetch(url, { ...init, headers })\n }\n\n // ── 发送(FormData/Blob/Stream 等保持原样,不破坏浏览器边界) ──\n return fetch(url, { ...init, headers })\n }\n\n return {\n apiFetch,\n get ready() { return !transportClient || transportClient.ready() },\n async init() {\n if (!transportClient)\n return\n const result = await transportClient.init()\n if (!result.success)\n throw new Error(result.error.message)\n },\n destroy() {\n transportClient?.destroy()\n },\n }\n}\n"]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
1
3
|
// src/crud/kit-crud.ts
|
|
2
4
|
function resolveText(text) {
|
|
3
5
|
return typeof text === "function" ? text() : text;
|
|
@@ -65,7 +67,32 @@ function defineCrud(resource) {
|
|
|
65
67
|
api: resource.api
|
|
66
68
|
};
|
|
67
69
|
}
|
|
70
|
+
var AbsolutePathSchema = z.custom(
|
|
71
|
+
(value) => typeof value === "string" && value.startsWith("/"),
|
|
72
|
+
"path must start with /"
|
|
73
|
+
);
|
|
74
|
+
var KitTransportConfigInputSchema = z.union([
|
|
75
|
+
z.literal(false),
|
|
76
|
+
z.object({
|
|
77
|
+
keyExchangePath: AbsolutePathSchema.default("/api/_hai/key-exchange"),
|
|
78
|
+
excludePaths: z.array(AbsolutePathSchema).default([]),
|
|
79
|
+
requireEncryption: z.boolean().default(true),
|
|
80
|
+
encryptResponse: z.boolean().default(true),
|
|
81
|
+
maxClients: z.number().int().positive().default(1e4)
|
|
82
|
+
})
|
|
83
|
+
]);
|
|
84
|
+
var KitConfigSchema = z.object({
|
|
85
|
+
transport: KitTransportConfigInputSchema.optional()
|
|
86
|
+
}).transform(({ transport }) => ({
|
|
87
|
+
transport: resolveKitTransportConfig(transport ?? false)
|
|
88
|
+
}));
|
|
89
|
+
function resolveKitTransportConfig(input = false) {
|
|
90
|
+
return KitTransportConfigInputSchema.parse(input);
|
|
91
|
+
}
|
|
92
|
+
function resolveKitConfig(input = {}) {
|
|
93
|
+
return KitConfigSchema.parse(input);
|
|
94
|
+
}
|
|
68
95
|
|
|
69
|
-
export { defineCrud, resolveOptions, resolveText };
|
|
70
|
-
//# sourceMappingURL=chunk-
|
|
71
|
-
//# sourceMappingURL=chunk-
|
|
96
|
+
export { KitConfigSchema, defineCrud, resolveKitConfig, resolveKitTransportConfig, resolveOptions, resolveText };
|
|
97
|
+
//# sourceMappingURL=chunk-KJUUEH7I.js.map
|
|
98
|
+
//# sourceMappingURL=chunk-KJUUEH7I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/crud/kit-crud.ts","../src/kit-config.ts"],"names":[],"mappings":"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","file":"chunk-KJUUEH7I.js","sourcesContent":["/**\n * @h-ai/kit — CRUD 操作定义\n *\n * 声明式 CRUD 资源定义工厂函数。\n * @module kit-crud\n */\n\nimport type {\n CrudFieldDef,\n CrudFieldOptions,\n CrudOperations,\n CrudResourceDef,\n CrudTableColumn,\n CrudText,\n} from './kit-crud-types.js'\n\n/**\n * 解析 CrudText 为字符串\n */\nfunction resolveText(text: CrudText): string {\n return typeof text === 'function' ? text() : text\n}\n\n/**\n * 按 order 排序字段\n */\nfunction sortFields(fields: CrudFieldDef[]): CrudFieldDef[] {\n return [...fields].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n}\n\n/**\n * 解析选项列表\n */\nfunction resolveOptions(options: CrudFieldOptions | undefined): Array<{ label: string, value: string | number | boolean }> {\n if (!options)\n return []\n return typeof options === 'function' ? options() : options\n}\n\n/**\n * 定义 CRUD 资源\n *\n * 返回 CrudOperations 对象,包含字段过滤、列构建和 API 代理等能力。\n * 与 @h-ai/ui 的 CrudPage 组件配合使用。\n *\n * @param resource - 资源定义\n * @returns CRUD 操作对象\n *\n * @example\n * ```ts\n * const userCrud = defineCrud({\n * name: 'user',\n * label: () => m.users_title(),\n * fields: [\n * { id: 'username', label: '用户名', type: 'string', validation: { required: true } },\n * { id: 'email', label: '邮箱', type: 'email' },\n * ],\n * api: { list, get, create, update, remove },\n * })\n * ```\n */\nexport function defineCrud<T = Record<string, unknown>>(\n resource: CrudResourceDef<T>,\n): CrudOperations<T> {\n const fields = resource.fields\n\n function getListFields(): CrudFieldDef[] {\n return sortFields(fields.filter(f => f.inList !== false))\n }\n\n function getFilterFields(): CrudFieldDef[] {\n return sortFields(fields.filter(f => f.filterable === true))\n }\n\n function getCreateFields(): CrudFieldDef[] {\n return sortFields(fields.filter(f => f.inCreate !== false))\n }\n\n function getEditFields(): CrudFieldDef[] {\n return sortFields(fields.filter(f => f.inEdit !== false))\n }\n\n function getDetailFields(): CrudFieldDef[] {\n return sortFields(fields.filter(f => f.inDetail !== false))\n }\n\n function toTableColumns(): CrudTableColumn[] {\n return getListFields().map(f => ({\n key: f.id,\n label: resolveText(f.label),\n width: f.width,\n align: f.align,\n render: f.render\n ? (item: Record<string, unknown>) => f.render!(item[f.id], item)\n : undefined,\n }))\n }\n\n function getDefaultValues(): Record<string, unknown> {\n const defaults: Record<string, unknown> = {}\n for (const f of fields) {\n if (f.defaultValue !== undefined) {\n defaults[f.id] = f.defaultValue\n }\n else if (f.type === 'boolean' || f.type === 'checkbox') {\n defaults[f.id] = false\n }\n else if (f.type === 'multi-select') {\n defaults[f.id] = []\n }\n else if (f.type === 'number') {\n defaults[f.id] = undefined\n }\n else {\n defaults[f.id] = ''\n }\n }\n return defaults\n }\n\n return {\n resource,\n getListFields,\n getFilterFields,\n getCreateFields,\n getEditFields,\n getDetailFields,\n toTableColumns,\n getDefaultValues,\n api: resource.api,\n }\n}\n\n// re-export 解析工具,供 UI 层使用\nexport { resolveOptions, resolveText }\n","/**\n * @h-ai/kit — transport 配置\n *\n * 为 `config/_kit.yml` 提供统一的 transport 配置结构、默认值与校验逻辑,\n * 方便应用层用一份配置同时驱动 `hooks.server.ts` 与 `kit.client.create()`。\n * @module kit-config\n */\n\nimport { z } from 'zod'\n\nconst AbsolutePathSchema = z.custom<`/${string}`>(\n value => typeof value === 'string' && value.startsWith('/'),\n 'path must start with /',\n)\n\n/** `config/_kit.yml` 中的 transport 配置。 */\nexport interface KitTransportConfig {\n readonly keyExchangePath: `/${string}`\n readonly excludePaths: readonly `/${string}`[]\n readonly requireEncryption: boolean\n readonly encryptResponse: boolean\n readonly maxClients: number\n}\n\n/** `config/_kit.yml` 的顶层配置结构。 */\nexport interface KitConfig {\n readonly transport: false | KitTransportConfig\n}\n\n/** `config/_kit.yml` 中的 transport 输入类型。 */\nexport type KitTransportConfigInput = Partial<{\n readonly keyExchangePath: `/${string}`\n readonly excludePaths: readonly `/${string}`[]\n readonly requireEncryption: boolean\n readonly encryptResponse: boolean\n readonly maxClients: number\n}>\n\n/** `config/_kit.yml` 的顶层输入类型。 */\nexport interface KitConfigInput {\n readonly transport?: false | KitTransportConfigInput\n}\n\nconst KitTransportConfigInputSchema = z.union([\n z.literal(false),\n z.object({\n keyExchangePath: AbsolutePathSchema.default('/api/_hai/key-exchange'),\n excludePaths: z.array(AbsolutePathSchema).default([]),\n requireEncryption: z.boolean().default(true),\n encryptResponse: z.boolean().default(true),\n maxClients: z.number().int().positive().default(10000),\n }),\n])\n\n/** `config/_kit.yml` 对应的配置 Schema。 */\nexport const KitConfigSchema = z.object({\n transport: KitTransportConfigInputSchema.optional(),\n}).transform(({ transport }) => ({\n transport: resolveKitTransportConfig(transport ?? false),\n}))\n\n/**\n * 解析 transport 配置并补齐默认值。\n *\n * @param input - `config/_kit.yml` 中的 transport 配置\n * @returns 完整 transport 配置;`false` 表示关闭\n */\nexport function resolveKitTransportConfig(\n input: false | KitTransportConfigInput = false,\n): false | KitTransportConfig {\n return KitTransportConfigInputSchema.parse(input)\n}\n\n/**\n * 解析 `config/_kit.yml` 顶层配置并补齐默认值。\n *\n * 适合同一份配置同时供 SvelteKit 服务端 hook 与浏览器端 client 读取。\n */\nexport function resolveKitConfig(input: KitConfigInput = {}): KitConfig {\n return KitConfigSchema.parse(input)\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { core
|
|
1
|
+
import { core } from '@h-ai/core';
|
|
2
|
+
import { TRANSPORT_PROTOCOL } from '@h-ai/crypto';
|
|
2
3
|
|
|
3
4
|
// src/kit-i18n.ts
|
|
4
5
|
|
|
@@ -30,6 +31,21 @@ var en_US_default = {
|
|
|
30
31
|
kit_resourceNotFound: "Resource not found",
|
|
31
32
|
kit_internalError: "Internal server error",
|
|
32
33
|
kit_validationFailed: "Validation failed",
|
|
34
|
+
kit_validationRequired: "This field is required",
|
|
35
|
+
kit_validationInvalid: "Invalid input format",
|
|
36
|
+
kit_validationInvalidType: "Invalid field type",
|
|
37
|
+
kit_validationStringMin: "Enter at least {min} characters",
|
|
38
|
+
kit_validationStringMax: "Enter at most {max} characters",
|
|
39
|
+
kit_validationNumberMin: "Value must be at least {min}",
|
|
40
|
+
kit_validationNumberMax: "Value must be at most {max}",
|
|
41
|
+
kit_validationArrayMin: "Select at least {min} item(s)",
|
|
42
|
+
kit_validationArrayMax: "Select at most {max} item(s)",
|
|
43
|
+
kit_validationTooSmall: "Value must be at least {min}",
|
|
44
|
+
kit_validationTooBig: "Value must be at most {max}",
|
|
45
|
+
kit_validationEmail: "Enter a valid email address",
|
|
46
|
+
kit_validationUrl: "Enter a valid URL",
|
|
47
|
+
kit_validationUuid: "Enter a valid UUID",
|
|
48
|
+
kit_validationEnum: "Select a valid option",
|
|
33
49
|
kit_noGuardsPassed: "No guards passed",
|
|
34
50
|
kit_requiredRoles: "Required roles: {roles}",
|
|
35
51
|
kit_requiredPermissions: "Required permissions: {permissions}",
|
|
@@ -64,6 +80,21 @@ var zh_CN_default = {
|
|
|
64
80
|
kit_resourceNotFound: "\u8D44\u6E90\u672A\u627E\u5230",
|
|
65
81
|
kit_internalError: "\u5185\u90E8\u670D\u52A1\u5668\u9519\u8BEF",
|
|
66
82
|
kit_validationFailed: "\u6570\u636E\u9A8C\u8BC1\u5931\u8D25",
|
|
83
|
+
kit_validationRequired: "\u6B64\u5B57\u6BB5\u5FC5\u586B",
|
|
84
|
+
kit_validationInvalid: "\u8F93\u5165\u683C\u5F0F\u65E0\u6548",
|
|
85
|
+
kit_validationInvalidType: "\u5B57\u6BB5\u7C7B\u578B\u4E0D\u6B63\u786E",
|
|
86
|
+
kit_validationStringMin: "\u81F3\u5C11\u8F93\u5165 {min} \u4E2A\u5B57\u7B26",
|
|
87
|
+
kit_validationStringMax: "\u6700\u591A\u8F93\u5165 {max} \u4E2A\u5B57\u7B26",
|
|
88
|
+
kit_validationNumberMin: "\u6570\u503C\u4E0D\u80FD\u5C0F\u4E8E {min}",
|
|
89
|
+
kit_validationNumberMax: "\u6570\u503C\u4E0D\u80FD\u5927\u4E8E {max}",
|
|
90
|
+
kit_validationArrayMin: "\u81F3\u5C11\u9009\u62E9 {min} \u9879",
|
|
91
|
+
kit_validationArrayMax: "\u6700\u591A\u9009\u62E9 {max} \u9879",
|
|
92
|
+
kit_validationTooSmall: "\u4E0D\u80FD\u5C0F\u4E8E {min}",
|
|
93
|
+
kit_validationTooBig: "\u4E0D\u80FD\u5927\u4E8E {max}",
|
|
94
|
+
kit_validationEmail: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740",
|
|
95
|
+
kit_validationUrl: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u7F51\u5740",
|
|
96
|
+
kit_validationUuid: "\u8BF7\u8F93\u5165\u6709\u6548\u7684 UUID",
|
|
97
|
+
kit_validationEnum: "\u8BF7\u9009\u62E9\u6709\u6548\u9009\u9879",
|
|
67
98
|
kit_noGuardsPassed: "\u6CA1\u6709\u5B88\u536B\u901A\u8FC7",
|
|
68
99
|
kit_requiredRoles: "\u9700\u8981\u89D2\u8272\uFF1A{roles}",
|
|
69
100
|
kit_requiredPermissions: "\u9700\u8981\u6743\u9650\uFF1A{permissions}",
|
|
@@ -75,97 +106,41 @@ var kitM = core.i18n.createMessageGetter({ "zh-CN": zh_CN_default, "en-US": en_U
|
|
|
75
106
|
function setAllModulesLocale(locale) {
|
|
76
107
|
core.i18n.setGlobalLocale(locale);
|
|
77
108
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
clientKeys.set(clientId, clientPublicKey);
|
|
100
|
-
return clientId;
|
|
101
|
-
},
|
|
102
|
-
getClientPublicKey(clientId) {
|
|
103
|
-
return clientKeys.get(clientId);
|
|
104
|
-
},
|
|
105
|
-
encryptResponse(clientId, data) {
|
|
106
|
-
const clientPublicKey = clientKeys.get(clientId);
|
|
107
|
-
if (!clientPublicKey) {
|
|
108
|
-
throw new Error(kitM("kit_transportClientKeyNotFound"));
|
|
109
|
-
}
|
|
110
|
-
const symmetricKey = cryptoService.symmetric.generateKey();
|
|
111
|
-
const encResult = cryptoService.symmetric.encryptWithIV(data, symmetricKey);
|
|
112
|
-
if (!encResult.success || !encResult.data) {
|
|
113
|
-
throw new Error(kitM("kit_transportEncryptFailed"));
|
|
114
|
-
}
|
|
115
|
-
const keyEncResult = cryptoService.asymmetric.encrypt(symmetricKey, clientPublicKey);
|
|
116
|
-
if (!keyEncResult.success || !keyEncResult.data) {
|
|
117
|
-
throw new Error(kitM("kit_transportEncryptKeyFailed"));
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
encryptedKey: keyEncResult.data,
|
|
121
|
-
ciphertext: encResult.data.ciphertext,
|
|
122
|
-
iv: encResult.data.iv
|
|
123
|
-
};
|
|
124
|
-
},
|
|
125
|
-
decryptRequest(payload) {
|
|
126
|
-
const keyDecResult = cryptoService.asymmetric.decrypt(payload.encryptedKey, serverKeyPair.privateKey);
|
|
127
|
-
if (!keyDecResult.success || !keyDecResult.data) {
|
|
128
|
-
throw new Error(kitM("kit_transportDecryptKeyFailed"));
|
|
129
|
-
}
|
|
130
|
-
const decResult = cryptoService.symmetric.decryptWithIV(payload.ciphertext, keyDecResult.data, payload.iv);
|
|
131
|
-
if (!decResult.success || typeof decResult.data !== "string") {
|
|
132
|
-
throw new Error(kitM("kit_transportDecryptFailed"));
|
|
133
|
-
}
|
|
134
|
-
return decResult.data;
|
|
135
|
-
}
|
|
109
|
+
var DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH = `/api${TRANSPORT_PROTOCOL.DEFAULT_KEY_EXCHANGE_PATH}`;
|
|
110
|
+
var API_PREFIX = "/api/";
|
|
111
|
+
var ROOT_DATA_PATH = "/__data.json";
|
|
112
|
+
var DATA_SUFFIX = "/__data.json";
|
|
113
|
+
function normalizePath(pathname) {
|
|
114
|
+
return pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
115
|
+
}
|
|
116
|
+
function isSvelteKitDataPath(pathname) {
|
|
117
|
+
return pathname === ROOT_DATA_PATH || pathname.endsWith(DATA_SUFFIX);
|
|
118
|
+
}
|
|
119
|
+
function shouldHandleTransportPath(pathname, keyExchangePath = DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH) {
|
|
120
|
+
return pathname === keyExchangePath || pathname.startsWith(API_PREFIX) || isSvelteKitDataPath(pathname);
|
|
121
|
+
}
|
|
122
|
+
function shouldExcludeTransportPath(pathname, excludePaths, keyExchangePath) {
|
|
123
|
+
if (pathname === keyExchangePath)
|
|
124
|
+
return true;
|
|
125
|
+
return excludePaths.some((excludePath) => {
|
|
126
|
+
const normalizedPath = normalizePath(excludePath);
|
|
127
|
+
return pathname === normalizedPath || pathname.startsWith(`${normalizedPath}/`);
|
|
136
128
|
});
|
|
137
129
|
}
|
|
138
|
-
function
|
|
139
|
-
return
|
|
140
|
-
try {
|
|
141
|
-
const body = await request.json();
|
|
142
|
-
if (!body.clientPublicKey || typeof body.clientPublicKey !== "string") {
|
|
143
|
-
return new Response(
|
|
144
|
-
JSON.stringify({ error: kitM("kit_transportInvalidPayload") }),
|
|
145
|
-
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
const clientId = manager.registerClientKey(body.clientPublicKey);
|
|
149
|
-
const serverPublicKey = manager.getServerPublicKey();
|
|
150
|
-
return new Response(
|
|
151
|
-
JSON.stringify({ serverPublicKey, clientId }),
|
|
152
|
-
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
153
|
-
);
|
|
154
|
-
} catch {
|
|
155
|
-
return new Response(
|
|
156
|
-
JSON.stringify({ error: kitM("kit_transportKeyExchangeFailed") }),
|
|
157
|
-
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
130
|
+
function resolveTransportPath(urlOrPath, origin = "http://localhost") {
|
|
131
|
+
return new URL(urlOrPath, origin).pathname;
|
|
161
132
|
}
|
|
162
|
-
function
|
|
163
|
-
|
|
133
|
+
function shouldUseTransportForUrl(url, options = {}) {
|
|
134
|
+
const baseOrigin = options.origin ?? "http://localhost";
|
|
135
|
+
const requestUrl = url instanceof URL ? url : new URL(url, baseOrigin);
|
|
136
|
+
if (options.origin && requestUrl.origin !== options.origin)
|
|
137
|
+
return false;
|
|
138
|
+
const keyExchangePath = options.keyExchangePath ?? DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH;
|
|
139
|
+
if (!shouldHandleTransportPath(requestUrl.pathname, keyExchangePath))
|
|
164
140
|
return false;
|
|
165
|
-
|
|
166
|
-
return typeof p.encryptedKey === "string" && typeof p.ciphertext === "string" && typeof p.iv === "string" && p.encryptedKey.length > 0 && p.ciphertext.length > 0 && p.iv.length > 0;
|
|
141
|
+
return !shouldExcludeTransportPath(requestUrl.pathname, options.excludePaths ?? [], keyExchangePath);
|
|
167
142
|
}
|
|
168
143
|
|
|
169
|
-
export {
|
|
170
|
-
//# sourceMappingURL=chunk-
|
|
171
|
-
//# sourceMappingURL=chunk-
|
|
144
|
+
export { DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, kitM, resolveTransportPath, setAllModulesLocale, shouldExcludeTransportPath, shouldHandleTransportPath, shouldUseTransportForUrl };
|
|
145
|
+
//# sourceMappingURL=chunk-MI65L22O.js.map
|
|
146
|
+
//# sourceMappingURL=chunk-MI65L22O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../messages/en-US.json","../messages/zh-CN.json","../src/kit-i18n.ts","../src/modules/crypto/kit-transport-paths.ts"],"names":[],"mappings":"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","file":"chunk-MI65L22O.js","sourcesContent":["{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"kit_unauthorized\": \"Unauthorized\",\n \"kit_forbidden\": \"Forbidden: missing {permission} permission\",\n \"kit_authNotConfigured\": \"kit.auth is not configured: pass auth.operations in kit.createHandle()\",\n \"kit_csrfTokenFailed\": \"Failed to generate CSRF token\",\n \"kit_csrfVerifyFailed\": \"CSRF token verification failed\",\n \"kit_csrfValidationFailed\": \"Invalid CSRF token\",\n \"kit_rateLimitExceeded\": \"Too many requests, please try again later\",\n \"kit_unsupportedContentType\": \"Unsupported content type\",\n \"kit_parseBodyFailed\": \"Failed to parse request body\",\n \"kit_encryptFailed\": \"Encryption failed\",\n \"kit_transportKeyGenerationFailed\": \"Transport encryption key generation failed\",\n \"kit_transportClientKeyNotFound\": \"Client key not found\",\n \"kit_transportEncryptFailed\": \"Transport encryption failed\",\n \"kit_transportEncryptKeyFailed\": \"Symmetric key encryption failed\",\n \"kit_transportDecryptKeyFailed\": \"Symmetric key decryption failed\",\n \"kit_transportDecryptFailed\": \"Transport decryption failed\",\n \"kit_transportInvalidPayload\": \"Invalid encrypted payload\",\n \"kit_transportKeyExchangeFailed\": \"Key exchange failed\",\n \"kit_transportClientIdRequired\": \"Missing X-Client-Id header, key exchange is required for transport encryption\",\n \"kit_signFailed\": \"Request signing failed\",\n \"kit_authRequired\": \"Authentication required\",\n \"kit_accessDenied\": \"Access denied\",\n \"kit_resourceNotFound\": \"Resource not found\",\n \"kit_internalError\": \"Internal server error\",\n \"kit_validationFailed\": \"Validation failed\",\n \"kit_validationRequired\": \"This field is required\",\n \"kit_validationInvalid\": \"Invalid input format\",\n \"kit_validationInvalidType\": \"Invalid field type\",\n \"kit_validationStringMin\": \"Enter at least {min} characters\",\n \"kit_validationStringMax\": \"Enter at most {max} characters\",\n \"kit_validationNumberMin\": \"Value must be at least {min}\",\n \"kit_validationNumberMax\": \"Value must be at most {max}\",\n \"kit_validationArrayMin\": \"Select at least {min} item(s)\",\n \"kit_validationArrayMax\": \"Select at most {max} item(s)\",\n \"kit_validationTooSmall\": \"Value must be at least {min}\",\n \"kit_validationTooBig\": \"Value must be at most {max}\",\n \"kit_validationEmail\": \"Enter a valid email address\",\n \"kit_validationUrl\": \"Enter a valid URL\",\n \"kit_validationUuid\": \"Enter a valid UUID\",\n \"kit_validationEnum\": \"Select a valid option\",\n \"kit_noGuardsPassed\": \"No guards passed\",\n \"kit_requiredRoles\": \"Required roles: {roles}\",\n \"kit_requiredPermissions\": \"Required permissions: {permissions}\",\n \"kit_idRequired\": \"ID is required\"\n}\n","{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"kit_unauthorized\": \"未授权\",\n \"kit_forbidden\": \"权限不足:缺少 {permission} 权限\",\n \"kit_authNotConfigured\": \"kit.auth 未配置:请在 kit.createHandle() 中传入 auth.operations\",\n \"kit_csrfTokenFailed\": \"生成 CSRF Token 失败\",\n \"kit_csrfVerifyFailed\": \"CSRF Token 验证失败\",\n \"kit_csrfValidationFailed\": \"CSRF Token 无效\",\n \"kit_rateLimitExceeded\": \"请求过于频繁,请稍后再试\",\n \"kit_unsupportedContentType\": \"不支持的内容类型\",\n \"kit_parseBodyFailed\": \"请求体解析失败\",\n \"kit_encryptFailed\": \"加密失败\",\n \"kit_transportKeyGenerationFailed\": \"传输加密密钥对生成失败\",\n \"kit_transportClientKeyNotFound\": \"客户端密钥未找到\",\n \"kit_transportEncryptFailed\": \"传输加密失败\",\n \"kit_transportEncryptKeyFailed\": \"对称密钥加密失败\",\n \"kit_transportDecryptKeyFailed\": \"对称密钥解密失败\",\n \"kit_transportDecryptFailed\": \"传输解密失败\",\n \"kit_transportInvalidPayload\": \"无效的加密载荷\",\n \"kit_transportKeyExchangeFailed\": \"密钥交换失败\",\n \"kit_transportClientIdRequired\": \"缺少 X-Client-Id 请求头,传输加密必须先完成密钥交换\",\n \"kit_signFailed\": \"请求签名失败\",\n \"kit_authRequired\": \"需要身份认证\",\n \"kit_accessDenied\": \"拒绝访问\",\n \"kit_resourceNotFound\": \"资源未找到\",\n \"kit_internalError\": \"内部服务器错误\",\n \"kit_validationFailed\": \"数据验证失败\",\n \"kit_validationRequired\": \"此字段必填\",\n \"kit_validationInvalid\": \"输入格式无效\",\n \"kit_validationInvalidType\": \"字段类型不正确\",\n \"kit_validationStringMin\": \"至少输入 {min} 个字符\",\n \"kit_validationStringMax\": \"最多输入 {max} 个字符\",\n \"kit_validationNumberMin\": \"数值不能小于 {min}\",\n \"kit_validationNumberMax\": \"数值不能大于 {max}\",\n \"kit_validationArrayMin\": \"至少选择 {min} 项\",\n \"kit_validationArrayMax\": \"最多选择 {max} 项\",\n \"kit_validationTooSmall\": \"不能小于 {min}\",\n \"kit_validationTooBig\": \"不能大于 {max}\",\n \"kit_validationEmail\": \"请输入有效的邮箱地址\",\n \"kit_validationUrl\": \"请输入有效的网址\",\n \"kit_validationUuid\": \"请输入有效的 UUID\",\n \"kit_validationEnum\": \"请选择有效选项\",\n \"kit_noGuardsPassed\": \"没有守卫通过\",\n \"kit_requiredRoles\": \"需要角色:{roles}\",\n \"kit_requiredPermissions\": \"需要权限:{permissions}\",\n \"kit_idRequired\": \"ID 不能为空\"\n}\n","/**\n * @h-ai/kit — i18n 工具\n *\n * Kit 模块的国际化消息工具,从 @h-ai/core 调用 i18n API。\n * @module kit-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\ntype KitMessageKey = keyof typeof messagesZhCN\n\n/** 获取 Kit 模块的 i18n 消息 */\nexport const kitM\n = core.i18n.createMessageGetter<KitMessageKey>({ 'zh-CN': messagesZhCN, 'en-US': messagesEnUS })\n\n/**\n * 统一设置所有 hai 模块的默认语言\n *\n * 通过 @h-ai/core 的集中式 locale 管理器,一次调用即可同步所有模块。\n * 各模块的 createMessageGetter 会读取全局 locale。\n *\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * // 在 i18n handle 中\n * const locale = event.cookies.get('PARAGLIDE_LOCALE') ?? 'zh-CN'\n * kit.i18n.setLocale(locale)\n * ```\n */\nexport function setAllModulesLocale(locale: string): void {\n core.i18n.setGlobalLocale(locale)\n}\n","/**\n * @h-ai/kit — 传输加密路径匹配工具\n *\n * 统一定义哪些同源请求应纳入 transport 保护范围,供服务端 middleware\n * 与浏览器端 fetch 包装共同复用,避免两端判断漂移。\n * @module kit-transport-paths\n */\n\nimport { TRANSPORT_PROTOCOL } from '@h-ai/crypto'\n\nexport const DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH = `/api${TRANSPORT_PROTOCOL.DEFAULT_KEY_EXCHANGE_PATH}`\n\nconst API_PREFIX = '/api/'\nconst ROOT_DATA_PATH = '/__data.json'\nconst DATA_SUFFIX = '/__data.json'\n\nfunction normalizePath(pathname: string): string {\n return pathname.startsWith('/') ? pathname : `/${pathname}`\n}\n\n/** 判断路径是否为 SvelteKit 的页面数据端点。 */\nexport function isSvelteKitDataPath(pathname: string): boolean {\n return pathname === ROOT_DATA_PATH || pathname.endsWith(DATA_SUFFIX)\n}\n\n/** 判断路径是否属于 transport 保护范围。 */\nexport function shouldHandleTransportPath(\n pathname: string,\n keyExchangePath: string = DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH,\n): boolean {\n return pathname === keyExchangePath || pathname.startsWith(API_PREFIX) || isSvelteKitDataPath(pathname)\n}\n\n/** 判断路径是否应排除 transport。 */\nexport function shouldExcludeTransportPath(\n pathname: string,\n excludePaths: string[],\n keyExchangePath: string,\n): boolean {\n if (pathname === keyExchangePath)\n return true\n\n return excludePaths.some((excludePath) => {\n const normalizedPath = normalizePath(excludePath)\n return pathname === normalizedPath || pathname.startsWith(`${normalizedPath}/`)\n })\n}\n\n/** 从相对/绝对 URL 中提取 pathname。 */\nexport function resolveTransportPath(urlOrPath: string, origin = 'http://localhost'): string {\n return new URL(urlOrPath, origin).pathname\n}\n\n/**\n * 判断浏览器侧请求是否应走 transport。\n *\n * - 仅处理同源请求\n * - 仅保护 `/api/*` 与 SvelteKit `__data.json`\n * - 命中 `excludePaths` 时透传明文\n */\nexport function shouldUseTransportForUrl(\n url: string | URL,\n options: {\n origin?: string\n keyExchangePath?: string\n excludePaths?: string[]\n } = {},\n): boolean {\n const baseOrigin = options.origin ?? 'http://localhost'\n const requestUrl = url instanceof URL ? url : new URL(url, baseOrigin)\n\n if (options.origin && requestUrl.origin !== options.origin)\n return false\n\n const keyExchangePath = options.keyExchangePath ?? DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH\n if (!shouldHandleTransportPath(requestUrl.pathname, keyExchangePath))\n return false\n\n return !shouldExcludeTransportPath(requestUrl.pathname, options.excludePaths ?? [], keyExchangePath)\n}\n"]}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { C as ClientTransportConfig, K as KitClient, e as KitClientConfig, c as createKitClient } from '../index-
|
|
1
|
+
export { C as ClientTransportConfig, K as KitClient, e as KitClientConfig, c as createKitClient, l as createSvelteKitNavAdapter, i as installBrowserTransport, a as installBrowserTransportFetch } from '../index-M00CsNUR.js';
|
|
2
|
+
import '@h-ai/crypto';
|
|
2
3
|
import '@h-ai/core';
|
|
3
4
|
import '@h-ai/iam';
|
|
4
|
-
import '
|
|
5
|
-
import '../kit-crypto-types-C-gEvx04.js';
|
|
5
|
+
import 'zod';
|
package/dist/client/index.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
-
export { createKitClient } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { createKitClient, installBrowserTransport, installBrowserTransportFetch } from '../chunk-EMSCTFJJ.js';
|
|
2
|
+
import '../chunk-MI65L22O.js';
|
|
3
|
+
import { invalidateAll, goto } from '$app/navigation';
|
|
4
|
+
import { page } from '$app/state';
|
|
5
|
+
|
|
6
|
+
function createSvelteKitNavAdapter() {
|
|
7
|
+
return {
|
|
8
|
+
get pathname() {
|
|
9
|
+
return page.url.pathname;
|
|
10
|
+
},
|
|
11
|
+
async navigate(url) {
|
|
12
|
+
await goto(url, { invalidateAll: true });
|
|
13
|
+
},
|
|
14
|
+
async refresh() {
|
|
15
|
+
await invalidateAll();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { createSvelteKitNavAdapter };
|
|
3
21
|
//# sourceMappingURL=index.js.map
|
|
4
22
|
//# sourceMappingURL=index.js.map
|