@joint-ops/hitlimit 1.0.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +396 -0
  3. package/dist/core/config.d.ts +3 -0
  4. package/dist/core/config.d.ts.map +1 -0
  5. package/dist/core/config.js +20 -0
  6. package/dist/core/config.js.map +1 -0
  7. package/dist/core/headers.d.ts +3 -0
  8. package/dist/core/headers.d.ts.map +1 -0
  9. package/dist/core/headers.js +18 -0
  10. package/dist/core/headers.js.map +1 -0
  11. package/dist/core/limiter.d.ts +3 -0
  12. package/dist/core/limiter.d.ts.map +1 -0
  13. package/dist/core/limiter.js +48 -0
  14. package/dist/core/limiter.js.map +1 -0
  15. package/dist/core/response.d.ts +3 -0
  16. package/dist/core/response.d.ts.map +1 -0
  17. package/dist/core/response.js +12 -0
  18. package/dist/core/response.js.map +1 -0
  19. package/dist/core/utils.d.ts +3 -0
  20. package/dist/core/utils.d.ts.map +1 -0
  21. package/dist/core/utils.js +19 -0
  22. package/dist/core/utils.js.map +1 -0
  23. package/dist/index.d.ts +7 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +40 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/loggers/console.d.ts +4 -0
  28. package/dist/loggers/console.d.ts.map +1 -0
  29. package/dist/loggers/console.js +17 -0
  30. package/dist/loggers/console.js.map +1 -0
  31. package/dist/loggers/pino.d.ts +4 -0
  32. package/dist/loggers/pino.d.ts.map +1 -0
  33. package/dist/loggers/pino.js +10 -0
  34. package/dist/loggers/pino.js.map +1 -0
  35. package/dist/loggers/winston.d.ts +4 -0
  36. package/dist/loggers/winston.d.ts.map +1 -0
  37. package/dist/loggers/winston.js +9 -0
  38. package/dist/loggers/winston.js.map +1 -0
  39. package/dist/nest.d.ts +27 -0
  40. package/dist/nest.d.ts.map +1 -0
  41. package/dist/nest.js +114 -0
  42. package/dist/nest.js.map +1 -0
  43. package/dist/node.d.ts +10 -0
  44. package/dist/node.d.ts.map +1 -0
  45. package/dist/node.js +50 -0
  46. package/dist/node.js.map +1 -0
  47. package/dist/stores/memory.d.ts +3 -0
  48. package/dist/stores/memory.d.ts.map +1 -0
  49. package/dist/stores/memory.js +37 -0
  50. package/dist/stores/memory.js.map +1 -0
  51. package/dist/stores/redis.d.ts +7 -0
  52. package/dist/stores/redis.d.ts.map +1 -0
  53. package/dist/stores/redis.js +36 -0
  54. package/dist/stores/redis.js.map +1 -0
  55. package/dist/stores/sqlite.d.ts +6 -0
  56. package/dist/stores/sqlite.d.ts.map +1 -0
  57. package/dist/stores/sqlite.js +48 -0
  58. package/dist/stores/sqlite.js.map +1 -0
  59. package/package.json +160 -0
@@ -0,0 +1,4 @@
1
+ import type { HitLimitLogger } from '@joint-ops/hitlimit-types';
2
+ export declare function consoleLogger(): HitLimitLogger;
3
+ export declare function silentLogger(): HitLimitLogger;
4
+ //# sourceMappingURL=console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/loggers/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE/D,wBAAgB,aAAa,IAAI,cAAc,CAO9C;AAED,wBAAgB,YAAY,IAAI,cAAc,CAO7C"}
@@ -0,0 +1,17 @@
1
+ export function consoleLogger() {
2
+ return {
3
+ debug: (msg, meta) => console.debug(`[hitlimit] ${msg}`, meta),
4
+ info: (msg, meta) => console.info(`[hitlimit] ${msg}`, meta),
5
+ warn: (msg, meta) => console.warn(`[hitlimit] ${msg}`, meta),
6
+ error: (msg, meta) => console.error(`[hitlimit] ${msg}`, meta)
7
+ };
8
+ }
9
+ export function silentLogger() {
10
+ return {
11
+ debug: () => { },
12
+ info: () => { },
13
+ warn: () => { },
14
+ error: () => { }
15
+ };
16
+ }
17
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/loggers/console.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC;QAC9D,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC;QAC5D,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC;QAC5D,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC;KAC/D,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;QACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;QACd,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;QACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;KAChB,CAAA;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Logger as PinoLogger } from 'pino';
2
+ import type { HitLimitLogger } from '@joint-ops/hitlimit-types';
3
+ export declare function pinoLogger(pino: PinoLogger): HitLimitLogger;
4
+ //# sourceMappingURL=pino.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pino.d.ts","sourceRoot":"","sources":["../../src/loggers/pino.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE/D,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAQ3D"}
@@ -0,0 +1,10 @@
1
+ export function pinoLogger(pino) {
2
+ const child = pino.child({ component: 'hitlimit' });
3
+ return {
4
+ debug: (msg, meta) => child.debug(meta, msg),
5
+ info: (msg, meta) => child.info(meta, msg),
6
+ warn: (msg, meta) => child.warn(meta, msg),
7
+ error: (msg, meta) => child.error(meta, msg)
8
+ };
9
+ }
10
+ //# sourceMappingURL=pino.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pino.js","sourceRoot":"","sources":["../../src/loggers/pino.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,UAAU,CAAC,IAAgB;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAA;IACnD,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC;QAC5C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;QAC1C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;QAC1C,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC;KAC7C,CAAA;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Logger as WinstonLogger } from 'winston';
2
+ import type { HitLimitLogger } from '@joint-ops/hitlimit-types';
3
+ export declare function winstonLogger(winston: WinstonLogger): HitLimitLogger;
4
+ //# sourceMappingURL=winston.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"winston.d.ts","sourceRoot":"","sources":["../../src/loggers/winston.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,SAAS,CAAA;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE/D,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,cAAc,CAOpE"}
@@ -0,0 +1,9 @@
1
+ export function winstonLogger(winston) {
2
+ return {
3
+ debug: (msg, meta) => winston.debug(msg, { ...meta, component: 'hitlimit' }),
4
+ info: (msg, meta) => winston.info(msg, { ...meta, component: 'hitlimit' }),
5
+ warn: (msg, meta) => winston.warn(msg, { ...meta, component: 'hitlimit' }),
6
+ error: (msg, meta) => winston.error(msg, { ...meta, component: 'hitlimit' })
7
+ };
8
+ }
9
+ //# sourceMappingURL=winston.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"winston.js","sourceRoot":"","sources":["../../src/loggers/winston.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,aAAa,CAAC,OAAsB;IAClD,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;QAC5E,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;QAC1E,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;QAC1E,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;KAC7E,CAAA;AACH,CAAC"}
package/dist/nest.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { DynamicModule, CanActivate, ExecutionContext, type Type } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import type { Request } from 'express';
4
+ import type { HitLimitOptions } from '@joint-ops/hitlimit-types';
5
+ export declare const HITLIMIT_OPTIONS = "HITLIMIT_OPTIONS";
6
+ export declare const HITLIMIT_ROUTE_OPTIONS = "HITLIMIT_ROUTE_OPTIONS";
7
+ export interface HitLimitModuleOptions extends HitLimitOptions<Request> {
8
+ }
9
+ export interface HitLimitModuleAsyncOptions {
10
+ imports?: Type<any>[];
11
+ inject?: any[];
12
+ useFactory: (...args: any[]) => Promise<HitLimitModuleOptions> | HitLimitModuleOptions;
13
+ }
14
+ export declare class HitLimitGuard implements CanActivate {
15
+ private options;
16
+ private config;
17
+ private store;
18
+ private reflector;
19
+ constructor(options: HitLimitModuleOptions, reflector?: Reflector);
20
+ canActivate(context: ExecutionContext): Promise<boolean>;
21
+ }
22
+ export declare function HitLimit(options: HitLimitOptions<Request>): MethodDecorator;
23
+ export declare class HitLimitModule {
24
+ static register(options?: HitLimitModuleOptions): DynamicModule;
25
+ static registerAsync(options: HitLimitModuleAsyncOptions): DynamicModule;
26
+ }
27
+ //# sourceMappingURL=nest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nest.d.ts","sourceRoot":"","sources":["../src/nest.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEb,WAAW,EACX,gBAAgB,EAIhB,KAAK,IAAI,EACV,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAY,MAAM,SAAS,CAAA;AAChD,OAAO,KAAK,EAAE,eAAe,EAAiC,MAAM,2BAA2B,CAAA;AAK/F,eAAO,MAAM,gBAAgB,qBAAqB,CAAA;AAClD,eAAO,MAAM,sBAAsB,2BAA2B,CAAA;AAE9D,MAAM,WAAW,qBAAsB,SAAQ,eAAe,CAAC,OAAO,CAAC;CAAG;AAE1E,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;IACrB,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,qBAAqB,CAAC,GAAG,qBAAqB,CAAA;CACvF;AAMD,qBACa,aAAc,YAAW,WAAW;IAMnB,OAAO,CAAC,OAAO;IAL3C,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,SAAS,CAAW;gBAGQ,OAAO,EAAE,qBAAqB,EACpD,SAAS,CAAC,EAAE,SAAS;IAO7B,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CA+C/D;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,eAAe,CAE3E;AAED,qBACa,cAAc;IACzB,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAE,qBAA0B,GAAG,aAAa;IAcnE,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,0BAA0B,GAAG,aAAa;CAezE"}
package/dist/nest.js ADDED
@@ -0,0 +1,114 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ var HitLimitModule_1;
14
+ import { Module, Injectable, Inject, SetMetadata, Optional } from '@nestjs/common';
15
+ import { Reflector } from '@nestjs/core';
16
+ import { resolveConfig } from './core/config.js';
17
+ import { checkLimit } from './core/limiter.js';
18
+ import { memoryStore } from './stores/memory.js';
19
+ export const HITLIMIT_OPTIONS = 'HITLIMIT_OPTIONS';
20
+ export const HITLIMIT_ROUTE_OPTIONS = 'HITLIMIT_ROUTE_OPTIONS';
21
+ function getDefaultKey(req) {
22
+ return req.ip || req.socket?.remoteAddress || 'unknown';
23
+ }
24
+ let HitLimitGuard = class HitLimitGuard {
25
+ options;
26
+ config;
27
+ store;
28
+ reflector;
29
+ constructor(options, reflector) {
30
+ this.options = options;
31
+ this.reflector = reflector || new Reflector();
32
+ this.store = options.store ?? memoryStore();
33
+ this.config = resolveConfig(options, this.store, getDefaultKey);
34
+ }
35
+ async canActivate(context) {
36
+ const request = context.switchToHttp().getRequest();
37
+ const response = context.switchToHttp().getResponse();
38
+ const routeOptions = this.reflector.get(HITLIMIT_ROUTE_OPTIONS, context.getHandler());
39
+ let config = this.config;
40
+ if (routeOptions) {
41
+ config = resolveConfig({ ...this.options, ...routeOptions }, routeOptions.store ?? this.store, routeOptions.key ?? getDefaultKey);
42
+ }
43
+ if (config.skip) {
44
+ const shouldSkip = await config.skip(request);
45
+ if (shouldSkip) {
46
+ return true;
47
+ }
48
+ }
49
+ try {
50
+ const result = await checkLimit(config, request);
51
+ Object.entries(result.headers).forEach(([key, value]) => {
52
+ response.setHeader(key, value);
53
+ });
54
+ if (!result.allowed) {
55
+ response.status(429).json(result.body);
56
+ return false;
57
+ }
58
+ return true;
59
+ }
60
+ catch (error) {
61
+ const action = await config.onStoreError(error, request);
62
+ if (action === 'deny') {
63
+ response.status(429).json({ hitlimit: true, message: 'Rate limit error' });
64
+ return false;
65
+ }
66
+ return true;
67
+ }
68
+ }
69
+ };
70
+ HitLimitGuard = __decorate([
71
+ Injectable(),
72
+ __param(0, Inject(HITLIMIT_OPTIONS)),
73
+ __param(1, Optional()),
74
+ __metadata("design:paramtypes", [Object, Reflector])
75
+ ], HitLimitGuard);
76
+ export { HitLimitGuard };
77
+ export function HitLimit(options) {
78
+ return SetMetadata(HITLIMIT_ROUTE_OPTIONS, options);
79
+ }
80
+ let HitLimitModule = HitLimitModule_1 = class HitLimitModule {
81
+ static register(options = {}) {
82
+ return {
83
+ module: HitLimitModule_1,
84
+ providers: [
85
+ {
86
+ provide: HITLIMIT_OPTIONS,
87
+ useValue: options
88
+ },
89
+ HitLimitGuard
90
+ ],
91
+ exports: [HITLIMIT_OPTIONS, HitLimitGuard]
92
+ };
93
+ }
94
+ static registerAsync(options) {
95
+ return {
96
+ module: HitLimitModule_1,
97
+ imports: options.imports || [],
98
+ providers: [
99
+ {
100
+ provide: HITLIMIT_OPTIONS,
101
+ useFactory: options.useFactory,
102
+ inject: options.inject || []
103
+ },
104
+ HitLimitGuard
105
+ ],
106
+ exports: [HITLIMIT_OPTIONS, HitLimitGuard]
107
+ };
108
+ }
109
+ };
110
+ HitLimitModule = HitLimitModule_1 = __decorate([
111
+ Module({})
112
+ ], HitLimitModule);
113
+ export { HitLimitModule };
114
+ //# sourceMappingURL=nest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nest.js","sourceRoot":"","sources":["../src/nest.ts"],"names":[],"mappings":"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"}
package/dist/node.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { IncomingMessage } from 'http';
2
+ import type { HitLimitOptions, HitLimitResult } from '@joint-ops/hitlimit-types';
3
+ export type { HitLimitOptions, HitLimitInfo, HitLimitResult, HitLimitStore, StoreResult } from '@joint-ops/hitlimit-types';
4
+ export { memoryStore } from './stores/memory.js';
5
+ export interface HitLimiter {
6
+ check(req: IncomingMessage): Promise<HitLimitResult>;
7
+ reset(key: string): Promise<void> | void;
8
+ }
9
+ export declare function createHitLimit(options?: HitLimitOptions<IncomingMessage>): HitLimiter;
10
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAKhF,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAC1H,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAMhD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IACpD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CACzC;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAe,CAAC,eAAe,CAAM,GAAG,UAAU,CA2CzF"}
package/dist/node.js ADDED
@@ -0,0 +1,50 @@
1
+ import { resolveConfig } from './core/config.js';
2
+ import { checkLimit } from './core/limiter.js';
3
+ import { memoryStore } from './stores/memory.js';
4
+ export { memoryStore } from './stores/memory.js';
5
+ function getDefaultKey(req) {
6
+ return req.socket?.remoteAddress || 'unknown';
7
+ }
8
+ export function createHitLimit(options = {}) {
9
+ const store = options.store ?? memoryStore();
10
+ const config = resolveConfig(options, store, getDefaultKey);
11
+ return {
12
+ async check(req) {
13
+ if (config.skip) {
14
+ const shouldSkip = await config.skip(req);
15
+ if (shouldSkip) {
16
+ return {
17
+ allowed: true,
18
+ info: { limit: config.limit, remaining: config.limit, resetIn: 0, resetAt: 0, key: '' },
19
+ headers: {},
20
+ body: {}
21
+ };
22
+ }
23
+ }
24
+ try {
25
+ return await checkLimit(config, req);
26
+ }
27
+ catch (error) {
28
+ const action = await config.onStoreError(error, req);
29
+ if (action === 'deny') {
30
+ return {
31
+ allowed: false,
32
+ info: { limit: config.limit, remaining: 0, resetIn: 0, resetAt: 0, key: '' },
33
+ headers: {},
34
+ body: { hitlimit: true, message: 'Rate limit error' }
35
+ };
36
+ }
37
+ return {
38
+ allowed: true,
39
+ info: { limit: config.limit, remaining: config.limit, resetIn: 0, resetAt: 0, key: '' },
40
+ headers: {},
41
+ body: {}
42
+ };
43
+ }
44
+ },
45
+ reset(key) {
46
+ return store.reset(key);
47
+ }
48
+ };
49
+ }
50
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,SAAS,aAAa,CAAC,GAAoB;IACzC,OAAO,GAAG,CAAC,MAAM,EAAE,aAAa,IAAI,SAAS,CAAA;AAC/C,CAAC;AAOD,MAAM,UAAU,cAAc,CAAC,UAA4C,EAAE;IAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,WAAW,EAAE,CAAA;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;IAE3D,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,GAAoB;YAC9B,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAChB,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBACzC,IAAI,UAAU,EAAE,CAAC;oBACf,OAAO;wBACL,OAAO,EAAE,IAAI;wBACb,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;wBACvF,OAAO,EAAE,EAAE;wBACX,IAAI,EAAE,EAAE;qBACT,CAAA;gBACH,CAAC;YACH,CAAC;YAED,IAAI,CAAC;gBACH,OAAO,MAAM,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YACtC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,KAAc,EAAE,GAAG,CAAC,CAAA;gBAC7D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;oBACtB,OAAO;wBACL,OAAO,EAAE,KAAK;wBACd,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;wBAC5E,OAAO,EAAE,EAAE;wBACX,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,EAAE;qBACtD,CAAA;gBACH,CAAC;gBACD,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;oBACvF,OAAO,EAAE,EAAE;oBACX,IAAI,EAAE,EAAE;iBACT,CAAA;YACH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,GAAW;YACf,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACzB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { HitLimitStore } from '@joint-ops/hitlimit-types';
2
+ export declare function memoryStore(): HitLimitStore;
3
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAA;AAgD3E,wBAAgB,WAAW,IAAI,aAAa,CAE3C"}
@@ -0,0 +1,37 @@
1
+ class MemoryStore {
2
+ hits = new Map();
3
+ cleanupTimer;
4
+ constructor() {
5
+ this.cleanupTimer = setInterval(() => this.cleanup(), 60000);
6
+ }
7
+ hit(key, windowMs, _limit) {
8
+ const now = Date.now();
9
+ const entry = this.hits.get(key);
10
+ if (!entry || entry.resetAt <= now) {
11
+ const resetAt = now + windowMs;
12
+ this.hits.set(key, { count: 1, resetAt });
13
+ return { count: 1, resetAt };
14
+ }
15
+ entry.count++;
16
+ return { count: entry.count, resetAt: entry.resetAt };
17
+ }
18
+ reset(key) {
19
+ this.hits.delete(key);
20
+ }
21
+ shutdown() {
22
+ clearInterval(this.cleanupTimer);
23
+ this.hits.clear();
24
+ }
25
+ cleanup() {
26
+ const now = Date.now();
27
+ for (const [key, entry] of this.hits) {
28
+ if (entry.resetAt <= now) {
29
+ this.hits.delete(key);
30
+ }
31
+ }
32
+ }
33
+ }
34
+ export function memoryStore() {
35
+ return new MemoryStore();
36
+ }
37
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW;IACP,IAAI,GAAG,IAAI,GAAG,EAAiB,CAAA;IAC/B,YAAY,CAAgC;IAEpD;QACE,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAA;IAC9D,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAE,MAAc;QAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAEhC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,GAAG,GAAG,QAAQ,CAAA;YAC9B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;YACzC,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAA;QAC9B,CAAC;QAED,KAAK,CAAC,KAAK,EAAE,CAAA;QACb,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;IACvD,CAAC;IAED,KAAK,CAAC,GAAW;QACf,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACvB,CAAC;IAED,QAAQ;QACN,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAChC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;IACnB,CAAC;IAEO,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACrC,IAAI,KAAK,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;gBACzB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,WAAW,EAAE,CAAA;AAC1B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { HitLimitStore } from '@joint-ops/hitlimit-types';
2
+ export interface RedisStoreOptions {
3
+ url?: string;
4
+ keyPrefix?: string;
5
+ }
6
+ export declare function redisStore(options?: RedisStoreOptions): HitLimitStore;
7
+ //# sourceMappingURL=redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/stores/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAA;AAG3E,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA2CD,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAErE"}
@@ -0,0 +1,36 @@
1
+ import Redis from 'ioredis';
2
+ class RedisStore {
3
+ redis;
4
+ prefix;
5
+ constructor(options = {}) {
6
+ this.redis = new Redis(options.url ?? 'redis://localhost:6379');
7
+ this.prefix = options.keyPrefix ?? 'hitlimit:';
8
+ }
9
+ async hit(key, windowMs, _limit) {
10
+ const redisKey = this.prefix + key;
11
+ const now = Date.now();
12
+ const results = await this.redis
13
+ .multi()
14
+ .incr(redisKey)
15
+ .pttl(redisKey)
16
+ .exec();
17
+ const count = results[0][1];
18
+ let ttl = results[1][1];
19
+ if (ttl < 0) {
20
+ await this.redis.pexpire(redisKey, windowMs);
21
+ ttl = windowMs;
22
+ }
23
+ const resetAt = now + ttl;
24
+ return { count, resetAt };
25
+ }
26
+ async reset(key) {
27
+ await this.redis.del(this.prefix + key);
28
+ }
29
+ async shutdown() {
30
+ await this.redis.quit();
31
+ }
32
+ }
33
+ export function redisStore(options) {
34
+ return new RedisStore(options);
35
+ }
36
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/stores/redis.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,SAAS,CAAA;AAO3B,MAAM,UAAU;IACN,KAAK,CAAO;IACZ,MAAM,CAAQ;IAEtB,YAAY,UAA6B,EAAE;QACzC,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,wBAAwB,CAAC,CAAA;QAC/D,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,SAAS,IAAI,WAAW,CAAA;IAChD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAE,MAAc;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAA;QAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAEtB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK;aAC7B,KAAK,EAAE;aACP,IAAI,CAAC,QAAQ,CAAC;aACd,IAAI,CAAC,QAAQ,CAAC;aACd,IAAI,EAAE,CAAA;QAET,MAAM,KAAK,GAAG,OAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAW,CAAA;QACtC,IAAI,GAAG,GAAG,OAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAW,CAAA;QAElC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;YAC5C,GAAG,GAAG,QAAQ,CAAA;QAChB,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,GAAG,GAAG,CAAA;QAEzB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IAC3B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW;QACrB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAA;IACzC,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;IACzB,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,OAA2B;IACpD,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAAA;AAChC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { HitLimitStore } from '@joint-ops/hitlimit-types';
2
+ export interface SqliteStoreOptions {
3
+ path?: string;
4
+ }
5
+ export declare function sqliteStore(options?: SqliteStoreOptions): HitLimitStore;
6
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/stores/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAA;AAG3E,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAwDD,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,aAAa,CAEvE"}
@@ -0,0 +1,48 @@
1
+ import Database from 'better-sqlite3';
2
+ class SqliteStore {
3
+ db;
4
+ hitStmt;
5
+ getStmt;
6
+ resetStmt;
7
+ cleanupTimer;
8
+ constructor(options = {}) {
9
+ this.db = new Database(options.path ?? ':memory:');
10
+ this.db.pragma('journal_mode = WAL');
11
+ this.db.exec(`
12
+ CREATE TABLE IF NOT EXISTS hitlimit (
13
+ key TEXT PRIMARY KEY,
14
+ count INTEGER NOT NULL,
15
+ reset_at INTEGER NOT NULL
16
+ )
17
+ `);
18
+ this.hitStmt = this.db.prepare(`
19
+ INSERT INTO hitlimit (key, count, reset_at) VALUES (?, 1, ?)
20
+ ON CONFLICT(key) DO UPDATE SET
21
+ count = CASE WHEN reset_at <= ? THEN 1 ELSE count + 1 END,
22
+ reset_at = CASE WHEN reset_at <= ? THEN excluded.reset_at ELSE reset_at END
23
+ `);
24
+ this.getStmt = this.db.prepare('SELECT count, reset_at FROM hitlimit WHERE key = ?');
25
+ this.resetStmt = this.db.prepare('DELETE FROM hitlimit WHERE key = ?');
26
+ this.cleanupTimer = setInterval(() => {
27
+ this.db.prepare('DELETE FROM hitlimit WHERE reset_at <= ?').run(Date.now());
28
+ }, 60000);
29
+ }
30
+ hit(key, windowMs, _limit) {
31
+ const now = Date.now();
32
+ const resetAt = now + windowMs;
33
+ this.hitStmt.run(key, resetAt, now, now);
34
+ const row = this.getStmt.get(key);
35
+ return { count: row.count, resetAt: row.reset_at };
36
+ }
37
+ reset(key) {
38
+ this.resetStmt.run(key);
39
+ }
40
+ shutdown() {
41
+ clearInterval(this.cleanupTimer);
42
+ this.db.close();
43
+ }
44
+ }
45
+ export function sqliteStore(options) {
46
+ return new SqliteStore(options);
47
+ }
48
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/stores/sqlite.ts"],"names":[],"mappings":"AACA,OAAO,QAAQ,MAAM,gBAAgB,CAAA;AAMrC,MAAM,WAAW;IACP,EAAE,CAAmB;IACrB,OAAO,CAAoB;IAC3B,OAAO,CAAoB;IAC3B,SAAS,CAAoB;IAC7B,YAAY,CAAgC;IAEpD,YAAY,UAA8B,EAAE;QAC1C,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,UAAU,CAAC,CAAA;QAClD,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAA;QAEpC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;KAMZ,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;KAK9B,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAA;QACpF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAA;QAEtE,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;YACnC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC7E,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAE,MAAc;QAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,OAAO,GAAG,GAAG,GAAG,QAAQ,CAAA;QAE9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAwC,CAAA;QAExE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAA;IACpD,CAAC;IAED,KAAK,CAAC,GAAW;QACf,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IAED,QAAQ;QACN,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAChC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;IACjB,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,OAA4B;IACtD,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAA;AACjC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,160 @@
1
+ {
2
+ "name": "@joint-ops/hitlimit",
3
+ "version": "1.0.0",
4
+ "description": "Fast rate limiting middleware for Express, NestJS & Node.js - API throttling, brute force protection, request limiting",
5
+ "author": {
6
+ "name": "Shayan M Hussain",
7
+ "email": "shayanhussain48@gmail.com",
8
+ "url": "https://github.com/ShayanHussainSB"
9
+ },
10
+ "license": "MIT",
11
+ "homepage": "https://hitlimit.dev",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/JointOps/hitlimit-monorepo",
15
+ "directory": "packages/hitlimit"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/JointOps/hitlimit-monorepo/issues"
19
+ },
20
+ "keywords": [
21
+ "rate-limit",
22
+ "rate-limiter",
23
+ "rate-limiting",
24
+ "express",
25
+ "express-rate-limit",
26
+ "express-middleware",
27
+ "express-throttle",
28
+ "nestjs",
29
+ "nestjs-throttler",
30
+ "nestjs-rate-limit",
31
+ "nest-throttler",
32
+ "middleware",
33
+ "throttle",
34
+ "throttling",
35
+ "api",
36
+ "api-rate-limit",
37
+ "api-throttle",
38
+ "request-limit",
39
+ "request-throttling",
40
+ "ddos-protection",
41
+ "brute-force",
42
+ "brute-force-protection",
43
+ "login-protection",
44
+ "redis",
45
+ "redis-rate-limit",
46
+ "sqlite",
47
+ "better-sqlite3",
48
+ "typescript",
49
+ "nodejs",
50
+ "node-rate-limit",
51
+ "sliding-window",
52
+ "fixed-window",
53
+ "token-bucket",
54
+ "leaky-bucket",
55
+ "rate-limiter-flexible",
56
+ "express-slow-down",
57
+ "api-security",
58
+ "request-limiter",
59
+ "http-rate-limit"
60
+ ],
61
+ "type": "module",
62
+ "main": "./dist/index.js",
63
+ "module": "./dist/index.js",
64
+ "types": "./dist/index.d.ts",
65
+ "exports": {
66
+ ".": {
67
+ "types": "./dist/index.d.ts",
68
+ "import": "./dist/index.js"
69
+ },
70
+ "./nest": {
71
+ "types": "./dist/nest.d.ts",
72
+ "import": "./dist/nest.js"
73
+ },
74
+ "./node": {
75
+ "types": "./dist/node.d.ts",
76
+ "import": "./dist/node.js"
77
+ },
78
+ "./stores/sqlite": {
79
+ "types": "./dist/stores/sqlite.d.ts",
80
+ "import": "./dist/stores/sqlite.js"
81
+ },
82
+ "./stores/redis": {
83
+ "types": "./dist/stores/redis.d.ts",
84
+ "import": "./dist/stores/redis.js"
85
+ },
86
+ "./loggers/console": {
87
+ "types": "./dist/loggers/console.d.ts",
88
+ "import": "./dist/loggers/console.js"
89
+ },
90
+ "./loggers/pino": {
91
+ "types": "./dist/loggers/pino.d.ts",
92
+ "import": "./dist/loggers/pino.js"
93
+ },
94
+ "./loggers/winston": {
95
+ "types": "./dist/loggers/winston.d.ts",
96
+ "import": "./dist/loggers/winston.js"
97
+ }
98
+ },
99
+ "files": [
100
+ "dist"
101
+ ],
102
+ "sideEffects": false,
103
+ "scripts": {
104
+ "build": "tsc",
105
+ "clean": "rm -rf dist",
106
+ "test": "vitest run",
107
+ "test:watch": "vitest"
108
+ },
109
+ "dependencies": {
110
+ "@joint-ops/hitlimit-types": "workspace:*"
111
+ },
112
+ "peerDependencies": {
113
+ "@nestjs/common": ">=8.0.0",
114
+ "@nestjs/core": ">=8.0.0",
115
+ "better-sqlite3": ">=9.0.0",
116
+ "ioredis": ">=5.0.0",
117
+ "pino": ">=8.0.0",
118
+ "winston": ">=3.0.0"
119
+ },
120
+ "peerDependenciesMeta": {
121
+ "@nestjs/common": {
122
+ "optional": true
123
+ },
124
+ "@nestjs/core": {
125
+ "optional": true
126
+ },
127
+ "better-sqlite3": {
128
+ "optional": true
129
+ },
130
+ "ioredis": {
131
+ "optional": true
132
+ },
133
+ "pino": {
134
+ "optional": true
135
+ },
136
+ "winston": {
137
+ "optional": true
138
+ }
139
+ },
140
+ "devDependencies": {
141
+ "@nestjs/common": "^10.0.0",
142
+ "@nestjs/core": "^10.0.0",
143
+ "@nestjs/platform-express": "^10.0.0",
144
+ "@nestjs/testing": "^10.0.0",
145
+ "@types/better-sqlite3": "^7.6.0",
146
+ "@types/express": "^4.17.21",
147
+ "@types/node": "^20.0.0",
148
+ "@types/supertest": "^6.0.0",
149
+ "better-sqlite3": "^11.0.0",
150
+ "express": "^4.18.0",
151
+ "ioredis": "^5.3.0",
152
+ "pino": "^10.3.0",
153
+ "reflect-metadata": "^0.2.0",
154
+ "rxjs": "^7.8.0",
155
+ "supertest": "^7.0.0",
156
+ "typescript": "^5.3.0",
157
+ "vitest": "^2.0.0",
158
+ "winston": "^3.19.0"
159
+ }
160
+ }