@spring-systems/server 0.8.0

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 (45) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/LICENSE +8 -0
  3. package/README.md +94 -0
  4. package/dist/api-route-handler.d.ts +49 -0
  5. package/dist/api-route-handler.js +19 -0
  6. package/dist/api-route-handler.js.map +1 -0
  7. package/dist/chunk-7IUSTA5W.js +113 -0
  8. package/dist/chunk-7IUSTA5W.js.map +1 -0
  9. package/dist/chunk-CLZU34DG.js +465 -0
  10. package/dist/chunk-CLZU34DG.js.map +1 -0
  11. package/dist/chunk-CP33WQ5Q.js +47 -0
  12. package/dist/chunk-CP33WQ5Q.js.map +1 -0
  13. package/dist/chunk-FEB3UZEG.js +407 -0
  14. package/dist/chunk-FEB3UZEG.js.map +1 -0
  15. package/dist/chunk-KA7RJCWA.js +24 -0
  16. package/dist/chunk-KA7RJCWA.js.map +1 -0
  17. package/dist/chunk-OYTV4D7E.js +159 -0
  18. package/dist/chunk-OYTV4D7E.js.map +1 -0
  19. package/dist/chunk-YV6DZVPI.js +43 -0
  20. package/dist/chunk-YV6DZVPI.js.map +1 -0
  21. package/dist/client.d.ts +6 -0
  22. package/dist/client.js +14 -0
  23. package/dist/client.js.map +1 -0
  24. package/dist/handlers/index.d.ts +81 -0
  25. package/dist/handlers/index.js +48 -0
  26. package/dist/handlers/index.js.map +1 -0
  27. package/dist/index.d.ts +10 -0
  28. package/dist/index.js +44 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/next-adapters.d.ts +25 -0
  31. package/dist/next-adapters.js +14 -0
  32. package/dist/next-adapters.js.map +1 -0
  33. package/dist/proxy-middleware.d.ts +8 -0
  34. package/dist/proxy-middleware.js +10 -0
  35. package/dist/proxy-middleware.js.map +1 -0
  36. package/dist/rate-limiter.d.ts +67 -0
  37. package/dist/rate-limiter.js +15 -0
  38. package/dist/rate-limiter.js.map +1 -0
  39. package/dist/runtime-env.d.ts +15 -0
  40. package/dist/runtime-env.js +9 -0
  41. package/dist/runtime-env.js.map +1 -0
  42. package/dist/security-headers.d.ts +8 -0
  43. package/dist/security-headers.js +11 -0
  44. package/dist/security-headers.js.map +1 -0
  45. package/package.json +114 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,62 @@
1
+ # @spring-systems/server
2
+
3
+ ## 0.7.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Restored backward-compatible default for `createNextUIAdapter()` dynamic imports (`ssr: true` by default).
8
+ - Added `createClientOnlyNextUIAdapter()` helper for client-only apps (`ssr: false` by default).
9
+ - Added a dev-time warning when `createNextUIAdapter()` is used without explicit `defaultDynamicSsr`, to prevent accidental SSR defaults in client-only apps.
10
+ - Restricted `@spring-systems/server/client` exports to client-safe factories only (`createNextRouteAdapter`, `createClientOnlyNextUIAdapter`).
11
+ - Applied CORS headers consistently for proxy error responses (including non-logout routes).
12
+ - Enforced server-only root entrypoint via `import "server-only"`; client-safe adapters remain available under `@spring-systems/server/client`.
13
+ - Updated docs to use explicit `defaultDynamicSsr` in SSR-oriented adapter examples.
14
+ - Added dedicated `@spring-systems/server/client` export for client-only adapter imports.
15
+ - Refactored API route internals by extracting shared utility helpers from `api-route-handler`.
16
+ - Updated dependencies
17
+ - @spring-systems/core@0.7.5
18
+ - @spring-systems/ui@0.7.5
19
+
20
+ ## 0.7.4
21
+
22
+ ### Patch Changes
23
+
24
+ - Dependency updates and version alignment.
25
+ - Updated dependencies
26
+ - @spring-systems/core@0.7.4
27
+ - @spring-systems/ui@0.7.4
28
+
29
+ ## 0.7.3
30
+
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies
34
+ - @spring-systems/ui@0.7.3
35
+ - @spring-systems/core@0.7.3
36
+
37
+ ## 0.7.2
38
+
39
+ ### Patch Changes
40
+
41
+ - Improve package documentation, onboarding navigation, and npm-published docs coverage.
42
+ - Updated dependencies
43
+ - @spring-systems/core@0.7.2
44
+ - @spring-systems/ui@0.7.2
45
+
46
+ ## 0.7.1
47
+
48
+ ### Patch Changes
49
+
50
+ - Align package documentation with repository docs and enforce docs quality checks in release workflow.
51
+ - Updated dependencies
52
+ - @spring-systems/core@0.7.1
53
+ - @spring-systems/ui@0.7.1
54
+
55
+ ## 0.7.0
56
+
57
+ ### Patch Changes
58
+
59
+ - Initial npm registry release
60
+ - Updated dependencies
61
+ - @spring-systems/core@0.7.0
62
+ - @spring-systems/ui@0.7.0
package/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ Copyright (c) 2024-2026 The Authors
2
+
3
+ All rights reserved.
4
+
5
+ This software and associated documentation are proprietary and confidential.
6
+ No rights are granted except as expressly agreed in writing by the copyright holder(s).
7
+ Use, copying, modification, distribution, sublicensing, publication, or disclosure
8
+ without prior written permission is prohibited.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # @spring-systems/server
2
+
3
+ Next.js server-side integration for the Spring Systems SPRING framework. This package keeps the browser-to-backend boundary explicit: proxying, runtime env exposure, security headers, and server adapters live here instead of leaking into app code.
4
+
5
+ ## When To Use It
6
+
7
+ Use this package when the app should talk to the backend through Next.js rather than directly from the browser.
8
+
9
+ - use it for proxy routes, runtime environment bootstrap, server adapters, and security headers
10
+ - skip it for client-side rendering or reusable UI behavior
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @spring-systems/server @spring-systems/core @spring-systems/ui
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ Three files wire the server layer into a Next.js 16 app:
21
+
22
+ ```ts
23
+ // src/proxy.ts — replaces middleware.ts in Next.js 16
24
+ import "@/project-config";
25
+ export { proxy } from "@spring-systems/server/proxy";
26
+
27
+ export const config = {
28
+ matcher: ["/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|api/health).*)"],
29
+ };
30
+ ```
31
+
32
+ ```ts
33
+ // app/api/[...path]/route.ts
34
+ export { GET, POST, PUT, DELETE, PATCH, OPTIONS } from "@spring-systems/server/api-handler";
35
+ ```
36
+
37
+ ```tsx
38
+ // app/layout.tsx
39
+ import { RuntimeEnvScript } from "@spring-systems/server/runtime-env";
40
+ import { createNextRouteAdapter, createNextUIAdapter } from "@spring-systems/server/adapters";
41
+ import { SpringProvider } from "@spring-systems/ui/components";
42
+
43
+ export default function RootLayout({ children }) {
44
+ return (
45
+ <html>
46
+ <body>
47
+ <RuntimeEnvScript />
48
+ <SpringProvider
49
+ config={{}}
50
+ adapters={{
51
+ route: createNextRouteAdapter(),
52
+ ui: createNextUIAdapter({ defaultDynamicSsr: true }),
53
+ }}
54
+ >
55
+ {children}
56
+ </SpringProvider>
57
+ </body>
58
+ </html>
59
+ );
60
+ }
61
+ ```
62
+
63
+ The `@/project-config` side-effect import must come first — it registers framework settings (CSP, blocked paths, capabilities) before the proxy reads them.
64
+
65
+ ## Entry Points
66
+
67
+ | Import path | Use it for |
68
+ | ----------------------------------------- | ------------------------------- |
69
+ | `@spring-systems/server/api-handler` | API route handlers |
70
+ | `@spring-systems/server/runtime-env` | Runtime environment bootstrap |
71
+ | `@spring-systems/server/adapters` | Next.js adapter factories |
72
+ | `@spring-systems/server/client` | Client-safe adapter helpers |
73
+ | `@spring-systems/server/proxy` | Proxy middleware |
74
+ | `@spring-systems/server/rate-limiter` | Rate limiting helpers |
75
+ | `@spring-systems/server/handlers` | Shared request handlers |
76
+ | `@spring-systems/server/security-headers` | CSP and security header helpers |
77
+
78
+ ## Compatibility
79
+
80
+ Requires **Next.js 16** and **React 19**. Peer dependencies: `@spring-systems/core`, `@spring-systems/ui`.
81
+
82
+ ## Boundary Rules
83
+
84
+ - keep upstream API communication behind this package when deployment relies on cookie or CSRF protections
85
+ - do not move browser rendering concerns here just because the consuming app uses Next.js
86
+ - for runtime and deployment rules see [ENVIRONMENT_VARIABLES.md](https://bitbucket.org/springsystems-projects/spring-framework-frontend/blob/main/docs/ENVIRONMENT_VARIABLES.md) and [SECURITY.md](https://bitbucket.org/springsystems-projects/spring-framework-frontend/blob/main/docs/SECURITY.md) in the monorepo docs
87
+
88
+ ## Changelog
89
+
90
+ See [CHANGELOG.md](CHANGELOG.md) for release history and breaking changes.
91
+
92
+ ## License
93
+
94
+ UNLICENSED
@@ -0,0 +1,49 @@
1
+ import { NextRequest, NextResponse } from 'next/server.js';
2
+
3
+ /**
4
+ * API proxy route handler for Next.js App Router.
5
+ *
6
+ * Proxies requests from the frontend to the backend API, handling:
7
+ * - Session token management (cookie-based auth)
8
+ * - CSRF protection and CORS headers
9
+ * - Login rate limiting
10
+ * - Request body size limits
11
+ * - Path normalization and version deduplication
12
+ *
13
+ * @module api-route-handler
14
+ */
15
+
16
+ /** Handle GET requests through the API proxy. */
17
+ declare function GET(request: NextRequest, context: {
18
+ params: Promise<{
19
+ path: string[];
20
+ }>;
21
+ }): Promise<NextResponse<unknown>>;
22
+ /** Handle POST requests through the API proxy. */
23
+ declare function POST(request: NextRequest, context: {
24
+ params: Promise<{
25
+ path: string[];
26
+ }>;
27
+ }): Promise<NextResponse<unknown>>;
28
+ /** Handle PUT requests through the API proxy. */
29
+ declare function PUT(request: NextRequest, context: {
30
+ params: Promise<{
31
+ path: string[];
32
+ }>;
33
+ }): Promise<NextResponse<unknown>>;
34
+ /** Handle DELETE requests through the API proxy. */
35
+ declare function DELETE(request: NextRequest, context: {
36
+ params: Promise<{
37
+ path: string[];
38
+ }>;
39
+ }): Promise<NextResponse<unknown>>;
40
+ /** Handle PATCH requests through the API proxy. */
41
+ declare function PATCH(request: NextRequest, context: {
42
+ params: Promise<{
43
+ path: string[];
44
+ }>;
45
+ }): Promise<NextResponse<unknown>>;
46
+ /** Handle CORS preflight requests. */
47
+ declare function OPTIONS(request: NextRequest): Promise<NextResponse<unknown>>;
48
+
49
+ export { DELETE, GET, OPTIONS, PATCH, POST, PUT };
@@ -0,0 +1,19 @@
1
+ import {
2
+ DELETE,
3
+ GET,
4
+ OPTIONS,
5
+ PATCH,
6
+ POST,
7
+ PUT
8
+ } from "./chunk-CLZU34DG.js";
9
+ import "./chunk-FEB3UZEG.js";
10
+ import "./chunk-7IUSTA5W.js";
11
+ export {
12
+ DELETE,
13
+ GET,
14
+ OPTIONS,
15
+ PATCH,
16
+ POST,
17
+ PUT
18
+ };
19
+ //# sourceMappingURL=api-route-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,113 @@
1
+ // src/rate-limiter.ts
2
+ var InMemoryRateLimiter = class {
3
+ ipStore = /* @__PURE__ */ new Map();
4
+ accountStore = /* @__PURE__ */ new Map();
5
+ getStore(store) {
6
+ return store === "ip" ? this.ipStore : this.accountStore;
7
+ }
8
+ get(store, key) {
9
+ return this.getStore(store).get(key) ?? null;
10
+ }
11
+ set(store, key, entry) {
12
+ this.getStore(store).set(key, entry);
13
+ }
14
+ delete(store, key) {
15
+ this.getStore(store).delete(key);
16
+ }
17
+ size(store) {
18
+ return this.getStore(store).size;
19
+ }
20
+ evictOldest(store, count) {
21
+ const map = this.getStore(store);
22
+ const entries = [...map.entries()].sort((a, b) => a[1].windowStartedAt - b[1].windowStartedAt);
23
+ for (let i = 0; i < count && i < entries.length; i++) {
24
+ const candidate = entries[i];
25
+ if (!candidate) break;
26
+ map.delete(candidate[0]);
27
+ }
28
+ }
29
+ sweepExpired(store, now, windowMs) {
30
+ const map = this.getStore(store);
31
+ for (const [key, entry] of map.entries()) {
32
+ const windowExpired = now - entry.windowStartedAt > windowMs;
33
+ const noActiveBlock = entry.blockedUntil <= now;
34
+ if (windowExpired && noActiveBlock) {
35
+ map.delete(key);
36
+ }
37
+ }
38
+ }
39
+ };
40
+ var adapter = new InMemoryRateLimiter();
41
+ var hasWarnedInMemory = false;
42
+ function setRateLimiterAdapter(custom) {
43
+ adapter = custom;
44
+ }
45
+ function getRateLimiterAdapter() {
46
+ return adapter;
47
+ }
48
+ function checkRateLimit(ipKey, accountKey, policy) {
49
+ if (!hasWarnedInMemory && adapter instanceof InMemoryRateLimiter && process.env.NODE_ENV === "production") {
50
+ hasWarnedInMemory = true;
51
+ console.warn("[server] Rate limiter is using in-memory storage in production. For multi-instance deployments, call setRateLimiterAdapter() with a shared-storage adapter.");
52
+ }
53
+ const now = Date.now();
54
+ const ipResult = checkSingleLimit(adapter, "ip", ipKey, policy.maxAttemptsByIpAndAccount, policy, now);
55
+ if (ipResult) return ipResult;
56
+ if (accountKey) {
57
+ const accountResult = checkSingleLimit(adapter, "account", accountKey, policy.maxAttemptsByAccount, policy, now);
58
+ if (accountResult) return accountResult;
59
+ }
60
+ return null;
61
+ }
62
+ function recordFailedAttempt(ipKey, accountKey, policy) {
63
+ const now = Date.now();
64
+ recordAttempt(adapter, "ip", ipKey, policy, now);
65
+ if (accountKey) {
66
+ recordAttempt(adapter, "account", accountKey, policy, now);
67
+ }
68
+ }
69
+ function clearRateLimitEntries(ipKey, accountKey) {
70
+ adapter.delete("ip", ipKey);
71
+ if (accountKey) {
72
+ adapter.delete("account", accountKey);
73
+ }
74
+ }
75
+ function checkSingleLimit(adap, store, key, maxAttempts, policy, now) {
76
+ const entry = adap.get(store, key);
77
+ if (!entry) return null;
78
+ if (entry.blockedUntil > now) {
79
+ const remainSec = Math.ceil((entry.blockedUntil - now) / 1e3);
80
+ return `Rate limited (${store}). Try again in ${remainSec}s.`;
81
+ }
82
+ if (now - entry.windowStartedAt > policy.windowMs) {
83
+ adap.delete(store, key);
84
+ return null;
85
+ }
86
+ if (entry.count >= maxAttempts) {
87
+ entry.blockedUntil = now + policy.blockMs;
88
+ adap.set(store, key, entry);
89
+ return `Too many attempts (${store}). Blocked for ${Math.ceil(policy.blockMs / 1e3)}s.`;
90
+ }
91
+ return null;
92
+ }
93
+ function recordAttempt(adap, store, key, policy, now) {
94
+ if (adap.size(store) >= policy.maxKeys) {
95
+ adap.evictOldest(store, Math.floor(policy.maxKeys * 0.1));
96
+ }
97
+ const entry = adap.get(store, key);
98
+ if (!entry || now - entry.windowStartedAt > policy.windowMs) {
99
+ adap.set(store, key, { count: 1, windowStartedAt: now, blockedUntil: 0 });
100
+ } else {
101
+ entry.count++;
102
+ adap.set(store, key, entry);
103
+ }
104
+ }
105
+
106
+ export {
107
+ setRateLimiterAdapter,
108
+ getRateLimiterAdapter,
109
+ checkRateLimit,
110
+ recordFailedAttempt,
111
+ clearRateLimitEntries
112
+ };
113
+ //# sourceMappingURL=chunk-7IUSTA5W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rate-limiter.ts"],"sourcesContent":["/**\n * Pluggable rate limiter for API proxy authentication.\n *\n * The default implementation uses in-memory Maps (suitable for single-instance deployments).\n * Multi-instance deployments can replace this with a custom adapter via `setRateLimiterAdapter()`.\n *\n * @example\n * ```ts\n * import { setRateLimiterAdapter } from \"@spring-systems/server/rate-limiter\";\n * import { createCustomRateLimiter } from \"./my-rate-limiter\";\n *\n * setRateLimiterAdapter(createCustomRateLimiter());\n * ```\n *\n * @module rate-limiter\n */\n\nexport interface RateLimitEntry {\n count: number;\n windowStartedAt: number;\n blockedUntil: number;\n}\n\nexport interface RateLimitPolicy {\n windowMs: number;\n blockMs: number;\n maxAttemptsByIpAndAccount: number;\n maxAttemptsByAccount: number;\n maxKeys: number;\n}\n\n/**\n * Rate limiter adapter interface. Implementations must be async-safe.\n */\nexport interface RateLimiterAdapter {\n /** Get the current rate limit entry for a key, or null if no entry exists. */\n get(store: \"ip\" | \"account\", key: string): RateLimitEntry | null;\n\n /** Set/update the rate limit entry for a key. */\n set(store: \"ip\" | \"account\", key: string, entry: RateLimitEntry): void;\n\n /** Delete an entry (e.g. on successful login). */\n delete(store: \"ip\" | \"account\", key: string): void;\n\n /** Get the total number of tracked keys (for eviction logic). */\n size(store: \"ip\" | \"account\"): number;\n\n /** Clear the oldest entries when maxKeys is exceeded. */\n evictOldest(store: \"ip\" | \"account\", count: number): void;\n\n /**\n * Remove expired entries for a store.\n * Optional to preserve compatibility with existing custom adapters.\n */\n sweepExpired?(store: \"ip\" | \"account\", now: number, windowMs: number): void;\n}\n\n// ---------------------------------------------------------------------------\n// Default in-memory implementation\n// ---------------------------------------------------------------------------\n\nclass InMemoryRateLimiter implements RateLimiterAdapter {\n private ipStore = new Map<string, RateLimitEntry>();\n private accountStore = new Map<string, RateLimitEntry>();\n\n private getStore(store: \"ip\" | \"account\"): Map<string, RateLimitEntry> {\n return store === \"ip\" ? this.ipStore : this.accountStore;\n }\n\n get(store: \"ip\" | \"account\", key: string): RateLimitEntry | null {\n return this.getStore(store).get(key) ?? null;\n }\n\n set(store: \"ip\" | \"account\", key: string, entry: RateLimitEntry): void {\n this.getStore(store).set(key, entry);\n }\n\n delete(store: \"ip\" | \"account\", key: string): void {\n this.getStore(store).delete(key);\n }\n\n size(store: \"ip\" | \"account\"): number {\n return this.getStore(store).size;\n }\n\n evictOldest(store: \"ip\" | \"account\", count: number): void {\n const map = this.getStore(store);\n const entries = [...map.entries()].sort((a, b) => a[1].windowStartedAt - b[1].windowStartedAt);\n for (let i = 0; i < count && i < entries.length; i++) {\n const candidate = entries[i];\n if (!candidate) break;\n map.delete(candidate[0]);\n }\n }\n\n sweepExpired(store: \"ip\" | \"account\", now: number, windowMs: number): void {\n const map = this.getStore(store);\n for (const [key, entry] of map.entries()) {\n const windowExpired = now - entry.windowStartedAt > windowMs;\n const noActiveBlock = entry.blockedUntil <= now;\n if (windowExpired && noActiveBlock) {\n map.delete(key);\n }\n }\n }\n}\n\n// Singleton adapter\nlet adapter: RateLimiterAdapter = new InMemoryRateLimiter();\nlet hasWarnedInMemory = false;\n\n/** Replace the default in-memory rate limiter with a custom adapter. */\nexport function setRateLimiterAdapter(custom: RateLimiterAdapter): void {\n adapter = custom;\n}\n\n/** Get the current rate limiter adapter. */\nexport function getRateLimiterAdapter(): RateLimiterAdapter {\n return adapter;\n}\n\n/**\n * Check if a login attempt should be rate-limited.\n * @returns A reason string if blocked, or null if allowed.\n */\nexport function checkRateLimit(\n ipKey: string,\n accountKey: string | null,\n policy: RateLimitPolicy,\n): string | null {\n if (!hasWarnedInMemory && adapter instanceof InMemoryRateLimiter && process.env.NODE_ENV === \"production\") {\n hasWarnedInMemory = true;\n console.warn(\"[server] Rate limiter is using in-memory storage in production. For multi-instance deployments, call setRateLimiterAdapter() with a shared-storage adapter.\");\n }\n const now = Date.now();\n\n // Check IP-based limit\n const ipResult = checkSingleLimit(adapter, \"ip\", ipKey, policy.maxAttemptsByIpAndAccount, policy, now);\n if (ipResult) return ipResult;\n\n // Check account-based limit\n if (accountKey) {\n const accountResult = checkSingleLimit(adapter, \"account\", accountKey, policy.maxAttemptsByAccount, policy, now);\n if (accountResult) return accountResult;\n }\n\n return null;\n}\n\n/**\n * Record a failed login attempt.\n */\nexport function recordFailedAttempt(\n ipKey: string,\n accountKey: string | null,\n policy: RateLimitPolicy,\n): void {\n const now = Date.now();\n recordAttempt(adapter, \"ip\", ipKey, policy, now);\n if (accountKey) {\n recordAttempt(adapter, \"account\", accountKey, policy, now);\n }\n}\n\n/**\n * Clear rate limit entries for a key (e.g. on successful login).\n */\nexport function clearRateLimitEntries(ipKey: string, accountKey: string | null): void {\n adapter.delete(\"ip\", ipKey);\n if (accountKey) {\n adapter.delete(\"account\", accountKey);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction checkSingleLimit(\n adap: RateLimiterAdapter,\n store: \"ip\" | \"account\",\n key: string,\n maxAttempts: number,\n policy: RateLimitPolicy,\n now: number,\n): string | null {\n const entry = adap.get(store, key);\n if (!entry) return null;\n\n if (entry.blockedUntil > now) {\n const remainSec = Math.ceil((entry.blockedUntil - now) / 1000);\n return `Rate limited (${store}). Try again in ${remainSec}s.`;\n }\n\n if (now - entry.windowStartedAt > policy.windowMs) {\n adap.delete(store, key);\n return null;\n }\n\n if (entry.count >= maxAttempts) {\n entry.blockedUntil = now + policy.blockMs;\n adap.set(store, key, entry);\n return `Too many attempts (${store}). Blocked for ${Math.ceil(policy.blockMs / 1000)}s.`;\n }\n\n return null;\n}\n\nfunction recordAttempt(\n adap: RateLimiterAdapter,\n store: \"ip\" | \"account\",\n key: string,\n policy: RateLimitPolicy,\n now: number,\n): void {\n // Evict old entries if needed\n if (adap.size(store) >= policy.maxKeys) {\n adap.evictOldest(store, Math.floor(policy.maxKeys * 0.1));\n }\n\n const entry = adap.get(store, key);\n if (!entry || now - entry.windowStartedAt > policy.windowMs) {\n adap.set(store, key, { count: 1, windowStartedAt: now, blockedUntil: 0 });\n } else {\n entry.count++;\n adap.set(store, key, entry);\n }\n}\n"],"mappings":"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","names":[]}