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 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
- NOT_FOUND: "NOT_FOUND",
49
+ // 401
40
50
  FORBIDDEN: "FORBIDDEN",
51
+ // 403
52
+ NOT_FOUND: "NOT_FOUND",
53
+ // 404
41
54
  CONFLICT: "CONFLICT",
42
- INTERNAL_ERROR: "INTERNAL_ERROR"
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
- } else {
98
- fail(res, ErrorCode.INTERNAL_ERROR, "Internal server error", 500);
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",
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",