@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.
Files changed (44) hide show
  1. package/README.en.md +39 -13
  2. package/README.ja.md +39 -13
  3. package/README.md +39 -13
  4. package/dist/adapter/index.d.ts +17 -2
  5. package/dist/adapter/index.js.map +1 -1
  6. package/dist/browser.d.ts +12 -6
  7. package/dist/browser.js +6 -7
  8. package/dist/browser.js.map +1 -1
  9. package/dist/{chunk-DVW7CQ3H.js → chunk-2PWGJCWX.js} +3 -3
  10. package/dist/chunk-2PWGJCWX.js.map +1 -0
  11. package/dist/{chunk-VOTDAVCB.js → chunk-2YYRP7HH.js} +17 -33
  12. package/dist/chunk-2YYRP7HH.js.map +1 -0
  13. package/dist/{chunk-MLSP67JF.js → chunk-333FGMGQ.js} +59 -111
  14. package/dist/chunk-333FGMGQ.js.map +1 -0
  15. package/dist/chunk-OVRZIDCD.js +175 -0
  16. package/dist/chunk-OVRZIDCD.js.map +1 -0
  17. package/dist/{chunk-L7BTMAKX.js → chunk-P6XH7G4N.js} +3 -6
  18. package/dist/chunk-P6XH7G4N.js.map +1 -0
  19. package/dist/{chunk-NVYIUBU7.js → chunk-XC7I2L3G.js} +2 -2
  20. package/dist/chunk-XC7I2L3G.js.map +1 -0
  21. package/dist/client/index.d.ts +47 -1
  22. package/dist/client/index.js +3 -3
  23. package/dist/client/index.js.map +1 -1
  24. package/dist/index.d.ts +217 -347
  25. package/dist/index.js +138 -83
  26. package/dist/index.js.map +1 -1
  27. package/dist/kit-a2a-types-CwT1AFQu.d.ts +136 -0
  28. package/dist/{index-B7s-0mZl.d.ts → kit-client-auth-HnZIYUia.d.ts} +220 -101
  29. package/dist/kit-crypto-types-Dd24aJ4m.d.ts +194 -0
  30. package/dist/{kit-i18n-D9Qqk-1k.d.ts → kit-i18n-DEzmzE5T.d.ts} +142 -29
  31. package/dist/{kit-types-ByWdbCeO.d.ts → kit-types-CnzhN-AS.d.ts} +240 -162
  32. package/dist/modules/a2a/index.d.ts +13 -111
  33. package/dist/modules/a2a/index.js +2 -2
  34. package/dist/modules/crypto/index.d.ts +28 -195
  35. package/dist/modules/crypto/index.js +3 -3
  36. package/dist/modules/crypto/index.js.map +1 -1
  37. package/package.json +10 -10
  38. package/dist/chunk-DVW7CQ3H.js.map +0 -1
  39. package/dist/chunk-L7BTMAKX.js.map +0 -1
  40. package/dist/chunk-MLSP67JF.js.map +0 -1
  41. package/dist/chunk-NVYIUBU7.js.map +0 -1
  42. package/dist/chunk-QHYNL24G.js +0 -117
  43. package/dist/chunk-QHYNL24G.js.map +0 -1
  44. package/dist/chunk-VOTDAVCB.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,11 +1,14 @@
1
- import { defineCrud } from './chunk-L7BTMAKX.js';
2
- export { KitConfigSchema, defineCrud, resolveKitConfig, resolveKitTransportConfig, resolveOptions, resolveText } from './chunk-L7BTMAKX.js';
3
- import { createTokenStore, clearBrowserToken, setBrowserToken, logout, registerAndLogin, loginWithApiKey, loginWithLdap, loginWithOtp, login, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient, configureAuth, getAccessToken } from './chunk-VOTDAVCB.js';
4
- import { resolveA2AConfig, handleA2ARequest } from './chunk-DVW7CQ3H.js';
5
- import { transportEncryptionMiddleware } from './chunk-MLSP67JF.js';
6
- import './chunk-NVYIUBU7.js';
7
- import { kitM, setAllModulesLocale } from './chunk-QHYNL24G.js';
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
- cleanupTimer = null;
171
+ cleanupCursor;
122
172
  /**
123
- * 启动定期清理过期条目
124
- *
125
- * @param intervalMs - 清理间隔(毫秒)
173
+ * 有界惰性回收;保留迭代位置,避免每次从头扫描使尾部过期项永久滞留。
174
+ * @param now - 当前请求时间,等于 resetAt 时窗口已经失效。
126
175
  */
127
- startCleanup(intervalMs) {
128
- if (this.cleanupTimer)
129
- return;
130
- this.cleanupTimer = setInterval(() => {
131
- const now = Date.now();
132
- for (const [key, entry] of this.store) {
133
- if (entry.resetAt < now) {
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
- }, intervalMs);
138
- if (this.cleanupTimer && typeof this.cleanupTimer === "object" && "unref" in this.cleanupTimer) {
139
- this.cleanupTimer.unref();
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
- let entry = this.store.get(key);
173
- if (!entry || entry.resetAt < now) {
174
- entry = { count: 1, resetAt: now + windowMs };
175
- } else {
176
- entry.count++;
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
- const store = config.store ?? (() => {
196
- const memStore = new MemoryRateLimitStore();
197
- memStore.startCleanup(windowMs);
198
- return memStore;
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
- if (store.increment) {
206
- entry = await store.increment(key, windowMs);
207
- } else {
208
- const existing = await store.get(key);
209
- if (!existing || existing.resetAt < now) {
210
- entry = { count: 1, resetAt: now + windowMs };
211
- } else {
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", { key, requestId });
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 = typeof cryptoConfig.transport === "object" ? cryptoConfig.transport : {};
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
- encryptResponse: transportOpts.encryptResponse,
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
- return filtered.reduceRight(
544
- (next, handle) => (event2) => handle({ event: event2, resolve: next }),
545
- resolve
546
- )(event);
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