Skip to content

Commit 7aba4e5

Browse files
committed
chore: add @types/deno dependency and update KvStore constructor to support Deno.Kv
1 parent 99a6cc7 commit 7aba4e5

5 files changed

Lines changed: 9 additions & 51 deletions

File tree

bun.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@
5858
"@eslint/js": "9.14.0",
5959
"@jest/globals": "29.7.0",
6060
"@mswjs/data": "0.16.1",
61+
"@swc/core": "^1.3.107",
62+
"@swc/jest": "^0.2.29",
63+
"@types/deno": "^2.5.0",
6164
"@types/jest": "29.5.12",
6265
"@types/ms": "^0.7.34",
6366
"@types/node": "20.14.5",
@@ -77,8 +80,6 @@
7780
"npm-run-all": "4.1.5",
7881
"prettier": "3.3.2",
7982
"rimraf": "^6.0.1",
80-
"@swc/core": "^1.3.107",
81-
"@swc/jest": "^0.2.29",
8283
"ts-node": "^10.9.2",
8384
"tsup": "^8.4.0",
8485
"typescript": "5.5.4",

src/handlers/start/api/helpers/rate-limit.ts

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class KvStore implements Store {
99
_options: ConfigType | undefined;
1010
prefix = "rate-limiter";
1111

12-
constructor(readonly _store: Kv) {}
12+
constructor(readonly _store: Kv | Deno.Kv) {}
1313

1414
init(options: ConfigType): void {
1515
this._options = options;
@@ -80,9 +80,7 @@ export class KvStore implements Store {
8080
}
8181

8282
export async function createKvStore(): Promise<KvStore> {
83-
// @ts-expect-error - Deno isn't defined without having the DenoLand extension installed or within the runtime
8483
if (typeof Deno !== "undefined" && Deno.openKv) {
85-
// @ts-expect-error - Deno isn't defined without having the DenoLand extension installed or within the runtime
8684
const kv = await Deno.openKv();
8785
if (!kv) {
8886
throw new Error("Failed to open Deno KV");
@@ -136,31 +134,6 @@ export async function createUserRateLimiter(c: Context, next: () => Promise<void
136134
return;
137135
}
138136

139-
// Helper to create rate limit response
140-
function createRateLimitResponse(resetAt: number, user: { id: number }) {
141-
const logMessage = logger.warn("RateLimit: exceeded", {
142-
key: `user:${user.id}:${mode}`,
143-
resetAt,
144-
limit,
145-
});
146-
147-
return Response.json(
148-
{
149-
ok: false,
150-
reasons: [logMessage.logMessage.raw],
151-
resetAt,
152-
},
153-
{
154-
status: 429,
155-
headers: {
156-
"x-ratelimit-limit": limit.toString(),
157-
"x-ratelimit-reset": resetAt.toString(),
158-
},
159-
}
160-
);
161-
}
162-
163-
// Use hono-rate-limiter with user-specific key and custom handler
164137
const rateLimitKey = `user:${user.id}:${mode}`;
165138
const { rateLimiter } = await import("hono-rate-limiter");
166139
const userLimiter = rateLimiter({
@@ -171,22 +144,7 @@ export async function createUserRateLimiter(c: Context, next: () => Promise<void
171144
store: kvStore,
172145
skipSuccessfulRequests: false,
173146
skipFailedRequests: false,
174-
handler: async (c) => {
175-
const rateLimitInfo = await kvStore.get(rateLimitKey);
176-
const resetAt = rateLimitInfo?.resetTime?.getTime() ?? Date.now() + 60 * 1000;
177-
const response = createRateLimitResponse(resetAt, user);
178-
c.res = response;
179-
},
180147
});
181148

182-
// Execute rate limiter - it will call next() internally if not rate limited
183-
// If rate limited, it will call our custom handler which sets c.res
184-
await userLimiter(c, next);
185-
186-
// If rate limit was exceeded, the handler set c.res
187-
// hono-rate-limiter should prevent next() from being called, but we check anyway
188-
// to ensure we don't continue processing if a response was already set
189-
if (c.res && c.res.status === 429) {
190-
return;
191-
}
149+
return await userLimiter(c, next);
192150
}

tests/__mocks__/deno-kv.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,14 @@ function mockOpenKv() {
3535
return Promise.resolve(mockKv);
3636
}
3737

38-
// @ts-expect-error - Deno isn't defined without having the DenoLand extension installed or within the runtime
3938
if (globalThis.Deno) {
40-
// @ts-expect-error - Deno isn't defined without having the DenoLand extension installed or within the runtime
4139
Object.defineProperty(globalThis.Deno, "openKv", {
4240
value: mockOpenKv,
4341
writable: true,
4442
configurable: true,
4543
});
4644
} else {
47-
// @ts-expect-error - Deno isn't defined without having the DenoLand extension installed or within the runtime
4845
globalThis.Deno = {
4946
openKv: mockOpenKv,
50-
// @ts-expect-error - Deno isn't defined without having the DenoLand extension installed or within the runtime
5147
} as unknown as typeof Deno;
5248
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
3030
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
3131
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
32-
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
32+
"types": ["deno"] /* Specify type package names to be included without being referenced in a source file. */,
3333
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
3434
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
3535
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */

0 commit comments

Comments
 (0)