@withwiz/toolkit 0.2.2 → 0.2.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.
Files changed (46) hide show
  1. package/dist/auth/core/jwt/index.js +1 -1
  2. package/dist/auth/index.js +20 -20
  3. package/dist/{chunk-H5I5GWAA.js → chunk-6DD3MUDZ.js} +2 -2
  4. package/dist/{chunk-2KAPUVIS.js → chunk-7JWIX5BS.js} +1 -1
  5. package/dist/{chunk-N4YGR5WH.js → chunk-HCF3C3AQ.js} +11 -10
  6. package/dist/{chunk-3DZA6AGS.js → chunk-IJCVFLEY.js} +11 -4
  7. package/dist/{chunk-KXAWBFJN.js → chunk-IYXMGIEP.js} +11 -10
  8. package/dist/{chunk-MLGO3HLS.js → chunk-KXXROUA5.js} +20 -5
  9. package/dist/{chunk-XPASCCUA.js → chunk-M4VXPTKV.js} +2 -2
  10. package/dist/{chunk-TMVS4F7E.js → chunk-QYIUGQ5Q.js} +32 -7
  11. package/dist/{chunk-POKGHK3L.js → chunk-S54PFAEU.js} +2 -2
  12. package/dist/{chunk-Y3OTJH2S.js → chunk-SNVGJPPX.js} +36 -13
  13. package/dist/{chunk-T3LJYAMO.js → chunk-UT4J7NPS.js} +1 -1
  14. package/dist/{chunk-UXQRU3EM.js → chunk-V4KAPU2S.js} +3 -3
  15. package/dist/{chunk-GPBOMJSZ.js → chunk-WDRDGFHO.js} +74 -16
  16. package/dist/{chunk-SLG26KHZ.js → chunk-WTRBW2UY.js} +3 -3
  17. package/dist/{chunk-EEUBKZV4.js → chunk-Y2JYA2AR.js} +2 -2
  18. package/dist/constants/error-codes.d.ts +19 -12
  19. package/dist/constants/error-codes.js +3 -1
  20. package/dist/constants/index.js +3 -1
  21. package/dist/error/app-error.d.ts +1 -0
  22. package/dist/error/app-error.js +2 -2
  23. package/dist/error/error-display.d.ts +1 -1
  24. package/dist/error/error-display.js +7 -3
  25. package/dist/error/error-handler.d.ts +51 -12
  26. package/dist/error/error-handler.js +6 -3
  27. package/dist/error/friendly-messages-v2.js +5 -5
  28. package/dist/error/friendly-messages.d.ts +3 -0
  29. package/dist/error/friendly-messages.js +113 -5
  30. package/dist/error/index.js +22 -20
  31. package/dist/error/messages/en.js +1 -1
  32. package/dist/error/messages/index.js +3 -3
  33. package/dist/error/messages/ko.js +1 -1
  34. package/dist/middleware/auth.js +4 -4
  35. package/dist/middleware/error-handler.js +8 -6
  36. package/dist/middleware/index.js +11 -10
  37. package/dist/middleware/rate-limit.js +3 -3
  38. package/dist/middleware/wrappers.js +11 -10
  39. package/dist/storage/r2-storage.js +7 -3
  40. package/dist/utils/api-helpers.js +2 -2
  41. package/dist/utils/error-processor.js +3 -3
  42. package/dist/utils/index.js +4 -4
  43. package/dist/utils/input-validation.js +4 -4
  44. package/dist/utils/optimistic-lock.js +2 -2
  45. package/package.json +2 -1
  46. package/dist/chunk-2DVWSDST.js +0 -119
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  JWTManager,
3
3
  JWTService
4
- } from "../../../chunk-T3LJYAMO.js";
4
+ } from "../../../chunk-UT4J7NPS.js";
5
5
  import "../../../chunk-AIH3F7JV.js";
6
6
  import "../../../chunk-ORMEWXMH.js";
7
7
  export {
@@ -1,25 +1,6 @@
1
- import {
2
- JWTClientManager,
3
- clearStoredTokens,
4
- clearTokens,
5
- createApiHeaders,
6
- createAuthHeader,
7
- decodeJWTPayload,
8
- extractUserFromToken,
9
- getStoredTokens,
10
- getTokenExpirationString,
11
- getTokenIssuedAtString,
12
- getTokenRemainingTime,
13
- isTokenExpired,
14
- isTokenExpiringSoon,
15
- storeTokens
16
- } from "../chunk-XRRPEBKB.js";
17
1
  import {
18
2
  OAuthManager
19
3
  } from "../chunk-V5K5FYU7.js";
20
- import {
21
- TokenGenerator
22
- } from "../chunk-GDWEDUHO.js";
23
4
  import {
24
5
  DEFAULT_PASSWORD_CONFIG,
25
6
  createPasswordHasher,
@@ -35,6 +16,25 @@ import {
35
16
  defaultPasswordSchema,
36
17
  strongPasswordSchema
37
18
  } from "../chunk-IHXRF3BH.js";
19
+ import {
20
+ TokenGenerator
21
+ } from "../chunk-GDWEDUHO.js";
22
+ import {
23
+ JWTClientManager,
24
+ clearStoredTokens,
25
+ clearTokens,
26
+ createApiHeaders,
27
+ createAuthHeader,
28
+ decodeJWTPayload,
29
+ extractUserFromToken,
30
+ getStoredTokens,
31
+ getTokenExpirationString,
32
+ getTokenIssuedAtString,
33
+ getTokenRemainingTime,
34
+ isTokenExpired,
35
+ isTokenExpiringSoon,
36
+ storeTokens
37
+ } from "../chunk-XRRPEBKB.js";
38
38
  import {
39
39
  OAuthProvider,
40
40
  PasswordStrength,
@@ -44,7 +44,7 @@ import {
44
44
  import {
45
45
  JWTManager,
46
46
  JWTService
47
- } from "../chunk-T3LJYAMO.js";
47
+ } from "../chunk-UT4J7NPS.js";
48
48
  import {
49
49
  AUTH_ERROR_CODES,
50
50
  AuthError,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  AppError
3
- } from "./chunk-MLGO3HLS.js";
3
+ } from "./chunk-KXXROUA5.js";
4
4
  import {
5
5
  ERROR_CODES
6
- } from "./chunk-Y3OTJH2S.js";
6
+ } from "./chunk-SNVGJPPX.js";
7
7
  import {
8
8
  logger
9
9
  } from "./chunk-EZR55KV2.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ERROR_CODES,
3
3
  formatErrorMessage
4
- } from "./chunk-Y3OTJH2S.js";
4
+ } from "./chunk-SNVGJPPX.js";
5
5
 
6
6
  // src/utils/api-helpers.ts
7
7
  import { NextResponse } from "next/server";
@@ -61,6 +61,16 @@ var enMessages = {
61
61
  description: "Your session has expired for security.",
62
62
  action: "Please login again."
63
63
  },
64
+ 40104: {
65
+ title: "Password required",
66
+ description: "You must enter a password.",
67
+ action: "Please enter the password."
68
+ },
69
+ 40105: {
70
+ title: "Incorrect password",
71
+ description: "The password is incorrect.",
72
+ action: "Please check again."
73
+ },
64
74
  40106: {
65
75
  title: "Login failed",
66
76
  description: "Email or password is incorrect.",
@@ -173,16 +183,7 @@ var enMessages = {
173
183
  description: "Usage has been stopped.",
174
184
  action: "Please contact the owner."
175
185
  },
176
- 42206: {
177
- title: "Password required",
178
- description: "You must enter a password.",
179
- action: "Please enter the password."
180
- },
181
- 42207: {
182
- title: "Incorrect password",
183
- description: "The password is incorrect.",
184
- action: "Please check again."
185
- },
186
+ // 42206, 42207: moved to 401xx → see 40104, 40105 below
186
187
  42208: {
187
188
  title: "Alias not allowed",
188
189
  description: "This is a system reserved word.",
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  getErrorDisplayInfo,
3
3
  getFriendlyMessage
4
- } from "./chunk-2DVWSDST.js";
4
+ } from "./chunk-S54PFAEU.js";
5
+ import {
6
+ AppError
7
+ } from "./chunk-KXXROUA5.js";
5
8
  import {
6
9
  getErrorCategory
7
- } from "./chunk-Y3OTJH2S.js";
10
+ } from "./chunk-SNVGJPPX.js";
8
11
 
9
12
  // src/error/error-display.ts
10
13
  import { toast } from "sonner";
@@ -51,7 +54,7 @@ function showFriendlyError(errorOrCode, options) {
51
54
  }
52
55
  }
53
56
  async function handleApiResponse(response, options) {
54
- var _a;
57
+ var _a, _b;
55
58
  if (response.ok) {
56
59
  const data = await response.json();
57
60
  return data.data;
@@ -63,7 +66,11 @@ async function handleApiResponse(response, options) {
63
66
  onAuthError: options == null ? void 0 : options.onAuthError
64
67
  });
65
68
  }
66
- throw new Error(((_a = errorData.error) == null ? void 0 : _a.message) || `Request failed with status ${response.status}`);
69
+ const errorCode = (_a = errorData.error) == null ? void 0 : _a.code;
70
+ if (errorCode) {
71
+ throw new AppError(errorCode, (_b = errorData.error) == null ? void 0 : _b.message);
72
+ }
73
+ throw AppError.fromKey("SERVER_ERROR", `Request failed with status ${response.status}`);
67
74
  }
68
75
  function formatInlineError(code, locale) {
69
76
  const lang = locale || getLocale();
@@ -61,6 +61,16 @@ var koMessages = {
61
61
  description: "\uBCF4\uC548\uC744 \uC704\uD574 \uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
62
62
  action: "\uB2E4\uC2DC \uB85C\uADF8\uC778\uD574 \uC8FC\uC138\uC694."
63
63
  },
64
+ 40104: {
65
+ title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD544\uC694\uD574\uC694",
66
+ description: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574\uC57C \uD569\uB2C8\uB2E4.",
67
+ action: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694."
68
+ },
69
+ 40105: {
70
+ title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD2C0\uB838\uC5B4\uC694",
71
+ description: "\uBE44\uBC00\uBC88\uD638\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
72
+ action: "\uB2E4\uC2DC \uD655\uC778\uD574 \uC8FC\uC138\uC694."
73
+ },
64
74
  40106: {
65
75
  title: "\uB85C\uADF8\uC778\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694",
66
76
  description: "\uC774\uBA54\uC77C \uB610\uB294 \uBE44\uBC00\uBC88\uD638\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
@@ -173,16 +183,7 @@ var koMessages = {
173
183
  description: "\uC0AC\uC6A9\uC774 \uC911\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
174
184
  action: "\uC18C\uC720\uC790\uC5D0\uAC8C \uBB38\uC758\uD574 \uC8FC\uC138\uC694."
175
185
  },
176
- 42206: {
177
- title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD544\uC694\uD574\uC694",
178
- description: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574\uC57C \uD569\uB2C8\uB2E4.",
179
- action: "\uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694."
180
- },
181
- 42207: {
182
- title: "\uBE44\uBC00\uBC88\uD638\uAC00 \uD2C0\uB838\uC5B4\uC694",
183
- description: "\uBE44\uBC00\uBC88\uD638\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
184
- action: "\uB2E4\uC2DC \uD655\uC778\uD574 \uC8FC\uC138\uC694."
185
- },
186
+ // 42206, 42207: 401xx로 이동 → 아래 40104, 40105 참조
186
187
  42208: {
187
188
  title: "\uC0AC\uC6A9\uD560 \uC218 \uC5C6\uB294 \uBCC4\uCE6D\uC774\uC5D0\uC694",
188
189
  description: "\uC2DC\uC2A4\uD15C \uC608\uC57D\uC5B4\uC785\uB2C8\uB2E4.",
@@ -1,15 +1,29 @@
1
1
  import {
2
2
  ERROR_CODES,
3
+ classifyError,
3
4
  formatErrorMessage,
4
5
  getErrorCategory,
5
6
  getHttpStatus
6
- } from "./chunk-Y3OTJH2S.js";
7
+ } from "./chunk-SNVGJPPX.js";
7
8
 
8
9
  // src/error/app-error.ts
9
10
  var AppError = class _AppError extends Error {
10
11
  constructor(code, message, details, requestId) {
11
12
  const formattedMessage = formatErrorMessage(code, message);
12
13
  super(formattedMessage);
14
+ if (!Number.isInteger(code) || code < 1e4 || code > 59999) {
15
+ const safeCode = ERROR_CODES.INTERNAL_SERVER_ERROR.code;
16
+ this.name = "AppError";
17
+ this.code = safeCode;
18
+ this.status = getHttpStatus(safeCode);
19
+ this.key = "INTERNAL_SERVER_ERROR";
20
+ this.category = getErrorCategory(safeCode);
21
+ this.details = details;
22
+ this.timestamp = /* @__PURE__ */ new Date();
23
+ this.requestId = requestId;
24
+ Object.setPrototypeOf(this, _AppError.prototype);
25
+ return;
26
+ }
13
27
  this.name = "AppError";
14
28
  this.code = code;
15
29
  this.status = getHttpStatus(code);
@@ -97,10 +111,8 @@ var AppError = class _AppError extends Error {
97
111
  const code = parseInt(codeMatch[1], 10);
98
112
  return new _AppError(code, error.message.replace(/\s*\[\d{5}\]$/, ""));
99
113
  }
100
- return new _AppError(
101
- ERROR_CODES.SERVER_ERROR.code,
102
- fallbackMessage || ERROR_CODES.SERVER_ERROR.message
103
- );
114
+ const classified = classifyError(error);
115
+ return new _AppError(classified.code, fallbackMessage || classified.message);
104
116
  }
105
117
  return new _AppError(
106
118
  ERROR_CODES.INTERNAL_SERVER_ERROR.code,
@@ -320,6 +332,9 @@ var AppError = class _AppError extends Error {
320
332
  static ipBlocked(ip) {
321
333
  return _AppError.fromKey("IP_BLOCKED", void 0, { value: ip });
322
334
  }
335
+ static corsViolation(origin) {
336
+ return _AppError.fromKey("CORS_VIOLATION", void 0, { value: origin });
337
+ }
323
338
  };
324
339
  var app_error_default = AppError;
325
340
 
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  AppError
3
- } from "./chunk-MLGO3HLS.js";
3
+ } from "./chunk-KXXROUA5.js";
4
4
  import {
5
5
  ERROR_CODES,
6
6
  formatErrorMessage
7
- } from "./chunk-Y3OTJH2S.js";
7
+ } from "./chunk-SNVGJPPX.js";
8
8
  import {
9
9
  logApiRequest,
10
10
  logApiResponse,
@@ -1,12 +1,19 @@
1
+ import {
2
+ AUTH_ERROR_CODE_MAP
3
+ } from "./chunk-WDRDGFHO.js";
1
4
  import {
2
5
  getErrorMessage
3
- } from "./chunk-EEUBKZV4.js";
6
+ } from "./chunk-Y2JYA2AR.js";
4
7
  import {
5
8
  AppError
6
- } from "./chunk-MLGO3HLS.js";
9
+ } from "./chunk-KXXROUA5.js";
10
+ import {
11
+ ERROR_CODES,
12
+ classifyError
13
+ } from "./chunk-SNVGJPPX.js";
7
14
  import {
8
- ERROR_CODES
9
- } from "./chunk-Y3OTJH2S.js";
15
+ AuthError
16
+ } from "./chunk-AIH3F7JV.js";
10
17
  import {
11
18
  logger
12
19
  } from "./chunk-EZR55KV2.js";
@@ -46,15 +53,33 @@ var errorHandlerMiddleware = async (context, next) => {
46
53
  { issues }
47
54
  );
48
55
  }
56
+ if (error instanceof AuthError) {
57
+ const mappedCode = AUTH_ERROR_CODE_MAP[error.code];
58
+ if (mappedCode) {
59
+ return createErrorResponse(
60
+ mappedCode,
61
+ error.message,
62
+ context.locale,
63
+ context.requestId
64
+ );
65
+ }
66
+ const fallbackCode = error.statusCode === 401 ? ERROR_CODES.UNAUTHORIZED.code : ERROR_CODES.BAD_REQUEST.code;
67
+ return createErrorResponse(
68
+ fallbackCode,
69
+ error.message,
70
+ context.locale,
71
+ context.requestId
72
+ );
73
+ }
74
+ const classified = error instanceof Error ? classifyError(error) : ERROR_CODES.INTERNAL_SERVER_ERROR;
49
75
  logger.error("[ErrorHandler] Unexpected error:", error);
50
76
  const isProduction = process.env.NODE_ENV === "production";
51
- const safeMessage = isProduction ? "Internal server error" : error instanceof Error ? error.message : "Unknown error";
77
+ const safeMessage = isProduction ? classified.message : error instanceof Error ? error.message : "Unknown error";
52
78
  return createErrorResponse(
53
- ERROR_CODES.INTERNAL_SERVER_ERROR.code,
79
+ classified.code,
54
80
  safeMessage,
55
81
  context.locale,
56
82
  context.requestId,
57
- // 개발 환경에서만 스택 트레이스 포함
58
83
  !isProduction && error instanceof Error ? { stack: (_b = error.stack) == null ? void 0 : _b.split("\n").slice(0, 5) } : void 0
59
84
  );
60
85
  }
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  getErrorMessage
3
- } from "./chunk-EEUBKZV4.js";
3
+ } from "./chunk-Y2JYA2AR.js";
4
4
  import {
5
5
  getErrorCategory
6
- } from "./chunk-Y3OTJH2S.js";
6
+ } from "./chunk-SNVGJPPX.js";
7
7
 
8
8
  // src/error/friendly-messages-v2.ts
9
9
  var categoryIcons = {
@@ -92,6 +92,18 @@ var ERROR_CODES = {
92
92
  status: HTTP_STATUS.UNAUTHORIZED,
93
93
  message: "Invalid email or password."
94
94
  },
95
+ LINK_PASSWORD_REQUIRED: {
96
+ code: 40104,
97
+ key: "LINK_PASSWORD_REQUIRED",
98
+ status: HTTP_STATUS.UNAUTHORIZED,
99
+ message: "Link password is required."
100
+ },
101
+ LINK_PASSWORD_INCORRECT: {
102
+ code: 40105,
103
+ key: "LINK_PASSWORD_INCORRECT",
104
+ status: HTTP_STATUS.UNAUTHORIZED,
105
+ message: "Incorrect link password."
106
+ },
95
107
  SESSION_EXPIRED: {
96
108
  code: 40107,
97
109
  key: "SESSION_EXPIRED",
@@ -100,6 +112,7 @@ var ERROR_CODES = {
100
112
  },
101
113
  // ============================================
102
114
  // Permissions Related (403xx - 01~09)
115
+ // Reserved: 40301~40303 (미사용, 향후 세분화 확장용)
103
116
  // ============================================
104
117
  FORBIDDEN: {
105
118
  code: 40304,
@@ -113,6 +126,7 @@ var ERROR_CODES = {
113
126
  status: HTTP_STATUS.FORBIDDEN,
114
127
  message: "Email verification required."
115
128
  },
129
+ // Reserved: 40306~40307 (미사용, 향후 확장용)
116
130
  ACCOUNT_DISABLED: {
117
131
  code: 40308,
118
132
  key: "ACCOUNT_DISABLED",
@@ -127,6 +141,7 @@ var ERROR_CODES = {
127
141
  },
128
142
  // ============================================
129
143
  // Resource Not Found (404xx)
144
+ // Reserved: 40404~40407 (미사용, 향후 리소스 타입 확장용)
130
145
  // ============================================
131
146
  NOT_FOUND: {
132
147
  code: 40401,
@@ -166,6 +181,7 @@ var ERROR_CODES = {
166
181
  },
167
182
  // ============================================
168
183
  // Conflict (409xx)
184
+ // Reserved: 40901~40903 (미사용, 향후 확장용)
169
185
  // ============================================
170
186
  CONFLICT: {
171
187
  code: 40904,
@@ -224,18 +240,8 @@ var ERROR_CODES = {
224
240
  status: HTTP_STATUS.UNPROCESSABLE_ENTITY,
225
241
  message: "Link is inactive."
226
242
  },
227
- LINK_PASSWORD_REQUIRED: {
228
- code: 42206,
229
- key: "LINK_PASSWORD_REQUIRED",
230
- status: HTTP_STATUS.UNPROCESSABLE_ENTITY,
231
- message: "Link password is required."
232
- },
233
- LINK_PASSWORD_INCORRECT: {
234
- code: 42207,
235
- key: "LINK_PASSWORD_INCORRECT",
236
- status: HTTP_STATUS.UNPROCESSABLE_ENTITY,
237
- message: "Incorrect link password."
238
- },
243
+ // LINK_PASSWORD_REQUIRED: 401xx로 이동 (40104)
244
+ // LINK_PASSWORD_INCORRECT: 401xx로 이동 (40105)
239
245
  RESERVED_WORD_USED: {
240
246
  code: 42208,
241
247
  key: "RESERVED_WORD_USED",
@@ -308,6 +314,7 @@ var ERROR_CODES = {
308
314
  status: HTTP_STATUS.INTERNAL_SERVER_ERROR,
309
315
  message: "An error occurred during data processing."
310
316
  },
317
+ // Reserved: 50004~50005 (미사용, 향후 서버 에러 세분화용)
311
318
  EMAIL_SEND_FAILED: {
312
319
  code: 50006,
313
320
  key: "EMAIL_SEND_FAILED",
@@ -328,6 +335,7 @@ var ERROR_CODES = {
328
335
  },
329
336
  // ============================================
330
337
  // Service Unavailable (503xx)
338
+ // Reserved: 50301~50303 (미사용, 향후 확장용)
331
339
  // ============================================
332
340
  EXTERNAL_SERVICE_ERROR: {
333
341
  code: 50304,
@@ -457,6 +465,20 @@ function formatErrorMessage(code, customMessage) {
457
465
  const errorInfo = getErrorByCode(code);
458
466
  return customMessage || (errorInfo == null ? void 0 : errorInfo.message) || getDefaultErrorMessage(getHttpStatus(code));
459
467
  }
468
+ function classifyError(error) {
469
+ const msg = error.message.toLowerCase();
470
+ const errCode = error.code;
471
+ if (msg.includes("not found")) return ERROR_CODES.NOT_FOUND;
472
+ if (msg.includes("unauthorized")) return ERROR_CODES.UNAUTHORIZED;
473
+ if (msg.includes("forbidden") || msg.includes("access denied")) return ERROR_CODES.FORBIDDEN;
474
+ if (msg.includes("too many request") || msg.includes("rate limit")) return ERROR_CODES.RATE_LIMIT_EXCEEDED;
475
+ if (error.message.match(/P\d{4}/) || msg.includes("database") || msg.includes("prisma")) return ERROR_CODES.DATABASE_ERROR;
476
+ if (errCode === "ECONNREFUSED" || errCode === "ECONNRESET" || errCode === "ETIMEDOUT" || errCode === "ENOTFOUND" || msg.includes("fetch failed") || msg.includes("network")) return ERROR_CODES.EXTERNAL_SERVICE_ERROR;
477
+ if (msg.includes("redis") || msg.includes("cache") || msg.includes("upstash")) return ERROR_CODES.CACHE_ERROR;
478
+ if (msg.includes("email") && (msg.includes("send") || msg.includes("smtp"))) return ERROR_CODES.EMAIL_SEND_FAILED;
479
+ if (msg.includes("upload") || msg.includes("s3") || msg.includes("r2")) return ERROR_CODES.FILE_UPLOAD_FAILED;
480
+ return ERROR_CODES.SERVER_ERROR;
481
+ }
460
482
 
461
483
  export {
462
484
  HTTP_STATUS,
@@ -469,5 +491,6 @@ export {
469
491
  getLogLevel,
470
492
  getAllErrorCodes,
471
493
  getErrorCodesByCategory,
472
- formatErrorMessage
494
+ formatErrorMessage,
495
+ classifyError
473
496
  };
@@ -10,7 +10,7 @@ var JWTManager = class {
10
10
  this.logger = logger;
11
11
  this.secretKey = new TextEncoder().encode(config.secret);
12
12
  if (config.secret.length < 32) {
13
- throw new Error("JWT secret must be at least 32 characters long");
13
+ throw new JWTError("JWT secret must be at least 32 characters long", "TOKEN_CREATION_FAILED");
14
14
  }
15
15
  }
16
16
  /**
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  AppError
3
- } from "./chunk-MLGO3HLS.js";
3
+ } from "./chunk-KXXROUA5.js";
4
4
  import {
5
5
  ERROR_CODES
6
- } from "./chunk-Y3OTJH2S.js";
6
+ } from "./chunk-SNVGJPPX.js";
7
7
  import {
8
8
  JWTManager
9
- } from "./chunk-T3LJYAMO.js";
9
+ } from "./chunk-UT4J7NPS.js";
10
10
  import {
11
11
  logger
12
12
  } from "./chunk-EZR55KV2.js";
@@ -1,11 +1,15 @@
1
1
  import {
2
2
  AppError
3
- } from "./chunk-MLGO3HLS.js";
3
+ } from "./chunk-KXXROUA5.js";
4
4
  import {
5
5
  ERROR_CODES,
6
+ classifyError,
6
7
  formatErrorMessage,
7
8
  getHttpStatus
8
- } from "./chunk-Y3OTJH2S.js";
9
+ } from "./chunk-SNVGJPPX.js";
10
+ import {
11
+ AuthError
12
+ } from "./chunk-AIH3F7JV.js";
9
13
  import {
10
14
  logger
11
15
  } from "./chunk-EZR55KV2.js";
@@ -17,6 +21,37 @@ import {
17
21
  // src/error/error-handler.ts
18
22
  import { NextResponse } from "next/server";
19
23
  import { z } from "zod";
24
+ var AUTH_ERROR_CODE_MAP = {
25
+ // JWT
26
+ TOKEN_EXPIRED: ERROR_CODES.TOKEN_EXPIRED.code,
27
+ TOKEN_INVALID: ERROR_CODES.INVALID_TOKEN.code,
28
+ TOKEN_CREATION_FAILED: ERROR_CODES.SERVER_ERROR.code,
29
+ TOKEN_VERIFICATION_FAILED: ERROR_CODES.INVALID_TOKEN.code,
30
+ REFRESH_TOKEN_EXPIRED: ERROR_CODES.TOKEN_EXPIRED.code,
31
+ INVALID_PAYLOAD: ERROR_CODES.INVALID_TOKEN.code,
32
+ // OAuth
33
+ OAUTH_PROVIDER_NOT_CONFIGURED: ERROR_CODES.EXTERNAL_SERVICE_ERROR.code,
34
+ OAUTH_TOKEN_EXCHANGE_FAILED: ERROR_CODES.EXTERNAL_SERVICE_ERROR.code,
35
+ OAUTH_USER_INFO_FAILED: ERROR_CODES.EXTERNAL_SERVICE_ERROR.code,
36
+ // Password
37
+ PASSWORD_TOO_SHORT: ERROR_CODES.PASSWORD_TOO_WEAK.code,
38
+ PASSWORD_TOO_LONG: ERROR_CODES.PASSWORD_TOO_WEAK.code,
39
+ PASSWORD_MISSING_NUMBER: ERROR_CODES.PASSWORD_TOO_WEAK.code,
40
+ PASSWORD_MISSING_UPPERCASE: ERROR_CODES.PASSWORD_TOO_WEAK.code,
41
+ PASSWORD_MISSING_LOWERCASE: ERROR_CODES.PASSWORD_TOO_WEAK.code,
42
+ PASSWORD_MISSING_SPECIAL_CHAR: ERROR_CODES.PASSWORD_TOO_WEAK.code,
43
+ PASSWORD_HASH_FAILED: ERROR_CODES.SERVER_ERROR.code,
44
+ PASSWORD_VERIFY_FAILED: ERROR_CODES.SERVER_ERROR.code,
45
+ // Email
46
+ EMAIL_SEND_FAILED: ERROR_CODES.EMAIL_SEND_FAILED.code,
47
+ EMAIL_TOKEN_EXPIRED: ERROR_CODES.TOKEN_EXPIRED.code,
48
+ EMAIL_TOKEN_INVALID: ERROR_CODES.INVALID_TOKEN.code,
49
+ // Generic
50
+ INVALID_CREDENTIALS: ERROR_CODES.INVALID_CREDENTIALS.code,
51
+ USER_NOT_FOUND: ERROR_CODES.USER_NOT_FOUND.code,
52
+ EMAIL_ALREADY_EXISTS: ERROR_CODES.EMAIL_ALREADY_EXISTS.code,
53
+ UNAUTHORIZED: ERROR_CODES.UNAUTHORIZED.code
54
+ };
20
55
  var PRISMA_ERROR_MAP = {
21
56
  P2000: { code: 40001, message: "\uC785\uB825\uAC12\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4." },
22
57
  P2001: { code: 40401, message: "\uB808\uCF54\uB4DC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." },
@@ -50,6 +85,14 @@ function processError(error) {
50
85
  details: { issues }
51
86
  };
52
87
  }
88
+ if (error instanceof AuthError) {
89
+ const mappedCode = AUTH_ERROR_CODE_MAP[error.code];
90
+ if (mappedCode) {
91
+ return { code: mappedCode, message: formatErrorMessage(mappedCode, error.message), status: getHttpStatus(mappedCode) };
92
+ }
93
+ const fallbackCode = error.statusCode === 401 ? ERROR_CODES.UNAUTHORIZED.code : ERROR_CODES.BAD_REQUEST.code;
94
+ return { code: fallbackCode, message: formatErrorMessage(fallbackCode, error.message), status: error.statusCode };
95
+ }
53
96
  if (error instanceof Error) {
54
97
  const prismaMatch = error.message.match(/P\d{4}/);
55
98
  if (prismaMatch) {
@@ -58,13 +101,8 @@ function processError(error) {
58
101
  return { code: mapping.code, message: formatErrorMessage(mapping.code, mapping.message), status: getHttpStatus(mapping.code) };
59
102
  }
60
103
  }
61
- if (error.message.toLowerCase().includes("not found")) {
62
- return { code: ERROR_CODES.NOT_FOUND.code, message: formatErrorMessage(ERROR_CODES.NOT_FOUND.code), status: 404 };
63
- }
64
- if (error.message.toLowerCase().includes("unauthorized")) {
65
- return { code: ERROR_CODES.UNAUTHORIZED.code, message: formatErrorMessage(ERROR_CODES.UNAUTHORIZED.code), status: 401 };
66
- }
67
- return { code: ERROR_CODES.SERVER_ERROR.code, message: formatErrorMessage(ERROR_CODES.SERVER_ERROR.code), status: 500 };
104
+ const classified = classifyError(error);
105
+ return { code: classified.code, message: formatErrorMessage(classified.code), status: classified.status };
68
106
  }
69
107
  return { code: ERROR_CODES.INTERNAL_SERVER_ERROR.code, message: formatErrorMessage(ERROR_CODES.INTERNAL_SERVER_ERROR.code), status: 500 };
70
108
  }
@@ -81,13 +119,18 @@ var ErrorResponse = {
81
119
  // 400xx
82
120
  validation: (message, details) => errorToResponse(AppError.validation(message, details ? __spreadValues({}, details) : void 0)),
83
121
  badRequest: (message) => errorToResponse(AppError.badRequest(message)),
122
+ invalidInput: (message) => errorToResponse(AppError.invalidInput(message)),
123
+ missingField: (fieldName) => errorToResponse(AppError.missingField(fieldName)),
84
124
  invalidUrl: () => errorToResponse(AppError.invalidUrl()),
85
125
  invalidEmail: () => errorToResponse(AppError.invalidEmail()),
126
+ weakPassword: () => errorToResponse(AppError.weakPassword()),
86
127
  invalidAlias: () => errorToResponse(AppError.invalidAlias()),
87
128
  // 401xx
88
129
  unauthorized: (message) => errorToResponse(AppError.unauthorized(message)),
89
130
  invalidToken: () => errorToResponse(AppError.invalidToken()),
90
131
  tokenExpired: () => errorToResponse(AppError.tokenExpired()),
132
+ linkPasswordRequired: () => errorToResponse(AppError.linkPasswordRequired()),
133
+ linkPasswordIncorrect: () => errorToResponse(AppError.linkPasswordIncorrect()),
91
134
  invalidCredentials: () => errorToResponse(AppError.invalidCredentials()),
92
135
  sessionExpired: () => errorToResponse(AppError.sessionExpired()),
93
136
  // 403xx
@@ -99,6 +142,9 @@ var ErrorResponse = {
99
142
  notFound: (message) => errorToResponse(AppError.notFound(message)),
100
143
  userNotFound: () => errorToResponse(AppError.userNotFound()),
101
144
  linkNotFound: () => errorToResponse(AppError.linkNotFound()),
145
+ tagNotFound: () => errorToResponse(AppError.tagNotFound()),
146
+ favoriteNotFound: () => errorToResponse(AppError.favoriteNotFound()),
147
+ groupNotFound: () => errorToResponse(AppError.groupNotFound()),
102
148
  // 409xx
103
149
  conflict: (message) => errorToResponse(AppError.conflict(message)),
104
150
  duplicate: (resource) => errorToResponse(AppError.duplicate(resource)),
@@ -106,28 +152,40 @@ var ErrorResponse = {
106
152
  aliasExists: () => errorToResponse(AppError.aliasExists()),
107
153
  // 422xx
108
154
  businessRule: (message) => errorToResponse(AppError.businessRule(message)),
155
+ invalidOperation: (message) => errorToResponse(AppError.invalidOperation(message)),
109
156
  linkExpired: () => errorToResponse(AppError.linkExpired()),
110
157
  linkInactive: () => errorToResponse(AppError.linkInactive()),
111
- linkPasswordRequired: () => errorToResponse(AppError.linkPasswordRequired()),
112
- linkPasswordIncorrect: () => errorToResponse(AppError.linkPasswordIncorrect()),
113
158
  reservedWord: () => errorToResponse(AppError.reservedWord()),
114
159
  quotaExceeded: () => errorToResponse(AppError.quotaExceeded()),
160
+ alreadyFavorited: () => errorToResponse(AppError.alreadyFavorited()),
161
+ fileTooLarge: (maxSize) => errorToResponse(AppError.fileTooLarge(maxSize)),
162
+ unsupportedFileType: (fileType) => errorToResponse(AppError.unsupportedFileType(fileType)),
115
163
  // 429xx
116
164
  rateLimit: () => errorToResponse(AppError.rateLimit()),
117
165
  dailyLimit: () => errorToResponse(AppError.dailyLimit()),
166
+ apiQuotaExceeded: () => errorToResponse(AppError.apiQuotaExceeded()),
118
167
  // 500xx
119
168
  serverError: (message) => errorToResponse(AppError.serverError(message)),
120
- databaseError: () => errorToResponse(AppError.databaseError()),
169
+ internalError: (message) => errorToResponse(AppError.internalError(message)),
170
+ databaseError: (message) => errorToResponse(AppError.databaseError(message)),
171
+ emailSendFailed: () => errorToResponse(AppError.emailSendFailed()),
172
+ cacheError: () => errorToResponse(AppError.cacheError()),
173
+ fileUploadFailed: () => errorToResponse(AppError.fileUploadFailed()),
121
174
  // 503xx
122
- serviceUnavailable: () => errorToResponse(AppError.serviceUnavailable()),
175
+ serviceUnavailable: (message) => errorToResponse(AppError.serviceUnavailable(message)),
123
176
  externalServiceError: (service) => errorToResponse(AppError.externalServiceError(service)),
124
- // 보안
125
- accessBlocked: () => errorToResponse(AppError.accessBlocked()),
126
- ipBlocked: () => errorToResponse(AppError.ipBlocked())
177
+ // 보안 (403xx 71~79)
178
+ accessBlocked: (reason) => errorToResponse(AppError.accessBlocked(reason)),
179
+ securityValidationFailed: () => errorToResponse(AppError.securityValidationFailed()),
180
+ blockedUrl: (url) => errorToResponse(AppError.blockedUrl(url)),
181
+ suspiciousActivity: () => errorToResponse(AppError.suspiciousActivity()),
182
+ ipBlocked: (ip) => errorToResponse(AppError.ipBlocked(ip)),
183
+ corsViolation: (origin) => errorToResponse(AppError.corsViolation(origin))
127
184
  };
128
185
  var error_handler_default = { errorToResponse, processError, withErrorHandler, ErrorResponse };
129
186
 
130
187
  export {
188
+ AUTH_ERROR_CODE_MAP,
131
189
  errorToResponse,
132
190
  processError,
133
191
  withErrorHandler,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-4ODT3Q4A.js";
4
4
  import {
5
5
  rateLimitMiddleware
6
- } from "./chunk-H5I5GWAA.js";
6
+ } from "./chunk-6DD3MUDZ.js";
7
7
  import {
8
8
  responseLoggerMiddleware
9
9
  } from "./chunk-KAWVMIRJ.js";
@@ -14,13 +14,13 @@ import {
14
14
  adminMiddleware,
15
15
  authMiddleware,
16
16
  optionalAuthMiddleware
17
- } from "./chunk-UXQRU3EM.js";
17
+ } from "./chunk-V4KAPU2S.js";
18
18
  import {
19
19
  corsMiddleware
20
20
  } from "./chunk-FOKAATUQ.js";
21
21
  import {
22
22
  errorHandlerMiddleware
23
- } from "./chunk-TMVS4F7E.js";
23
+ } from "./chunk-QYIUGQ5Q.js";
24
24
  import {
25
25
  initRequestMiddleware
26
26
  } from "./chunk-62Q7DN5G.js";