saas-backend-kit 1.0.0 → 1.0.2

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 (58) hide show
  1. package/README.md +123 -344
  2. package/copy-dts.js +59 -0
  3. package/dist/auth/index.js +7 -2
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/auth/index.mjs +7 -2
  6. package/dist/auth/index.mjs.map +1 -1
  7. package/dist/config/index.js +6 -1
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/config/index.mjs +6 -1
  10. package/dist/config/index.mjs.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +232 -41
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +231 -42
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/logger/index.js +6 -1
  17. package/dist/logger/index.js.map +1 -1
  18. package/dist/logger/index.mjs +6 -1
  19. package/dist/logger/index.mjs.map +1 -1
  20. package/dist/notifications/index.js +6 -1
  21. package/dist/notifications/index.js.map +1 -1
  22. package/dist/notifications/index.mjs +6 -1
  23. package/dist/notifications/index.mjs.map +1 -1
  24. package/dist/queue/index.js +6 -1
  25. package/dist/queue/index.js.map +1 -1
  26. package/dist/queue/index.mjs +6 -1
  27. package/dist/queue/index.mjs.map +1 -1
  28. package/dist/rate-limit/index.js +7 -1
  29. package/dist/rate-limit/index.js.map +1 -1
  30. package/dist/rate-limit/index.mjs +7 -1
  31. package/dist/rate-limit/index.mjs.map +1 -1
  32. package/dist/response/index.js +51 -40
  33. package/dist/response/index.js.map +1 -1
  34. package/dist/response/index.mjs +51 -40
  35. package/dist/response/index.mjs.map +1 -1
  36. package/dist/upload/index.d.ts +57 -0
  37. package/dist/upload/index.js +344 -0
  38. package/dist/upload/index.js.map +1 -0
  39. package/dist/upload/index.mjs +334 -0
  40. package/dist/upload/index.mjs.map +1 -0
  41. package/jest-output.json +72 -0
  42. package/jest.config.js +19 -0
  43. package/package.json +20 -8
  44. package/saas-banner.svg +239 -0
  45. package/src/auth/jwt.ts +1 -1
  46. package/src/config/index.ts +5 -0
  47. package/src/index.ts +2 -0
  48. package/src/rate-limit/express.ts +1 -0
  49. package/src/response/index.ts +49 -40
  50. package/src/upload/index.ts +268 -0
  51. package/tests/auth.test.ts +134 -0
  52. package/tests/config.test.ts +36 -0
  53. package/tests/logger.test.ts +47 -0
  54. package/tests/notifications.test.ts +19 -0
  55. package/tests/rate-limit.test.ts +50 -0
  56. package/tests/upload.test.ts +33 -0
  57. package/tsconfig.test.json +14 -0
  58. package/tsup.config.ts +2 -1
@@ -0,0 +1,239 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 320" width="900" height="320">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#0a0a0f"/>
5
+ <stop offset="50%" style="stop-color:#0d1117"/>
6
+ <stop offset="100%" style="stop-color:#111827"/>
7
+ </linearGradient>
8
+ <linearGradient id="pipeGrad" x1="0%" y1="0%" x2="100%" y2="0%">
9
+ <stop offset="0%" style="stop-color:#26d97f;stop-opacity:0"/>
10
+ <stop offset="50%" style="stop-color:#26d97f;stop-opacity:1"/>
11
+ <stop offset="100%" style="stop-color:#26d97f;stop-opacity:0"/>
12
+ </linearGradient>
13
+ <linearGradient id="pipeGrad2" x1="0%" y1="0%" x2="100%" y2="0%">
14
+ <stop offset="0%" style="stop-color:#3178c6;stop-opacity:0"/>
15
+ <stop offset="50%" style="stop-color:#3178c6;stop-opacity:1"/>
16
+ <stop offset="100%" style="stop-color:#3178c6;stop-opacity:0"/>
17
+ </linearGradient>
18
+ <linearGradient id="pipeGrad3" x1="0%" y1="0%" x2="100%" y2="0%">
19
+ <stop offset="0%" style="stop-color:#ff9900;stop-opacity:0"/>
20
+ <stop offset="50%" style="stop-color:#ff9900;stop-opacity:1"/>
21
+ <stop offset="100%" style="stop-color:#ff9900;stop-opacity:0"/>
22
+ </linearGradient>
23
+ <filter id="glow-green">
24
+ <feGaussianBlur stdDeviation="4" result="blur"/>
25
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
26
+ </filter>
27
+ <filter id="glow-blue">
28
+ <feGaussianBlur stdDeviation="3" result="blur"/>
29
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
30
+ </filter>
31
+ <filter id="glow-soft">
32
+ <feGaussianBlur stdDeviation="2" result="blur"/>
33
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
34
+ </filter>
35
+ </defs>
36
+
37
+ <!-- Background -->
38
+ <rect width="900" height="320" fill="url(#bg)" rx="14"/>
39
+
40
+ <!-- Subtle dot-grid background -->
41
+ <pattern id="dots" x="0" y="0" width="24" height="24" patternUnits="userSpaceOnUse">
42
+ <circle cx="1" cy="1" r="0.8" fill="#1e293b" opacity="0.8"/>
43
+ </pattern>
44
+ <rect width="900" height="320" fill="url(#dots)" rx="14" opacity="0.6"/>
45
+
46
+ <!-- ===================== DATA PIPELINE VISUALIZATION ===================== -->
47
+ <!-- Horizontal pipeline backbone -->
48
+ <line x1="60" y1="160" x2="840" y2="160" stroke="#1e293b" stroke-width="2"/>
49
+
50
+ <!-- Animated data packet 1 - green -->
51
+ <rect x="0" y="154" width="36" height="12" rx="6" fill="url(#pipeGrad)" opacity="0.9">
52
+ <animate attributeName="x" values="-40;900" dur="3.2s" repeatCount="indefinite"/>
53
+ </rect>
54
+
55
+ <!-- Animated data packet 2 - blue -->
56
+ <rect x="0" y="154" width="28" height="12" rx="6" fill="url(#pipeGrad2)" opacity="0.85">
57
+ <animate attributeName="x" values="-40;900" dur="3.2s" begin="-1.1s" repeatCount="indefinite"/>
58
+ </rect>
59
+
60
+ <!-- Animated data packet 3 - orange -->
61
+ <rect x="0" y="154" width="22" height="12" rx="6" fill="url(#pipeGrad3)" opacity="0.8">
62
+ <animate attributeName="x" values="-40;900" dur="3.2s" begin="-2.0s" repeatCount="indefinite"/>
63
+ </rect>
64
+
65
+ <!-- ===================== MODULE NODES (pipeline stops) ===================== -->
66
+ <!-- Each node represents a feature of the library -->
67
+
68
+ <!-- Node 1: AUTH -->
69
+ <g filter="url(#glow-blue)">
70
+ <circle cx="130" cy="160" r="32" fill="#0d1117" stroke="#3178c6" stroke-width="1.8"/>
71
+ <circle cx="130" cy="160" r="32" fill="none" stroke="#3178c6" stroke-width="1">
72
+ <animate attributeName="r" values="32;40;32" dur="3s" repeatCount="indefinite"/>
73
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="3s" repeatCount="indefinite"/>
74
+ </circle>
75
+ <text x="130" y="155" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#3178c6" font-weight="bold">🔐</text>
76
+ <text x="130" y="168" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#3178c6" font-weight="bold">AUTH</text>
77
+ <text x="130" y="204" text-anchor="middle" font-family="'Courier New',monospace" font-size="8" fill="#3178c6" opacity="0.6">JWT · RBAC</text>
78
+ </g>
79
+
80
+ <!-- Connector line -->
81
+ <line x1="162" y1="160" x2="218" y2="160" stroke="#1e293b" stroke-width="1.5" stroke-dasharray="4,3"/>
82
+
83
+ <!-- Node 2: QUEUE -->
84
+ <g filter="url(#glow-green)">
85
+ <circle cx="250" cy="160" r="32" fill="#0d1117" stroke="#26d97f" stroke-width="1.8"/>
86
+ <circle cx="250" cy="160" r="32" fill="none" stroke="#26d97f" stroke-width="1">
87
+ <animate attributeName="r" values="32;40;32" dur="3.5s" begin="-0.8s" repeatCount="indefinite"/>
88
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="3.5s" begin="-0.8s" repeatCount="indefinite"/>
89
+ </circle>
90
+ <text x="250" y="155" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#26d97f" font-weight="bold">⚡</text>
91
+ <text x="250" y="168" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#26d97f" font-weight="bold">QUEUE</text>
92
+ <text x="250" y="204" text-anchor="middle" font-family="'Courier New',monospace" font-size="8" fill="#26d97f" opacity="0.6">BullMQ</text>
93
+ </g>
94
+
95
+ <!-- Connector line -->
96
+ <line x1="282" y1="160" x2="338" y2="160" stroke="#1e293b" stroke-width="1.5" stroke-dasharray="4,3"/>
97
+
98
+ <!-- Node 3: NOTIFY -->
99
+ <g filter="url(#glow-soft)">
100
+ <circle cx="370" cy="160" r="32" fill="#0d1117" stroke="#f59e0b" stroke-width="1.8"/>
101
+ <circle cx="370" cy="160" r="32" fill="none" stroke="#f59e0b" stroke-width="1">
102
+ <animate attributeName="r" values="32;40;32" dur="4s" begin="-1.5s" repeatCount="indefinite"/>
103
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="4s" begin="-1.5s" repeatCount="indefinite"/>
104
+ </circle>
105
+ <text x="370" y="155" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#f59e0b" font-weight="bold">🔔</text>
106
+ <text x="370" y="168" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#f59e0b" font-weight="bold">NOTIFY</text>
107
+ <text x="370" y="204" text-anchor="middle" font-family="'Courier New',monospace" font-size="8" fill="#f59e0b" opacity="0.6">Email · SMS</text>
108
+ </g>
109
+
110
+ <!-- Connector line -->
111
+ <line x1="402" y1="160" x2="458" y2="160" stroke="#1e293b" stroke-width="1.5" stroke-dasharray="4,3"/>
112
+
113
+ <!-- Node 4: UPLOAD (CENTER - largest) -->
114
+ <g filter="url(#glow-soft)">
115
+ <circle cx="490" cy="160" r="36" fill="#0d1117" stroke="#ff9900" stroke-width="2"/>
116
+ <circle cx="490" cy="160" r="36" fill="none" stroke="#ff9900" stroke-width="1.2">
117
+ <animate attributeName="r" values="36;48;36" dur="3s" begin="-0.4s" repeatCount="indefinite"/>
118
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="3s" begin="-0.4s" repeatCount="indefinite"/>
119
+ </circle>
120
+ <text x="490" y="154" text-anchor="middle" font-family="'Courier New',monospace" font-size="10" fill="#ff9900" font-weight="bold">☁️</text>
121
+ <text x="490" y="168" text-anchor="middle" font-family="'Courier New',monospace" font-size="10" fill="#ff9900" font-weight="bold">S3</text>
122
+ <text x="490" y="208" text-anchor="middle" font-family="'Courier New',monospace" font-size="8" fill="#ff9900" opacity="0.6">AWS Upload</text>
123
+ </g>
124
+
125
+ <!-- Connector line -->
126
+ <line x1="526" y1="160" x2="578" y2="160" stroke="#1e293b" stroke-width="1.5" stroke-dasharray="4,3"/>
127
+
128
+ <!-- Node 5: RATE LIMIT -->
129
+ <g filter="url(#glow-soft)">
130
+ <circle cx="610" cy="160" r="32" fill="#0d1117" stroke="#e11d48" stroke-width="1.8"/>
131
+ <circle cx="610" cy="160" r="32" fill="none" stroke="#e11d48" stroke-width="1">
132
+ <animate attributeName="r" values="32;40;32" dur="2.8s" begin="-2.1s" repeatCount="indefinite"/>
133
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="2.8s" begin="-2.1s" repeatCount="indefinite"/>
134
+ </circle>
135
+ <text x="610" y="155" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#e11d48" font-weight="bold">🛡️</text>
136
+ <text x="610" y="168" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#e11d48" font-weight="bold">LIMIT</text>
137
+ <text x="610" y="204" text-anchor="middle" font-family="'Courier New',monospace" font-size="8" fill="#e11d48" opacity="0.6">Rate Guard</text>
138
+ </g>
139
+
140
+ <!-- Connector line -->
141
+ <line x1="642" y1="160" x2="698" y2="160" stroke="#1e293b" stroke-width="1.5" stroke-dasharray="4,3"/>
142
+
143
+ <!-- Node 6: LOGGER -->
144
+ <g filter="url(#glow-soft)">
145
+ <circle cx="730" cy="160" r="32" fill="#0d1117" stroke="#8b5cf6" stroke-width="1.8"/>
146
+ <circle cx="730" cy="160" r="32" fill="none" stroke="#8b5cf6" stroke-width="1">
147
+ <animate attributeName="r" values="32;40;32" dur="3.8s" begin="-1.0s" repeatCount="indefinite"/>
148
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="3.8s" begin="-1.0s" repeatCount="indefinite"/>
149
+ </circle>
150
+ <text x="730" y="155" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#8b5cf6" font-weight="bold">📋</text>
151
+ <text x="730" y="168" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#8b5cf6" font-weight="bold">LOGGER</text>
152
+ <text x="730" y="204" text-anchor="middle" font-family="'Courier New',monospace" font-size="8" fill="#8b5cf6" opacity="0.6">Pino · JSON</text>
153
+ </g>
154
+
155
+ <!-- Connector line -->
156
+ <line x1="762" y1="160" x2="818" y2="160" stroke="#1e293b" stroke-width="1.5" stroke-dasharray="4,3"/>
157
+
158
+ <!-- Node 7: CONFIG -->
159
+ <g filter="url(#glow-soft)">
160
+ <circle cx="845" cy="160" r="28" fill="#0d1117" stroke="#06b6d4" stroke-width="1.8"/>
161
+ <circle cx="845" cy="160" r="28" fill="none" stroke="#06b6d4" stroke-width="1">
162
+ <animate attributeName="r" values="28;36;28" dur="3.2s" begin="-2.6s" repeatCount="indefinite"/>
163
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="3.2s" begin="-2.6s" repeatCount="indefinite"/>
164
+ </circle>
165
+ <text x="845" y="155" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#06b6d4" font-weight="bold">⚙️</text>
166
+ <text x="845" y="168" text-anchor="middle" font-family="'Courier New',monospace" font-size="9" fill="#06b6d4" font-weight="bold">CFG</text>
167
+ <text x="845" y="204" text-anchor="middle" font-family="'Courier New',monospace" font-size="8" fill="#06b6d4" opacity="0.6">Zod · Env</text>
168
+ </g>
169
+
170
+ <!-- ===================== TITLE + SUBTITLE ===================== -->
171
+ <text x="450" y="50" text-anchor="middle" font-family="'Courier New',monospace" font-size="28" font-weight="bold" fill="#f8fafc" letter-spacing="2" filter="url(#glow-soft)">
172
+ saas-backend-kit
173
+ <animate attributeName="opacity" values="0.85;1;0.85" dur="4s" repeatCount="indefinite"/>
174
+ </text>
175
+
176
+ <text x="450" y="76" text-anchor="middle" font-family="'Courier New',monospace" font-size="12" fill="#64748b">
177
+ Production-grade modular backend toolkit · Node.js · Express · Fastify
178
+ </text>
179
+
180
+ <!-- npm install command badge — animated border -->
181
+ <rect x="310" y="88" width="280" height="30" rx="6" fill="#0d1117" stroke="#26d97f" stroke-width="1" opacity="0.9">
182
+ <animate attributeName="stroke-opacity" values="0.4;1;0.4" dur="2s" repeatCount="indefinite"/>
183
+ </rect>
184
+ <text x="450" y="108" text-anchor="middle" font-family="'Courier New',monospace" font-size="12" fill="#26d97f">
185
+ $ npm install saas-backend-kit
186
+ </text>
187
+
188
+ <!-- ===================== FLOATING PARTICLES ===================== -->
189
+ <circle cx="50" cy="50" r="2" fill="#26d97f" opacity="0.5">
190
+ <animate attributeName="cy" values="50;270;50" dur="7s" repeatCount="indefinite"/>
191
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="7s" repeatCount="indefinite"/>
192
+ </circle>
193
+ <circle cx="870" cy="90" r="1.5" fill="#3178c6" opacity="0.5">
194
+ <animate attributeName="cy" values="90;30;90" dur="5s" repeatCount="indefinite"/>
195
+ <animate attributeName="opacity" values="0.5;0;0.5" dur="5s" repeatCount="indefinite"/>
196
+ </circle>
197
+ <circle cx="200" cy="280" r="2" fill="#f59e0b" opacity="0.4">
198
+ <animate attributeName="cx" values="200;700;200" dur="11s" repeatCount="indefinite"/>
199
+ <animate attributeName="opacity" values="0.4;0;0.4" dur="11s" repeatCount="indefinite"/>
200
+ </circle>
201
+ <circle cx="750" cy="30" r="1.5" fill="#8b5cf6" opacity="0.45">
202
+ <animate attributeName="cx" values="750;200;750" dur="9s" repeatCount="indefinite"/>
203
+ <animate attributeName="opacity" values="0.45;0;0.45" dur="9s" repeatCount="indefinite"/>
204
+ </circle>
205
+
206
+ <!-- ===================== BOTTOM STATUS BAR ===================== -->
207
+ <rect x="0" y="285" width="900" height="35" fill="#080c10" rx="0"/>
208
+ <rect x="0" y="282" width="900" height="3" fill="#26d97f" opacity="0.25"/>
209
+
210
+ <!-- Status indicators -->
211
+ <circle cx="30" cy="302" r="5" fill="#26d97f">
212
+ <animate attributeName="opacity" values="1;0.3;1" dur="1.5s" repeatCount="indefinite"/>
213
+ </circle>
214
+ <text x="42" y="307" font-family="'Courier New',monospace" font-size="10" fill="#26d97f" opacity="0.8">LIVE</text>
215
+
216
+ <text x="90" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b">|</text>
217
+ <text x="100" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b" opacity="0.7">MIT Licensed</text>
218
+
219
+ <text x="210" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b">|</text>
220
+ <text x="220" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b" opacity="0.7">TypeScript Ready</text>
221
+
222
+ <text x="360" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b">|</text>
223
+ <text x="370" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b" opacity="0.7">7 Modular Plugins</text>
224
+
225
+ <text x="520" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b">|</text>
226
+ <text x="530" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b" opacity="0.7">Express · Fastify</text>
227
+
228
+ <text x="670" y="307" font-family="'Courier New',monospace" font-size="10" fill="#64748b">|</text>
229
+ <text x="680" y="307" font-family="'Courier New',monospace" font-size="10" fill="#3178c6" opacity="0.85">by @AshishK-M</text>
230
+
231
+ <!-- Scrolling ticker on right -->
232
+ <clipPath id="tickerClip">
233
+ <rect x="800" y="288" width="95" height="28"/>
234
+ </clipPath>
235
+ <text font-family="'Courier New',monospace" font-size="9" fill="#26d97f" opacity="0.6" clip-path="url(#tickerClip)">
236
+ <tspan x="900" y="305">auth · queue · notify · upload · logger · rateLimit · config</tspan>
237
+ <animate attributeName="x" values="900;600" dur="8s" repeatCount="indefinite"/>
238
+ </text>
239
+ </svg>
package/src/auth/jwt.ts CHANGED
@@ -39,7 +39,7 @@ export class JWTService {
39
39
  }
40
40
 
41
41
  refreshTokens(refreshToken: string): TokenPair {
42
- const payload = this.verifyRefreshToken(refreshToken);
42
+ const { iat, exp, nbf, ...payload } = this.verifyRefreshToken(refreshToken) as JWTPayload & { iat?: number; exp?: number; nbf?: number };
43
43
  return this.generateTokenPair(payload);
44
44
  }
45
45
  }
@@ -24,6 +24,11 @@ export const envSchema = z.object({
24
24
  RATE_LIMIT_WINDOW: z.string().default('1m'),
25
25
  RATE_LIMIT_LIMIT: z.string().default('100'),
26
26
  LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),
27
+ AWS_REGION: z.string().default('us-east-1'),
28
+ AWS_ACCESS_KEY_ID: z.string().optional(),
29
+ AWS_SECRET_ACCESS_KEY: z.string().optional(),
30
+ AWS_S3_BUCKET: z.string().optional(),
31
+ AWS_ENDPOINT: z.string().optional(),
27
32
  });
28
33
 
29
34
  export type EnvConfig = z.infer<typeof envSchema>;
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export { logger } from './logger';
5
5
  export { rateLimit, createRateLimiter } from './rate-limit';
6
6
  export { config } from './config';
7
7
  export { ResponseHelper, response } from './response';
8
+ export { upload, s3Service, S3Service } from './upload';
8
9
  export { createApp, createExpressApp, SaaSAppBuilder, PluginManager, Plugin, AppOptions } from './plugin';
9
10
 
10
11
  export { AuthOptions, User, JWTPayload, TokenPair, LoginCredentials, RegisterData, Role, Permission, RolePermissions } from './auth/types';
@@ -14,3 +15,4 @@ export { LoggerConfig, LogLevel } from './logger';
14
15
  export { RateLimitOptions } from './rate-limit';
15
16
  export { EnvConfig, ConfigOptions } from './config';
16
17
  export { ApiResponse, PaginatedResponse, ErrorResponse } from './response';
18
+ export { S3Config, UploadOptions, UploadResult, SignedUrlOptions, FileObject } from './upload';
@@ -22,6 +22,7 @@ class InMemoryRateLimiter {
22
22
 
23
23
  constructor() {
24
24
  this.cleanupInterval = setInterval(() => this.cleanup(), 60000);
25
+ this.cleanupInterval.unref();
25
26
  }
26
27
 
27
28
  private cleanup(): void {
@@ -13,7 +13,8 @@ export interface ApiResponse<T = unknown> {
13
13
  };
14
14
  }
15
15
 
16
- export interface PaginatedResponse<T> extends ApiResponse<T> {
16
+ export interface PaginatedResponse<T> extends Omit<ApiResponse<T[]>, 'data'> {
17
+ data: T[];
17
18
  meta: {
18
19
  page: number;
19
20
  limit: number;
@@ -141,57 +142,65 @@ declare global {
141
142
  }
142
143
  }
143
144
 
144
- Response.prototype.success = function <T>(data?: T, message?: string, statusCode: number = 200) {
145
- return ResponseHelper.success(this, data, message, statusCode);
146
- };
145
+ try {
146
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
147
+ const proto = require('express').response;
148
+ if (proto) {
149
+ proto.success = function <T>(this: Response, data?: T, message?: string, statusCode: number = 200) {
150
+ return ResponseHelper.success(this, data, message, statusCode);
151
+ };
147
152
 
148
- Response.prototype.created = function <T>(data?: T, message?: string) {
149
- return ResponseHelper.created(this, data, message);
150
- };
153
+ proto.created = function <T>(this: Response, data?: T, message?: string) {
154
+ return ResponseHelper.created(this, data, message);
155
+ };
151
156
 
152
- Response.prototype.updated = function <T>(data?: T, message?: string) {
153
- return ResponseHelper.updated(this, data, message);
154
- };
157
+ proto.updated = function <T>(this: Response, data?: T, message?: string) {
158
+ return ResponseHelper.updated(this, data, message);
159
+ };
155
160
 
156
- Response.prototype.deleted = function (message?: string) {
157
- return ResponseHelper.deleted(this, message);
158
- };
161
+ proto.deleted = function (this: Response, message?: string) {
162
+ return ResponseHelper.deleted(this, message);
163
+ };
159
164
 
160
- Response.prototype.error = function (error: string, statusCode: number = 400, code?: string, details?: Record<string, unknown>) {
161
- return ResponseHelper.error(this, error, statusCode, code, details);
162
- };
165
+ proto.error = function (this: Response, error: string, statusCode: number = 400, code?: string, details?: Record<string, unknown>) {
166
+ return ResponseHelper.error(this, error, statusCode, code, details);
167
+ };
163
168
 
164
- Response.prototype.badRequest = function (error?: string, code?: string) {
165
- return ResponseHelper.badRequest(this, error, code);
166
- };
169
+ proto.badRequest = function (this: Response, error?: string, code?: string) {
170
+ return ResponseHelper.badRequest(this, error, code);
171
+ };
167
172
 
168
- Response.prototype.unauthorized = function (error?: string, code?: string) {
169
- return ResponseHelper.unauthorized(this, error, code);
170
- };
173
+ proto.unauthorized = function (this: Response, error?: string, code?: string) {
174
+ return ResponseHelper.unauthorized(this, error, code);
175
+ };
171
176
 
172
- Response.prototype.forbidden = function (error?: string, code?: string) {
173
- return ResponseHelper.forbidden(this, error, code);
174
- };
177
+ proto.forbidden = function (this: Response, error?: string, code?: string) {
178
+ return ResponseHelper.forbidden(this, error, code);
179
+ };
175
180
 
176
- Response.prototype.notFound = function (error?: string, code?: string) {
177
- return ResponseHelper.notFound(this, error, code);
178
- };
181
+ proto.notFound = function (this: Response, error?: string, code?: string) {
182
+ return ResponseHelper.notFound(this, error, code);
183
+ };
179
184
 
180
- Response.prototype.conflict = function (error?: string, code?: string) {
181
- return ResponseHelper.conflict(this, error, code);
182
- };
185
+ proto.conflict = function (this: Response, error?: string, code?: string) {
186
+ return ResponseHelper.conflict(this, error, code);
187
+ };
183
188
 
184
- Response.prototype.validationError = function (error: string, details?: Record<string, unknown>) {
185
- return ResponseHelper.validationError(this, error, details);
186
- };
189
+ proto.validationError = function (this: Response, error: string, details?: Record<string, unknown>) {
190
+ return ResponseHelper.validationError(this, error, details);
191
+ };
187
192
 
188
- Response.prototype.internalError = function (error?: string) {
189
- return ResponseHelper.internalError(this, error);
190
- };
193
+ proto.internalError = function (this: Response, error?: string) {
194
+ return ResponseHelper.internalError(this, error);
195
+ };
191
196
 
192
- Response.prototype.paginated = function <T>(data: T[], page: number, limit: number, total: number) {
193
- return ResponseHelper.paginated(this, data, page, limit, total);
194
- };
197
+ proto.paginated = function <T>(this: Response, data: T[], page: number, limit: number, total: number) {
198
+ return ResponseHelper.paginated(this, data, page, limit, total);
199
+ };
200
+ }
201
+ } catch {
202
+ // express not available at runtime
203
+ }
195
204
 
196
205
  export const response = ResponseHelper;
197
206
  export default ResponseHelper;