Skip to content

Commit

Permalink
Merge pull request #121 from upstash/DX-1248
Browse files Browse the repository at this point in the history
DX-1248: Ratelimit Update Eslint
  • Loading branch information
CahidArda authored Sep 18, 2024
2 parents 377f72c + e44e34c commit 1901192
Show file tree
Hide file tree
Showing 28 changed files with 292 additions and 229 deletions.
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist
examples
node_modules
37 changes: 0 additions & 37 deletions biome.json

This file was deleted.

Binary file modified bun.lockb
Binary file not shown.
103 changes: 103 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import unicorn from "eslint-plugin-unicorn";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});

export default [
{
ignores: ["**/*.config.*", "**/examples", "**/dist"],
},
...compat.extends(
"eslint:recommended",
"plugin:unicorn/recommended",
"plugin:@typescript-eslint/recommended"
),
{
plugins: {
"@typescript-eslint": typescriptEslint,
unicorn,
},

languageOptions: {
globals: {},
ecmaVersion: 5,
sourceType: "script",

parserOptions: {
project: "./tsconfig.json",
},
},

rules: {
"no-console": [
"error",
{
allow: ["warn", "error"],
},
],

"@typescript-eslint/no-magic-numbers": "off",
"@typescript-eslint/unbound-method": "off",
"@typescript-eslint/prefer-as-const": "error",
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/consistent-type-definitions": ["error", "type"],

"@typescript-eslint/no-unused-vars": [
"error",
{
varsIgnorePattern: "^_",
argsIgnorePattern: "^_",
},
],

"@typescript-eslint/prefer-ts-expect-error": "off",

"@typescript-eslint/no-misused-promises": [
"error",
{
checksVoidReturn: false,
},
],

"unicorn/prevent-abbreviations": "off",

"no-implicit-coercion": [
"error",
{
boolean: true,
},
],

"no-extra-boolean-cast": [
"error",
{
enforceForLogicalOperands: true,
},
],

"no-unneeded-ternary": [
"error",
{
defaultAssignment: true,
},
],

"unicorn/no-array-reduce": ["off"],
"unicorn/no-nested-ternary": "off",
"unicorn/no-null": "off",
"unicorn/filename-case": "off",
},
},
];
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
"scripts": {
"build": "tsup",
"test": "bun test src --coverage",
"fmt": "bunx @biomejs/biome check --apply ./src"
"fmt": "prettier --write .",
"lint": "eslint \"src/**/*.{js,ts,tsx}\" --quiet --fix"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.4.0",
"@upstash/redis": "^1.31.5",
"bun-types": "latest",
"rome": "^11.0.0",
"eslint": "^9.10.0",
"eslint-plugin-unicorn": "^55.0.0",
"tsup": "^7.2.0",
"turbo": "^1.10.15",
"typescript": "^5.0.0"
Expand Down
13 changes: 13 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @type {import('prettier').Config}
*/
const config = {
endOfLine: "lf",
singleQuote: false,
tabWidth: 2,
trailingComma: "es5",
printWidth: 100,
arrowParens: "always",
};

export default config;
7 changes: 4 additions & 3 deletions src/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Analytics as CoreAnalytics, Aggregate } from "@upstash/core-analytics";
import type { Aggregate } from "@upstash/core-analytics";
import { Analytics as CoreAnalytics } from "@upstash/core-analytics";
import type { Redis } from "./types";

export type Geo = {
Expand Down Expand Up @@ -51,10 +52,10 @@ export class Analytics {
* @returns
*/
public extractGeo(req: { geo?: Geo; cf?: Geo }): Geo {
if (typeof req.geo !== "undefined") {
if (req.geo !== undefined) {
return req.geo;
}
if (typeof req.cf !== "undefined") {
if (req.cf !== undefined) {
return req.cf;
}

Expand Down
8 changes: 4 additions & 4 deletions src/blockUntilReady.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ const metrics: Record<string | symbol, number> = {};

const spy = new Proxy(redis, {
get: (target, prop) => {
if (typeof metrics[prop] === "undefined") {
if (metrics[prop] === undefined) {
metrics[prop] = 0;
}
metrics[prop]++;
// @ts-ignore - we don't care about the types here
// @ts-expect-error we don't care about the types here
return target[prop];
},
});
Expand All @@ -36,7 +36,7 @@ describe("blockUntilReady", () => {
expect(res.success).toBe(false);

Check failure on line 36 in src/blockUntilReady.test.ts

View workflow job for this annotation

GitHub Actions / Tests

error: expect(received).toBe(expected)

Expected: false Received: true at /home/runner/work/ratelimit-js/ratelimit-js/src/blockUntilReady.test.ts:36:25

Check failure on line 36 in src/blockUntilReady.test.ts

View workflow job for this annotation

GitHub Actions / Tests

error: expect(received).toBe(expected)

Expected: false Received: true at /home/runner/work/ratelimit-js/ratelimit-js/src/blockUntilReady.test.ts:36:25

Check failure on line 36 in src/blockUntilReady.test.ts

View workflow job for this annotation

GitHub Actions / Tests

error: expect(received).toBe(expected)

Expected: false Received: true at /home/runner/work/ratelimit-js/ratelimit-js/src/blockUntilReady.test.ts:36:25

Check failure on line 36 in src/blockUntilReady.test.ts

View workflow job for this annotation

GitHub Actions / Tests

error: expect(received).toBe(expected)

Expected: false Received: true at /home/runner/work/ratelimit-js/ratelimit-js/src/blockUntilReady.test.ts:36:25

Check failure on line 36 in src/blockUntilReady.test.ts

View workflow job for this annotation

GitHub Actions / Tests

error: expect(received).toBe(expected)

Expected: false Received: true at /home/runner/work/ratelimit-js/ratelimit-js/src/blockUntilReady.test.ts:36:25

Check failure on line 36 in src/blockUntilReady.test.ts

View workflow job for this annotation

GitHub Actions / Tests

error: expect(received).toBe(expected)

Expected: false Received: true at /home/runner/work/ratelimit-js/ratelimit-js/src/blockUntilReady.test.ts:36:25

Check failure on line 36 in src/blockUntilReady.test.ts

View workflow job for this annotation

GitHub Actions / Tests

error: expect(received).toBe(expected)

Expected: false Received: true at /home/runner/work/ratelimit-js/ratelimit-js/src/blockUntilReady.test.ts:36:25
expect(start + 1000).toBeLessThanOrEqual(Date.now());
await res.pending;
}, 20000);
}, 20_000);

test("resolving before the timeout", async () => {
const id = crypto.randomUUID();
Expand All @@ -52,5 +52,5 @@ describe("blockUntilReady", () => {
expect(start + 1000).toBeGreaterThanOrEqual(Date.now());

await res.pending;
}, 20000);
}, 20_000);
});
7 changes: 3 additions & 4 deletions src/cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ test("ephemeral cache", async () => {

const spy = new Proxy(redis, {
get: (target, prop) => {
if (typeof metrics[prop] === "undefined") {
if (metrics[prop] === undefined) {
metrics[prop] = 0;
}
metrics[prop]++;
// @ts-ignore - we don't care about the types here
// @ts-expect-error - we don't care about the types here
return target[prop];
},
});
const ratelimit = new Ratelimit({
// @ts-ignore - we don't care about the types here
redis: spy,
limiter: Ratelimit.tokenBucket(maxTokens, "5 s", maxTokens),
ephemeralCache: new Map(),
Expand All @@ -42,4 +41,4 @@ test("ephemeral cache", async () => {
expect(reasons).toContain("cacheBlock")

await new Promise((r) => setTimeout(r, 5000));
}, 10000);
}, 10_000);
28 changes: 10 additions & 18 deletions src/deny-list/deny-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect, test, describe, afterAll, beforeAll } from "bun:test";
import { Redis } from "@upstash/redis";
import { Ratelimit } from "../index";
import { checkDenyListCache, defaultDeniedResponse, resolveLimitPayload } from "./deny-list";
import { DenyListResponse, RatelimitResponseType } from "../types";
import type { DenyListResponse, RatelimitResponseType } from "../types";


test("should get expected response from defaultDeniedResponse", () => {
Expand All @@ -24,14 +24,6 @@ describe("should resolve ratelimit and deny list response", async () => {
const redis = Redis.fromEnv();
const prefix = `test-resolve-prefix`;

let callCount = 0;
const spyRedis = {
multi: () => {
callCount += 1;
return redis.multi();
}
}

const initialResponse = {
success: true,
limit: 100,
Expand Down Expand Up @@ -60,15 +52,15 @@ describe("should resolve ratelimit and deny list response", async () => {
return redis.multi();
}
}

const denyListResponse: DenyListResponse = {
deniedValue: "testValue",
invalidIpDenyList: true
};

const response = resolveLimitPayload(spyRedis as Redis, prefix, [initialResponse, denyListResponse], 8);
await response.pending;

expect(response).toEqual(expectedResponse);
expect(callCount).toBe(1) // calls multi once to store ips
});
Expand All @@ -82,15 +74,15 @@ describe("should resolve ratelimit and deny list response", async () => {
return redis.multi();
}
}

const denyListResponse: DenyListResponse = {
deniedValue: "testValue",
invalidIpDenyList: false
};

const response = resolveLimitPayload(spyRedis as Redis, prefix, [initialResponse, denyListResponse], 8);
await response.pending;

expect(response).toEqual(expectedResponse);
expect(callCount).toBe(0) // doesn't call multi to update deny list
});
Expand All @@ -101,7 +93,7 @@ describe("should reject in deny list", async () => {
const redis = Redis.fromEnv();
const prefix = `test-prefix`;
const denyListKey = [prefix, "denyList", "all"].join(":");


const ratelimit = new Ratelimit({
redis,
Expand Down Expand Up @@ -140,7 +132,7 @@ describe("should reject in deny list", async () => {

test("should deny with ip in the deny list", async () => {

const { success, reason } = await ratelimit.limit("some-value", {ip: "denyIp"});
const { success, reason } = await ratelimit.limit("some-value", { ip: "denyIp" });

expect(success).toBe(false);
expect(reason).toBe("denyList");
Expand All @@ -151,7 +143,7 @@ describe("should reject in deny list", async () => {

test("should deny with user agent in the deny list", async () => {

const { success, reason } = await ratelimit.limit("some-value", {userAgent: "denyAgent"});
const { success, reason } = await ratelimit.limit("some-value", { userAgent: "denyAgent" });

expect(success).toBe(false);
expect(reason).toBe("denyList");
Expand All @@ -162,7 +154,7 @@ describe("should reject in deny list", async () => {

test("should deny with country in the deny list", async () => {

const { success, reason } = await ratelimit.limit("some-value", {country: "denyCountry"});
const { success, reason } = await ratelimit.limit("some-value", { country: "denyCountry" });

expect(success).toBe(false);
expect(reason).toBe("denyList");
Expand All @@ -173,7 +165,7 @@ describe("should reject in deny list", async () => {

test("should deny with multiple in deny list", async () => {

const { success, reason } = await ratelimit.limit("denyIdentifier", {country: "denyCountry"});
const { success, reason } = await ratelimit.limit("denyIdentifier", { country: "denyCountry" });

expect(success).toBe(false);
expect(reason).toBe("denyList");
Expand Down
7 changes: 4 additions & 3 deletions src/deny-list/deny-list.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DeniedValue, DenyListResponse, DenyListExtension, LimitPayload, IpDenyListStatusKey } from "../types"
import { RatelimitResponse, Redis } from "../types"
import type { DeniedValue, DenyListResponse, LimitPayload} from "../types";
import { DenyListExtension, IpDenyListStatusKey } from "../types"
import type { RatelimitResponse, Redis } from "../types"
import { Cache } from "../cache";
import { checkDenyListScript } from "./scripts";
import { updateIpDenyList } from "./ip-deny-list";
Expand Down Expand Up @@ -30,7 +31,7 @@ export const checkDenyListCache = (members: string[]): DeniedValue => {
*/
const blockMember = (member: string) => {
if (denyListCache.size() > 1000) denyListCache.empty();
denyListCache.blockUntil(member, Date.now() + 60000);
denyListCache.blockUntil(member, Date.now() + 60_000);
}

/**
Expand Down
Loading

0 comments on commit 1901192

Please sign in to comment.