@h-ai/kit 0.1.0-alpha.67 → 0.1.0-alpha.68
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 +39 -13
- package/README.ja.md +39 -13
- package/README.md +39 -13
- package/dist/adapter/index.d.ts +17 -2
- package/dist/adapter/index.js.map +1 -1
- package/dist/browser.d.ts +12 -6
- package/dist/browser.js +6 -7
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-DVW7CQ3H.js → chunk-2PWGJCWX.js} +3 -3
- package/dist/chunk-2PWGJCWX.js.map +1 -0
- package/dist/{chunk-VOTDAVCB.js → chunk-2YYRP7HH.js} +17 -33
- package/dist/chunk-2YYRP7HH.js.map +1 -0
- package/dist/{chunk-MLSP67JF.js → chunk-333FGMGQ.js} +59 -111
- package/dist/chunk-333FGMGQ.js.map +1 -0
- package/dist/chunk-OVRZIDCD.js +175 -0
- package/dist/chunk-OVRZIDCD.js.map +1 -0
- package/dist/{chunk-L7BTMAKX.js → chunk-P6XH7G4N.js} +3 -6
- package/dist/chunk-P6XH7G4N.js.map +1 -0
- package/dist/{chunk-NVYIUBU7.js → chunk-XC7I2L3G.js} +2 -2
- package/dist/chunk-XC7I2L3G.js.map +1 -0
- package/dist/client/index.d.ts +47 -1
- package/dist/client/index.js +3 -3
- package/dist/client/index.js.map +1 -1
- package/dist/index.d.ts +217 -347
- package/dist/index.js +138 -83
- package/dist/index.js.map +1 -1
- package/dist/kit-a2a-types-CwT1AFQu.d.ts +136 -0
- package/dist/{index-B7s-0mZl.d.ts → kit-client-auth-HnZIYUia.d.ts} +220 -101
- package/dist/kit-crypto-types-Dd24aJ4m.d.ts +194 -0
- package/dist/{kit-i18n-D9Qqk-1k.d.ts → kit-i18n-DEzmzE5T.d.ts} +142 -29
- package/dist/{kit-types-ByWdbCeO.d.ts → kit-types-CnzhN-AS.d.ts} +240 -162
- package/dist/modules/a2a/index.d.ts +13 -111
- package/dist/modules/a2a/index.js +2 -2
- package/dist/modules/crypto/index.d.ts +28 -195
- package/dist/modules/crypto/index.js +3 -3
- package/dist/modules/crypto/index.js.map +1 -1
- package/package.json +10 -10
- package/dist/chunk-DVW7CQ3H.js.map +0 -1
- package/dist/chunk-L7BTMAKX.js.map +0 -1
- package/dist/chunk-MLSP67JF.js.map +0 -1
- package/dist/chunk-NVYIUBU7.js.map +0 -1
- package/dist/chunk-QHYNL24G.js +0 -117
- package/dist/chunk-QHYNL24G.js.map +0 -1
- package/dist/chunk-VOTDAVCB.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { defineCrud } from './chunk-
|
|
2
|
-
export { KitConfigSchema, defineCrud, resolveKitConfig, resolveKitTransportConfig, resolveOptions, resolveText } from './chunk-
|
|
3
|
-
import {
|
|
4
|
-
import { resolveA2AConfig, handleA2ARequest } from './chunk-
|
|
5
|
-
import { transportEncryptionMiddleware } from './chunk-
|
|
6
|
-
import './chunk-
|
|
7
|
-
import { kitM, setAllModulesLocale } from './chunk-
|
|
1
|
+
import { defineCrud } from './chunk-P6XH7G4N.js';
|
|
2
|
+
export { KitConfigSchema, defineCrud, resolveKitConfig, resolveKitTransportConfig, resolveOptions, resolveText } from './chunk-P6XH7G4N.js';
|
|
3
|
+
import { clearBrowserToken, setBrowserToken, logout, registerAndLogin, loginWithApiKey, loginWithLdap, loginWithOtp, login, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient, configureAuth, getAccessToken } from './chunk-2YYRP7HH.js';
|
|
4
|
+
import { resolveA2AConfig, handleA2ARequest } from './chunk-2PWGJCWX.js';
|
|
5
|
+
import { transportEncryptionMiddleware } from './chunk-333FGMGQ.js';
|
|
6
|
+
import './chunk-XC7I2L3G.js';
|
|
7
|
+
import { kitM, setAllModulesLocale } from './chunk-OVRZIDCD.js';
|
|
8
8
|
import { core } from '@h-ai/core';
|
|
9
|
+
import { createHash } from 'crypto';
|
|
10
|
+
import { isIP } from 'net';
|
|
11
|
+
import process2 from 'process';
|
|
9
12
|
import { IamConfirmPasswordResetInputSchema, IamRequestPasswordResetInputSchema, IamSendOtpInputSchema, IamRegisterInputSchema, IamOtpLoginInputSchema, IamLdapLoginInputSchema, IamLoginInputSchema } from '@h-ai/api-contract';
|
|
10
13
|
import { z } from 'zod';
|
|
11
14
|
|
|
@@ -116,67 +119,93 @@ function loggingMiddleware(config = {}) {
|
|
|
116
119
|
function redactObject(obj, fields) {
|
|
117
120
|
return core.sanitize.sanitizeSensitiveFields(obj, { matcher: fields });
|
|
118
121
|
}
|
|
122
|
+
function permitsMemory() {
|
|
123
|
+
return !core.module.requiresSharedState(process2.env);
|
|
124
|
+
}
|
|
125
|
+
function createCacheRateLimitStore(backend, options) {
|
|
126
|
+
if (!options.namespace || options.namespace.length > 128)
|
|
127
|
+
throw new TypeError("A rate limit namespace of 1-128 characters is required");
|
|
128
|
+
return {
|
|
129
|
+
scope: "shared",
|
|
130
|
+
async increment(key, windowMs) {
|
|
131
|
+
if (!backend.config || backend.config.type !== "redis" && !(backend.config.type === "memory" && options.allowMemory && permitsMemory()))
|
|
132
|
+
throw new Error("Rate limiting requires a shared Redis cache");
|
|
133
|
+
const digest = createHash("sha256").update(JSON.stringify([options.namespace, windowMs, key])).digest("hex");
|
|
134
|
+
const cacheKey = `hai:kit:ratelimit:{${digest}}`;
|
|
135
|
+
for (let attempt = 0; attempt < 64; attempt++) {
|
|
136
|
+
const read = await backend.kv.get(cacheKey);
|
|
137
|
+
if (!read.success)
|
|
138
|
+
throw new Error("Rate limit cache read failed");
|
|
139
|
+
const old = read.data ?? void 0;
|
|
140
|
+
if (old && (!Number.isSafeInteger(old.count) || old.count <= 0 || old.count >= Number.MAX_SAFE_INTEGER || !Number.isSafeInteger(old.resetAt)))
|
|
141
|
+
throw new Error("Invalid rate limit state");
|
|
142
|
+
const next = old ? { count: old.count + 1, resetAt: old.resetAt } : { count: 1, resetAt: Date.now() + windowMs };
|
|
143
|
+
const written = await backend.kv.compareAndSwap(cacheKey, old, next, old ? { keepTtl: true } : { px: windowMs });
|
|
144
|
+
if (!written.success)
|
|
145
|
+
throw new Error("Rate limit cache commit failed");
|
|
146
|
+
if (written.data)
|
|
147
|
+
return next;
|
|
148
|
+
}
|
|
149
|
+
throw new Error("Rate limit contention limit exceeded");
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function createMemoryRateLimitStore() {
|
|
154
|
+
if (!permitsMemory())
|
|
155
|
+
throw new Error("Memory rate limiting is restricted to development and tests");
|
|
156
|
+
return new MemoryRateLimitStore();
|
|
157
|
+
}
|
|
158
|
+
function clientKey(address) {
|
|
159
|
+
if (isIP(address) !== 6)
|
|
160
|
+
return address;
|
|
161
|
+
const normalized = new URL(`http://[${address}]`).hostname.slice(1, -1);
|
|
162
|
+
const mapped = /^::ffff:([0-9a-f]+):([0-9a-f]+)$/.exec(normalized);
|
|
163
|
+
if (!mapped)
|
|
164
|
+
return normalized;
|
|
165
|
+
const value = Number.parseInt(mapped[1], 16) * 65536 + Number.parseInt(mapped[2], 16);
|
|
166
|
+
return [value >>> 24, value >>> 16 & 255, value >>> 8 & 255, value & 255].join(".");
|
|
167
|
+
}
|
|
119
168
|
var MemoryRateLimitStore = class {
|
|
169
|
+
scope = "memory";
|
|
120
170
|
store = /* @__PURE__ */ new Map();
|
|
121
|
-
|
|
171
|
+
cleanupCursor;
|
|
122
172
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* @param intervalMs - 清理间隔(毫秒)
|
|
173
|
+
* 有界惰性回收;保留迭代位置,避免每次从头扫描使尾部过期项永久滞留。
|
|
174
|
+
* @param now - 当前请求时间,等于 resetAt 时窗口已经失效。
|
|
126
175
|
*/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.store.delete(key);
|
|
135
|
-
}
|
|
176
|
+
prune(now) {
|
|
177
|
+
this.cleanupCursor ??= this.store.entries();
|
|
178
|
+
for (let index = 0; index < 64; index++) {
|
|
179
|
+
const next = this.cleanupCursor.next();
|
|
180
|
+
if (next.done) {
|
|
181
|
+
this.cleanupCursor = void 0;
|
|
182
|
+
break;
|
|
136
183
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
184
|
+
const [key, entry] = next.value;
|
|
185
|
+
if (entry.resetAt <= now)
|
|
186
|
+
this.store.delete(key);
|
|
140
187
|
}
|
|
141
188
|
}
|
|
142
|
-
/**
|
|
143
|
-
* 停止定期清理并释放定时器资源
|
|
144
|
-
*
|
|
145
|
-
* 调用后可再次 startCleanup 重启。进程退出 / 热重载 / 测试拆装场景必须调用以
|
|
146
|
-
* 避免资源泄漏。
|
|
147
|
-
*/
|
|
148
|
-
stopCleanup() {
|
|
149
|
-
if (this.cleanupTimer) {
|
|
150
|
-
clearInterval(this.cleanupTimer);
|
|
151
|
-
this.cleanupTimer = null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
get(key) {
|
|
155
|
-
return this.store.get(key);
|
|
156
|
-
}
|
|
157
|
-
set(key, entry) {
|
|
158
|
-
this.store.set(key, entry);
|
|
159
|
-
}
|
|
160
|
-
delete(key) {
|
|
161
|
-
this.store.delete(key);
|
|
162
|
-
}
|
|
163
189
|
/**
|
|
164
190
|
* 原子自增(内存实现为同步操作,天然原子)
|
|
165
191
|
*
|
|
166
192
|
* @param key - 限流键
|
|
167
193
|
* @param windowMs - 窗口时长
|
|
168
|
-
* @returns
|
|
194
|
+
* @returns 自增后的快照,修改返回对象不会改变已保存的计数。
|
|
195
|
+
* @throws 新 key 超出容量或期限非法;中间件将其转换为 503。
|
|
196
|
+
* @example store.increment('client', 60_000)
|
|
169
197
|
*/
|
|
170
198
|
increment(key, windowMs) {
|
|
171
199
|
const now = Date.now();
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
200
|
+
if (!Number.isSafeInteger(windowMs) || windowMs <= 0 || windowMs > 2147483647)
|
|
201
|
+
throw new TypeError(kitM("kit_rateLimitWindowInvalid"));
|
|
202
|
+
this.prune(now);
|
|
203
|
+
const current = this.store.get(key);
|
|
204
|
+
if (!current && this.store.size >= 1e4)
|
|
205
|
+
throw new Error(kitM("kit_rateLimitMemoryCapacity"));
|
|
206
|
+
const entry = !current || current.resetAt <= now ? { count: 1, resetAt: now + windowMs } : { count: current.count + 1, resetAt: current.resetAt };
|
|
178
207
|
this.store.set(key, entry);
|
|
179
|
-
return entry;
|
|
208
|
+
return { ...entry };
|
|
180
209
|
}
|
|
181
210
|
};
|
|
182
211
|
function rateLimitMiddleware(config) {
|
|
@@ -185,38 +214,36 @@ function rateLimitMiddleware(config) {
|
|
|
185
214
|
maxRequests,
|
|
186
215
|
keyGenerator = (event) => {
|
|
187
216
|
try {
|
|
188
|
-
return event.getClientAddress?.() ?? "unknown";
|
|
217
|
+
return clientKey(event.getClientAddress?.() ?? "unknown");
|
|
189
218
|
} catch {
|
|
190
219
|
return "unknown";
|
|
191
220
|
}
|
|
192
221
|
},
|
|
193
222
|
onLimitReached
|
|
194
223
|
} = config;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
224
|
+
if (!Number.isSafeInteger(windowMs) || windowMs <= 0 || windowMs > 2147483647 || !Number.isSafeInteger(maxRequests) || maxRequests <= 0)
|
|
225
|
+
throw new TypeError("Rate limit window/count must be positive integers (window <= 2147483647ms)");
|
|
226
|
+
const store = config.store;
|
|
227
|
+
if (!store || typeof store.increment !== "function")
|
|
228
|
+
throw new TypeError("An atomic rate limit store is required");
|
|
229
|
+
if (store.scope === "memory" && !permitsMemory())
|
|
230
|
+
throw new Error("Memory rate limiting is restricted to development and tests");
|
|
200
231
|
return async (context, next) => {
|
|
201
232
|
const { event, requestId } = context;
|
|
202
|
-
const key = keyGenerator(event);
|
|
203
233
|
const now = Date.now();
|
|
204
234
|
let entry;
|
|
205
|
-
|
|
206
|
-
entry = await store.increment(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
entry = { ...existing, count: existing.count + 1 };
|
|
213
|
-
}
|
|
214
|
-
await store.set(key, entry);
|
|
235
|
+
try {
|
|
236
|
+
entry = await store.increment(keyGenerator(event), windowMs);
|
|
237
|
+
if (!Number.isSafeInteger(entry.count) || entry.count <= 0 || !Number.isSafeInteger(entry.resetAt))
|
|
238
|
+
throw new TypeError("Invalid atomic rate limit result");
|
|
239
|
+
} catch {
|
|
240
|
+
core.logger.error("Rate limit store unavailable", { requestId });
|
|
241
|
+
return new Response(JSON.stringify({ success: false, error: { code: "RATE_LIMIT_UNAVAILABLE", message: kitM("kit_internalError") }, requestId }), { status: 503, headers: { "Content-Type": "application/json" } });
|
|
215
242
|
}
|
|
216
243
|
const remaining = Math.max(0, maxRequests - entry.count);
|
|
217
244
|
const resetTime = Math.ceil(entry.resetAt / 1e3);
|
|
218
245
|
if (entry.count > maxRequests) {
|
|
219
|
-
core.logger.warn("Rate limit exceeded", {
|
|
246
|
+
core.logger.warn("Rate limit exceeded", { requestId });
|
|
220
247
|
if (onLimitReached) {
|
|
221
248
|
return onLimitReached(event);
|
|
222
249
|
}
|
|
@@ -236,7 +263,7 @@ function rateLimitMiddleware(config) {
|
|
|
236
263
|
"X-RateLimit-Limit": String(maxRequests),
|
|
237
264
|
"X-RateLimit-Remaining": "0",
|
|
238
265
|
"X-RateLimit-Reset": String(resetTime),
|
|
239
|
-
"Retry-After": String(Math.ceil((entry.resetAt - now) / 1e3))
|
|
266
|
+
"Retry-After": String(Math.max(1, Math.ceil((entry.resetAt - now) / 1e3)))
|
|
240
267
|
}
|
|
241
268
|
}
|
|
242
269
|
);
|
|
@@ -344,6 +371,7 @@ function createHandle(config = {}) {
|
|
|
344
371
|
}
|
|
345
372
|
if (rateLimitConfig) {
|
|
346
373
|
builtinMiddleware.push(rateLimitMiddleware({
|
|
374
|
+
...rateLimitConfig,
|
|
347
375
|
windowMs: rateLimitConfig.windowMs ?? 6e4,
|
|
348
376
|
maxRequests: rateLimitConfig.maxRequests ?? 100
|
|
349
377
|
}));
|
|
@@ -506,16 +534,14 @@ function matchPath(pathname, pattern) {
|
|
|
506
534
|
function buildTransportMiddleware(cryptoConfig) {
|
|
507
535
|
if (!cryptoConfig?.transport)
|
|
508
536
|
return null;
|
|
509
|
-
const transportOpts =
|
|
537
|
+
const transportOpts = cryptoConfig.transport;
|
|
510
538
|
return transportEncryptionMiddleware({
|
|
511
539
|
enabled: true,
|
|
512
540
|
crypto: cryptoConfig.crypto,
|
|
541
|
+
serverKeyRing: transportOpts.serverKeyRing,
|
|
513
542
|
keyExchangePath: transportOpts.keyExchangePath,
|
|
514
543
|
excludePaths: transportOpts.excludePaths,
|
|
515
|
-
|
|
516
|
-
keyStore: transportOpts.keyStore,
|
|
517
|
-
maxClients: transportOpts.maxClients,
|
|
518
|
-
requireEncryption: transportOpts.requireEncryption
|
|
544
|
+
keyStore: transportOpts.keyStore
|
|
519
545
|
});
|
|
520
546
|
}
|
|
521
547
|
function buildCookieProxyConfig(cryptoConfig) {
|
|
@@ -540,10 +566,32 @@ function sequence(...handles) {
|
|
|
540
566
|
return filtered[0];
|
|
541
567
|
}
|
|
542
568
|
return async ({ event, resolve }) => {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
569
|
+
function applyHandle(index, currentEvent, options) {
|
|
570
|
+
if (index === filtered.length)
|
|
571
|
+
return resolve(currentEvent, options);
|
|
572
|
+
return filtered[index]({
|
|
573
|
+
event: currentEvent,
|
|
574
|
+
resolve: async (nextEvent, nextOptions) => {
|
|
575
|
+
const merged = {
|
|
576
|
+
...options,
|
|
577
|
+
...nextOptions,
|
|
578
|
+
preload: options?.preload ?? nextOptions?.preload,
|
|
579
|
+
filterSerializedResponseHeaders: options?.filterSerializedResponseHeaders ?? nextOptions?.filterSerializedResponseHeaders
|
|
580
|
+
};
|
|
581
|
+
if (options?.transformPageChunk || nextOptions?.transformPageChunk) {
|
|
582
|
+
merged.transformPageChunk = async ({ html, done }) => {
|
|
583
|
+
if (nextOptions?.transformPageChunk)
|
|
584
|
+
html = await nextOptions.transformPageChunk({ html, done }) ?? "";
|
|
585
|
+
if (options?.transformPageChunk)
|
|
586
|
+
html = await options.transformPageChunk({ html, done }) ?? "";
|
|
587
|
+
return html;
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
return applyHandle(index + 1, nextEvent, merged);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
return applyHandle(0, event);
|
|
547
595
|
};
|
|
548
596
|
}
|
|
549
597
|
|
|
@@ -770,6 +818,12 @@ function createAuthApi(iam) {
|
|
|
770
818
|
|
|
771
819
|
// src/kit-main.ts
|
|
772
820
|
var kit = {
|
|
821
|
+
/** 原子限流工厂;共享store生命周期由应用管理。 */
|
|
822
|
+
middleware: {
|
|
823
|
+
rateLimit: rateLimitMiddleware,
|
|
824
|
+
createCacheRateLimitStore,
|
|
825
|
+
createMemoryRateLimitStore
|
|
826
|
+
},
|
|
773
827
|
// ─── Handle Hook ───
|
|
774
828
|
/** 创建 SvelteKit Handle Hook(含 auth / logging / rateLimit 内置配置) */
|
|
775
829
|
createHandle,
|
|
@@ -863,9 +917,8 @@ var kit = {
|
|
|
863
917
|
/** 写入浏览器端 Access Token(客户端 login/register 用) */
|
|
864
918
|
setBrowserToken,
|
|
865
919
|
/** 清除浏览器端 Access Token(客户端 logout 用) */
|
|
866
|
-
clearBrowserToken
|
|
920
|
+
clearBrowserToken
|
|
867
921
|
/** 创建浏览器端 Token 存储器(自定义 key 时使用) */
|
|
868
|
-
createTokenStore
|
|
869
922
|
},
|
|
870
923
|
// ─── CRUD ───
|
|
871
924
|
crud: {
|
|
@@ -875,7 +928,9 @@ var kit = {
|
|
|
875
928
|
// ─── i18n ───
|
|
876
929
|
i18n: {
|
|
877
930
|
/** 统一设置所有 hai 模块的默认语言 */
|
|
878
|
-
setLocale: setAllModulesLocale
|
|
931
|
+
setLocale: setAllModulesLocale,
|
|
932
|
+
/** 请求/SSR 语言隔离;异步处理使用同一请求上下文。 */
|
|
933
|
+
runWithLocale: core.i18n.runWithLocale
|
|
879
934
|
}
|
|
880
935
|
};
|
|
881
936
|
|