ayiin 2.0.40 → 2.0.42

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import Methods from "./methods";
2
- export type { DurationInput, FloodwaitPlaceholder, RateLimitStore, RateLimiterOptions, } from "./types";
2
+ export type { DurationInput, RateLimitStore, RateLimiterOptions, } from "./types";
3
3
  export default class Ayiin extends Methods {
4
4
  version: string;
5
5
  constructor();
@@ -3,5 +3,4 @@ import { RateLimiterOptions } from "../types";
3
3
  import Tools from "./tools";
4
4
  export declare class Limiter extends Tools {
5
5
  createRateLimit: (options: RateLimiterOptions) => RequestHandler;
6
- private formatMessage;
7
6
  }
@@ -44,7 +44,7 @@ class MemoryStore {
44
44
  // 🔹 Class Limiter
45
45
  class Limiter extends tools_1.default {
46
46
  createRateLimit = (options) => {
47
- const store = options.store ?? new MemoryStore(); // default ke MemoryStore
47
+ const store = options.store ?? new MemoryStore();
48
48
  return async (req, res, next) => {
49
49
  const now = Date.now();
50
50
  const userId = options.identifyUser
@@ -57,14 +57,17 @@ class Limiter extends tools_1.default {
57
57
  const blockedUntil = new Date(blockedUntilStr).getTime();
58
58
  const remainingMs = blockedUntil - now;
59
59
  if (remainingMs > 0) {
60
- const msg = options.messageFormat
61
- ? this.formatMessage(options.messageFormat, {
60
+ const msg = typeof options.messageFormat === "function"
61
+ ? options.messageFormat({
62
62
  api: options.keyPrefix,
63
63
  remainMs: remainingMs,
64
64
  remain: this.formatRemainingTime(remainingMs),
65
- limit: options.limit,
65
+ limit: Array.isArray(options.limit)
66
+ ? options.limit[0]
67
+ : options.limit,
66
68
  })
67
- : `[FloodWait] - Silakan coba lagi dalam ${this.formatRemainingTime(remainingMs)}.`;
69
+ : (options.messageFormat ??
70
+ `[FloodWait] - Silakan coba lagi dalam ${this.formatRemainingTime(remainingMs)}.`);
68
71
  return res.status(429).json({ success: false, error: msg });
69
72
  }
70
73
  }
@@ -75,45 +78,44 @@ class Limiter extends tools_1.default {
75
78
  if (hits === 1) {
76
79
  await store.expire(rateKey, windowMs);
77
80
  }
78
- if (hits > options.limit) {
81
+ // ambil level sekarang
82
+ let level = parseInt((await store.get(levelKey)) ?? "0", 10);
83
+ // tentukan limit sesuai level
84
+ let currentLimit;
85
+ if (Array.isArray(options.limit)) {
86
+ currentLimit = options.limit[Math.min(level, options.limit.length - 1)];
87
+ }
88
+ else {
89
+ currentLimit = Number(options.limit);
90
+ }
91
+ if (hits > currentLimit) {
92
+ level += 1;
93
+ await store.set(levelKey, level.toString(), refreshAtMs);
79
94
  let floodwaitMs;
80
- let level;
81
95
  if (Array.isArray(options.floodwaitMs)) {
82
- // escalation mode
83
- level = parseInt((await store.get(levelKey)) ?? "0", 10) + 1;
84
- await store.set(levelKey, level.toString(), refreshAtMs);
85
96
  const idx = Math.min(level - 1, options.floodwaitMs.length - 1);
86
97
  const floodwait = options.floodwaitMs[idx];
87
98
  floodwaitMs = this.toMilliseconds(floodwait ?? options.floodwaitMs[0]);
88
99
  }
89
100
  else {
90
- // fixed mode
91
101
  floodwaitMs = this.toMilliseconds(options.floodwaitMs);
92
102
  }
93
103
  const untilDate = new Date(now + floodwaitMs).toISOString();
94
104
  await store.set(floodKey, untilDate, floodwaitMs);
95
- const msg = options.messageFormat
96
- ? this.formatMessage(options.messageFormat, {
105
+ const msg = typeof options.messageFormat === "function"
106
+ ? options.messageFormat({
97
107
  api: options.keyPrefix,
98
108
  remainMs: floodwaitMs,
99
109
  remain: this.formatRemainingTime(floodwaitMs),
100
110
  level,
101
- limit: options.limit,
111
+ limit: currentLimit,
102
112
  })
103
- : `[FloodWait] - Terlalu banyak permintaan. Silakan coba lagi dalam ${this.formatRemainingTime(floodwaitMs)}.`;
113
+ : (options.messageFormat ??
114
+ `[FloodWait] - Terlalu banyak permintaan. Silakan coba lagi dalam ${this.formatRemainingTime(floodwaitMs)}.`);
104
115
  return res.status(429).json({ success: false, error: msg });
105
116
  }
106
117
  next();
107
118
  };
108
119
  };
109
- // internal formatter
110
- formatMessage(template, ctx) {
111
- return template
112
- .replace("%(api)s", ctx.api)
113
- .replace("%(remainMs)s", ctx.remainMs.toString())
114
- .replace("%(remain)s", ctx.remain)
115
- .replace("%(level)s", ctx.level?.toString() ?? "1")
116
- .replace("%(limit)s", ctx.limit?.toString() ?? "");
117
- }
118
120
  }
119
121
  exports.Limiter = Limiter;
package/dist/types.d.ts CHANGED
@@ -12,12 +12,17 @@ export interface DurationInput {
12
12
  }
13
13
  export interface RateLimiterOptions {
14
14
  keyPrefix: string;
15
- limit: number;
15
+ limit: number | readonly [number, ...number[]];
16
16
  windowMs: DurationInput;
17
- floodwaitMs: DurationInput | DurationInput[];
17
+ floodwaitMs: DurationInput | readonly [DurationInput, ...DurationInput[]];
18
18
  identifyUser?: (req: Request, res: Response) => string | Response<any, Record<string, any>>;
19
19
  store?: RateLimitStore;
20
20
  refreshAt?: DurationInput;
21
- messageFormat?: string extends FloodwaitPlaceholder ? string : never;
21
+ messageFormat?: string | ((ctx: {
22
+ api: string;
23
+ remainMs: number;
24
+ remain: string;
25
+ level?: number;
26
+ limit?: number;
27
+ }) => string);
22
28
  }
23
- export type FloodwaitPlaceholder = "%(api)s" | "%(remainMs)s" | "%(remain)s" | "%(level)s" | "%(limit)s";
package/package.json CHANGED
@@ -1,30 +1,30 @@
1
1
  {
2
2
  "name": "ayiin",
3
- "version": "2.0.40",
4
- "description": "Library Pribadi Yang Gak Ada Isinya",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
7
- "bin": {
8
- "axd": "./dist/bin/cli.js"
9
- },
10
- "scripts": {
11
- "build": "tsc"
3
+ "version": "2.0.42",
4
+ "author": {
5
+ "name": "AyiinXd",
6
+ "url": "https://github.com/AyiinXd"
12
7
  },
13
8
  "repository": {
14
9
  "type": "git",
15
10
  "url": "git+https://github.com/AyiinXd/ayiin.git"
16
11
  },
17
- "author": {
18
- "name": "AyiinXd",
19
- "url": "https://github.com/AyiinXd"
12
+ "main": "./dist/index.js",
13
+ "devDependencies": {
14
+ "@types/express": "^5.0.6",
15
+ "@types/node": "^26.3.0",
16
+ "typescript": "^7"
20
17
  },
18
+ "bin": {
19
+ "axd": "./dist/bin/cli.js"
20
+ },
21
+ "description": "Library Pribadi Yang Gak Ada Isinya",
21
22
  "keywords": [
22
23
  "ayiin"
23
24
  ],
24
25
  "license": "GPL-3.0-only",
25
- "devDependencies": {
26
- "@types/express": "^5.0.6",
27
- "@types/node": "^26.3.0",
28
- "typescript": "^7"
29
- }
26
+ "scripts": {
27
+ "build": "tsc"
28
+ },
29
+ "types": "./dist/index.d.ts"
30
30
  }