karos 1.0.3 → 1.0.4
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 +47 -6
- package/dist/index.js +123 -4
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { Response, Request, NextFunction } from 'express';
|
|
1
|
+
import { Response as Response$1, Request, NextFunction } from 'express';
|
|
2
2
|
|
|
3
3
|
declare const ErrorCode: {
|
|
4
4
|
readonly VALIDATION_FAILED: "VALIDATION_FAILED";
|
|
5
|
+
readonly BAD_REQUEST: "BAD_REQUEST";
|
|
5
6
|
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
6
|
-
readonly NOT_FOUND: "NOT_FOUND";
|
|
7
7
|
readonly FORBIDDEN: "FORBIDDEN";
|
|
8
|
+
readonly NOT_FOUND: "NOT_FOUND";
|
|
8
9
|
readonly CONFLICT: "CONFLICT";
|
|
9
10
|
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
11
|
+
readonly SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE";
|
|
10
12
|
};
|
|
11
13
|
type ErrorCodeType = typeof ErrorCode[keyof typeof ErrorCode];
|
|
12
14
|
interface ApiSuccessResponse<T = unknown> {
|
|
@@ -24,8 +26,8 @@ interface ApiErrorResponse {
|
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
declare function ok<T = unknown>(res: Response, data: T, message?: string, meta?: Record<string, unknown>): void;
|
|
28
|
-
declare function fail(res: Response, code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>): void;
|
|
29
|
+
declare function ok<T = unknown>(res: Response$1, data: T, message?: string, meta?: Record<string, unknown>): void;
|
|
30
|
+
declare function fail(res: Response$1, code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>): void;
|
|
29
31
|
|
|
30
32
|
declare class KarosError extends Error {
|
|
31
33
|
readonly code: ErrorCodeType;
|
|
@@ -33,12 +35,51 @@ declare class KarosError extends Error {
|
|
|
33
35
|
readonly errors?: Record<string, unknown>;
|
|
34
36
|
readonly isKarosError = true;
|
|
35
37
|
constructor(code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>);
|
|
38
|
+
toJSON(): {
|
|
39
|
+
success: boolean;
|
|
40
|
+
error: {
|
|
41
|
+
code: ErrorCodeType;
|
|
42
|
+
message: string;
|
|
43
|
+
details: Record<string, unknown> | undefined;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
36
46
|
}
|
|
37
47
|
declare const notFoundError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
38
48
|
declare const validationError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
39
49
|
declare const unauthorizedError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
50
|
+
declare const forbiddenError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
51
|
+
declare const conflictError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
52
|
+
declare const internalError: (message?: string, errors?: Record<string, unknown>) => never;
|
|
40
53
|
declare const httpError: (code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>) => never;
|
|
41
54
|
|
|
42
|
-
declare function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction): void;
|
|
55
|
+
declare function errorHandler(err: unknown, req: Request, res: Response$1, next: NextFunction): void;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Next.js / Web Standard Helper
|
|
59
|
+
* Returns a standardized SUCCESS Response object.
|
|
60
|
+
*/
|
|
61
|
+
declare function nextOk<T = unknown>(data: T, message?: string, status?: number, meta?: Record<string, unknown>): Response;
|
|
62
|
+
/**
|
|
63
|
+
* Next.js / Web Standard Helper
|
|
64
|
+
* Returns a standardized ERROR Response object.
|
|
65
|
+
*/
|
|
66
|
+
declare function nextFail(code: ErrorCodeType, message: string, status: number, errors?: Record<string, unknown>): Response;
|
|
67
|
+
/**
|
|
68
|
+
* Global Error Handler for Next.js App Router.
|
|
69
|
+
* Automatically detects:
|
|
70
|
+
* 1. KarosErrors (thrown manually)
|
|
71
|
+
* 2. Database Errors (Prisma, Mongo)
|
|
72
|
+
* 3. Unknown crashes (returns 500)
|
|
73
|
+
*
|
|
74
|
+
* Usage:
|
|
75
|
+
* try { ... } catch (err) { return handleNextError(err); }
|
|
76
|
+
*/
|
|
77
|
+
declare function handleNextError(err: any): Response;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Examines an error object and determines if it is a known database error.
|
|
81
|
+
* Supports: Prisma, Mongoose, MongoDB driver
|
|
82
|
+
*/
|
|
83
|
+
declare function resolveDbError(err: any): KarosError | null;
|
|
43
84
|
|
|
44
|
-
export { type ApiErrorResponse, type ApiSuccessResponse, ErrorCode, type ErrorCodeType, KarosError, errorHandler, fail, httpError, notFoundError, ok, unauthorizedError, validationError };
|
|
85
|
+
export { type ApiErrorResponse, type ApiSuccessResponse, ErrorCode, type ErrorCodeType, KarosError, conflictError, errorHandler, fail, forbiddenError, handleNextError, httpError, internalError, nextFail, nextOk, notFoundError, ok, resolveDbError, unauthorizedError, validationError };
|
package/dist/index.js
CHANGED
|
@@ -22,11 +22,18 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ErrorCode: () => ErrorCode,
|
|
24
24
|
KarosError: () => KarosError,
|
|
25
|
+
conflictError: () => conflictError,
|
|
25
26
|
errorHandler: () => errorHandler,
|
|
26
27
|
fail: () => fail,
|
|
28
|
+
forbiddenError: () => forbiddenError,
|
|
29
|
+
handleNextError: () => handleNextError,
|
|
27
30
|
httpError: () => httpError,
|
|
31
|
+
internalError: () => internalError,
|
|
32
|
+
nextFail: () => nextFail,
|
|
33
|
+
nextOk: () => nextOk,
|
|
28
34
|
notFoundError: () => notFoundError,
|
|
29
35
|
ok: () => ok,
|
|
36
|
+
resolveDbError: () => resolveDbError,
|
|
30
37
|
unauthorizedError: () => unauthorizedError,
|
|
31
38
|
validationError: () => validationError
|
|
32
39
|
});
|
|
@@ -35,11 +42,21 @@ module.exports = __toCommonJS(index_exports);
|
|
|
35
42
|
// src/types.ts
|
|
36
43
|
var ErrorCode = {
|
|
37
44
|
VALIDATION_FAILED: "VALIDATION_FAILED",
|
|
45
|
+
// 400
|
|
46
|
+
BAD_REQUEST: "BAD_REQUEST",
|
|
47
|
+
// 400 (Generic)
|
|
38
48
|
UNAUTHORIZED: "UNAUTHORIZED",
|
|
39
|
-
|
|
49
|
+
// 401
|
|
40
50
|
FORBIDDEN: "FORBIDDEN",
|
|
51
|
+
// 403
|
|
52
|
+
NOT_FOUND: "NOT_FOUND",
|
|
53
|
+
// 404
|
|
41
54
|
CONFLICT: "CONFLICT",
|
|
42
|
-
|
|
55
|
+
// 409
|
|
56
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
57
|
+
// 500
|
|
58
|
+
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE"
|
|
59
|
+
// 503 (Optional, good for external API failures)
|
|
43
60
|
};
|
|
44
61
|
|
|
45
62
|
// src/responses.ts
|
|
@@ -68,10 +85,24 @@ var KarosError = class _KarosError extends Error {
|
|
|
68
85
|
this.code = code;
|
|
69
86
|
this.status = status;
|
|
70
87
|
this.errors = errors;
|
|
88
|
+
Object.setPrototypeOf(this, _KarosError.prototype);
|
|
71
89
|
if (Error.captureStackTrace) {
|
|
72
90
|
Error.captureStackTrace(this, _KarosError);
|
|
73
91
|
}
|
|
74
92
|
}
|
|
93
|
+
// ✅ New Helper: Converts error to standardized JSON format
|
|
94
|
+
// This will be reused by both Express Middleware and Next.js helpers
|
|
95
|
+
toJSON() {
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
error: {
|
|
99
|
+
code: this.code,
|
|
100
|
+
message: this.message,
|
|
101
|
+
details: this.errors || void 0
|
|
102
|
+
// Only show if exists
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
75
106
|
};
|
|
76
107
|
var notFoundError = (message = "Not found", errors) => {
|
|
77
108
|
throw new KarosError("NOT_FOUND", message, 404, errors);
|
|
@@ -82,10 +113,46 @@ var validationError = (message = "Validation failed", errors) => {
|
|
|
82
113
|
var unauthorizedError = (message = "Unauthorized", errors) => {
|
|
83
114
|
throw new KarosError("UNAUTHORIZED", message, 401, errors);
|
|
84
115
|
};
|
|
116
|
+
var forbiddenError = (message = "Forbidden", errors) => {
|
|
117
|
+
throw new KarosError("FORBIDDEN", message, 403, errors);
|
|
118
|
+
};
|
|
119
|
+
var conflictError = (message = "Conflict", errors) => {
|
|
120
|
+
throw new KarosError("CONFLICT", message, 409, errors);
|
|
121
|
+
};
|
|
122
|
+
var internalError = (message = "Internal Server Error", errors) => {
|
|
123
|
+
throw new KarosError("INTERNAL_ERROR", message, 500, errors);
|
|
124
|
+
};
|
|
85
125
|
var httpError = (code, message, status, errors) => {
|
|
86
126
|
throw new KarosError(code, message, status, errors);
|
|
87
127
|
};
|
|
88
128
|
|
|
129
|
+
// src/db-handler.ts
|
|
130
|
+
function resolveDbError(err) {
|
|
131
|
+
if (!err) return null;
|
|
132
|
+
if (err.code === "P2002") {
|
|
133
|
+
const target = err.meta?.target ? ` (${err.meta.target})` : "";
|
|
134
|
+
return new KarosError(
|
|
135
|
+
ErrorCode.CONFLICT,
|
|
136
|
+
`Duplicate entry: Resource already exists${target}`,
|
|
137
|
+
409
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
if (err.code === "P2025") {
|
|
141
|
+
return new KarosError(ErrorCode.NOT_FOUND, "Record not found", 404);
|
|
142
|
+
}
|
|
143
|
+
if (err.code === 11e3 || err.errorResponse && err.errorResponse.code === 11e3) {
|
|
144
|
+
return new KarosError(ErrorCode.CONFLICT, "Duplicate entry: Resource already exists", 409);
|
|
145
|
+
}
|
|
146
|
+
if (err.name === "ValidationError") {
|
|
147
|
+
const details = {};
|
|
148
|
+
for (const field in err.errors) {
|
|
149
|
+
details[field] = err.errors[field].message;
|
|
150
|
+
}
|
|
151
|
+
return new KarosError(ErrorCode.VALIDATION_FAILED, "Database validation failed", 400, details);
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
89
156
|
// src/middleware.ts
|
|
90
157
|
function isKarosError(err) {
|
|
91
158
|
return typeof err === "object" && err !== null && err.isKarosError === true;
|
|
@@ -94,19 +161,71 @@ function errorHandler(err, req, res, next) {
|
|
|
94
161
|
if (res.headersSent) return next(err);
|
|
95
162
|
if (isKarosError(err)) {
|
|
96
163
|
fail(res, err.code, err.message, err.status, err.errors);
|
|
97
|
-
|
|
98
|
-
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const dbError = resolveDbError(err);
|
|
167
|
+
if (dbError) {
|
|
168
|
+
fail(res, dbError.code, dbError.message, dbError.status, dbError.errors);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
console.error("[Karos] Unexpected Error:", err);
|
|
172
|
+
fail(res, ErrorCode.INTERNAL_ERROR, "Internal server error", 500);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/next.ts
|
|
176
|
+
function nextOk(data, message, status = 200, meta) {
|
|
177
|
+
const body = {
|
|
178
|
+
success: true,
|
|
179
|
+
data,
|
|
180
|
+
...message && { message },
|
|
181
|
+
...meta && { meta }
|
|
182
|
+
};
|
|
183
|
+
return new Response(JSON.stringify(body), {
|
|
184
|
+
status,
|
|
185
|
+
headers: { "Content-Type": "application/json" }
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function nextFail(code, message, status, errors) {
|
|
189
|
+
const body = {
|
|
190
|
+
success: false,
|
|
191
|
+
error: {
|
|
192
|
+
code,
|
|
193
|
+
message,
|
|
194
|
+
...errors && { errors }
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
return new Response(JSON.stringify(body), {
|
|
198
|
+
status,
|
|
199
|
+
headers: { "Content-Type": "application/json" }
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function handleNextError(err) {
|
|
203
|
+
if (err && err.isKarosError) {
|
|
204
|
+
return nextFail(err.code, err.message, err.status, err.errors);
|
|
205
|
+
}
|
|
206
|
+
const dbErr = resolveDbError(err);
|
|
207
|
+
if (dbErr) {
|
|
208
|
+
return nextFail(dbErr.code, dbErr.message, dbErr.status, dbErr.errors);
|
|
99
209
|
}
|
|
210
|
+
console.error("[Karos Next] Unexpected Error:", err);
|
|
211
|
+
return nextFail(ErrorCode.INTERNAL_ERROR, "Internal Server Error", 500);
|
|
100
212
|
}
|
|
101
213
|
// Annotate the CommonJS export names for ESM import in node:
|
|
102
214
|
0 && (module.exports = {
|
|
103
215
|
ErrorCode,
|
|
104
216
|
KarosError,
|
|
217
|
+
conflictError,
|
|
105
218
|
errorHandler,
|
|
106
219
|
fail,
|
|
220
|
+
forbiddenError,
|
|
221
|
+
handleNextError,
|
|
107
222
|
httpError,
|
|
223
|
+
internalError,
|
|
224
|
+
nextFail,
|
|
225
|
+
nextOk,
|
|
108
226
|
notFoundError,
|
|
109
227
|
ok,
|
|
228
|
+
resolveDbError,
|
|
110
229
|
unauthorizedError,
|
|
111
230
|
validationError
|
|
112
231
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "karos",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Opinionated API response and error handling for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"express",
|
|
18
18
|
"error-handling",
|
|
19
19
|
"node",
|
|
20
|
-
"backend"
|
|
20
|
+
"backend",
|
|
21
|
+
"nextjs",
|
|
22
|
+
"node",
|
|
23
|
+
"error-structure"
|
|
21
24
|
],
|
|
22
25
|
"author": "Krishna Shrivastava",
|
|
23
26
|
"license": "MIT",
|