@pori15/logixlysia 6.0.11 → 6.0.12
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/package.json +2 -2
- package/src/Error/errors.ts +156 -156
- package/src/Error/type.ts +10 -10
- package/src/extensions/banner.ts +26 -26
- package/src/extensions/index.ts +28 -28
- package/src/helpers/status.ts +58 -58
- package/src/index.ts +151 -151
- package/src/interfaces.ts +207 -207
- package/src/logger/create-logger.ts +238 -238
- package/src/logger/handle-http-error.ts +134 -134
- package/src/logger/index.ts +89 -89
- package/src/output/file.ts +40 -40
- package/src/output/fs.ts +5 -5
- package/src/output/index.ts +42 -42
- package/src/output/rotation-manager.ts +121 -121
- package/src/utils/format.ts +2 -2
- package/src/utils/get-error-code.ts +23 -23
- package/src/utils/handle-error.ts +149 -149
- package/src/utils/rotation.ts +91 -91
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pori15/logixlysia",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.12",
|
|
4
4
|
"description": "🦊 Logixlysia is a logger for Elysia",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"elysia": "^1.4.28",
|
|
53
53
|
"pino-pretty": ">=13.0.0",
|
|
54
|
-
"typescript": "^
|
|
54
|
+
"typescript": "^6.0.2"
|
|
55
55
|
},
|
|
56
56
|
"peerDependenciesMeta": {
|
|
57
57
|
"pino-pretty": {
|
package/src/Error/errors.ts
CHANGED
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
// RFC 9457 Problem Details - https://www.rfc-editor.org/rfc/rfc9457.html
|
|
2
|
-
|
|
3
|
-
export interface ProblemDocument {
|
|
4
|
-
detail?: string;
|
|
5
|
-
instance?: string;
|
|
6
|
-
status?: number;
|
|
7
|
-
title: string;
|
|
8
|
-
type: string;
|
|
9
|
-
[key: string]: unknown;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* RFC 9457 Problem Details Error
|
|
14
|
-
*
|
|
15
|
-
* Core members:
|
|
16
|
-
* - type: URI reference identifying the problem type (default "about:blank")
|
|
17
|
-
* - title: Short human-readable summary
|
|
18
|
-
* - status: HTTP status code
|
|
19
|
-
* - detail: Human-readable explanation for this occurrence
|
|
20
|
-
* - instance: URI reference identifying this specific occurrence
|
|
21
|
-
* - extensions: Additional properties serialized as-is
|
|
22
|
-
*/
|
|
23
|
-
export class ProblemError extends Error {
|
|
24
|
-
public readonly status: number;
|
|
25
|
-
public readonly title: string;
|
|
26
|
-
public readonly type: string;
|
|
27
|
-
public readonly detail?: string;
|
|
28
|
-
public readonly instance?: string;
|
|
29
|
-
public readonly extensions?: Record<string, unknown>;
|
|
30
|
-
|
|
31
|
-
constructor(
|
|
32
|
-
type = "about:blank",
|
|
33
|
-
title: string,
|
|
34
|
-
status: number,
|
|
35
|
-
detail?: string,
|
|
36
|
-
instance?: string,
|
|
37
|
-
extensions?: Record<string, unknown>
|
|
38
|
-
) {
|
|
39
|
-
super(detail || title);
|
|
40
|
-
Object.setPrototypeOf(this, ProblemError.prototype);
|
|
41
|
-
|
|
42
|
-
this.status = status;
|
|
43
|
-
this.title = title;
|
|
44
|
-
this.type = type;
|
|
45
|
-
this.detail = detail;
|
|
46
|
-
this.instance = instance;
|
|
47
|
-
this.extensions = extensions;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
toJSON(): ProblemDocument {
|
|
51
|
-
return {
|
|
52
|
-
type: this.type,
|
|
53
|
-
title: this.title,
|
|
54
|
-
status: this.status,
|
|
55
|
-
...(this.detail ? { detail: this.detail } : {}),
|
|
56
|
-
...(this.instance ? { instance: this.instance } : {}),
|
|
57
|
-
...this.extensions,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ==========================================
|
|
63
|
-
// Factory Function
|
|
64
|
-
// ==========================================
|
|
65
|
-
|
|
66
|
-
const DEFAULT_TITLES: Record<number, string> = {
|
|
67
|
-
400: "Bad Request",
|
|
68
|
-
401: "Unauthorized",
|
|
69
|
-
402: "Payment Required",
|
|
70
|
-
403: "Forbidden",
|
|
71
|
-
404: "Not Found",
|
|
72
|
-
405: "Method Not Allowed",
|
|
73
|
-
406: "Not Acceptable",
|
|
74
|
-
409: "Conflict",
|
|
75
|
-
500: "Internal Server Error",
|
|
76
|
-
501: "Not Implemented",
|
|
77
|
-
502: "Bad Gateway",
|
|
78
|
-
503: "Service Unavailable",
|
|
79
|
-
504: "Gateway Timeout",
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
export interface ProblemConfig {
|
|
83
|
-
detail?: string;
|
|
84
|
-
extensions?: Record<string, unknown>;
|
|
85
|
-
instance?: string;
|
|
86
|
-
title?: string;
|
|
87
|
-
type?: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* 工厂函数:根据 HTTP 状态码快速创建 ProblemError
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* throw createProblem(400, { detail: 'Invalid email' })
|
|
95
|
-
* throw createProblem(409, { title: 'Duplicate', detail: 'Email already exists' })
|
|
96
|
-
*/
|
|
97
|
-
export const createProblem = (
|
|
98
|
-
status: number,
|
|
99
|
-
overrides?: ProblemConfig
|
|
100
|
-
): ProblemError => {
|
|
101
|
-
const title = overrides?.title ?? DEFAULT_TITLES[status] ?? "Unknown Error";
|
|
102
|
-
return new ProblemError(
|
|
103
|
-
overrides?.type ?? `https://httpstatuses.com/${status}`,
|
|
104
|
-
title,
|
|
105
|
-
status,
|
|
106
|
-
overrides?.detail,
|
|
107
|
-
overrides?.instance,
|
|
108
|
-
overrides?.extensions
|
|
109
|
-
);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// ==========================================
|
|
113
|
-
// HttpError Namespace (convenience wrappers via createProblem)
|
|
114
|
-
// ==========================================
|
|
115
|
-
|
|
116
|
-
type HttpErrorFactory = (
|
|
117
|
-
detail?: string,
|
|
118
|
-
extensions?: Record<string, unknown>
|
|
119
|
-
) => ProblemError;
|
|
120
|
-
|
|
121
|
-
const factory =
|
|
122
|
-
(status: number): HttpErrorFactory =>
|
|
123
|
-
(detail, extensions) =>
|
|
124
|
-
createProblem(status, { detail, extensions });
|
|
125
|
-
|
|
126
|
-
export interface HttpErrorConstructor {
|
|
127
|
-
BadGateway: HttpErrorFactory;
|
|
128
|
-
BadRequest: HttpErrorFactory;
|
|
129
|
-
Conflict: HttpErrorFactory;
|
|
130
|
-
Forbidden: HttpErrorFactory;
|
|
131
|
-
GatewayTimeout: HttpErrorFactory;
|
|
132
|
-
InternalServerError: HttpErrorFactory;
|
|
133
|
-
MethodNotAllowed: HttpErrorFactory;
|
|
134
|
-
NotAcceptable: HttpErrorFactory;
|
|
135
|
-
NotFound: HttpErrorFactory;
|
|
136
|
-
NotImplemented: HttpErrorFactory;
|
|
137
|
-
PaymentRequired: HttpErrorFactory;
|
|
138
|
-
ServiceUnavailable: HttpErrorFactory;
|
|
139
|
-
Unauthorized: HttpErrorFactory;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export const HttpError: HttpErrorConstructor = {
|
|
143
|
-
BadRequest: factory(400),
|
|
144
|
-
Unauthorized: factory(401),
|
|
145
|
-
PaymentRequired: factory(402),
|
|
146
|
-
Forbidden: factory(403),
|
|
147
|
-
NotFound: factory(404),
|
|
148
|
-
MethodNotAllowed: factory(405),
|
|
149
|
-
NotAcceptable: factory(406),
|
|
150
|
-
Conflict: factory(409),
|
|
151
|
-
InternalServerError: factory(500),
|
|
152
|
-
NotImplemented: factory(501),
|
|
153
|
-
BadGateway: factory(502),
|
|
154
|
-
ServiceUnavailable: factory(503),
|
|
155
|
-
GatewayTimeout: factory(504),
|
|
156
|
-
};
|
|
1
|
+
// RFC 9457 Problem Details - https://www.rfc-editor.org/rfc/rfc9457.html
|
|
2
|
+
|
|
3
|
+
export interface ProblemDocument {
|
|
4
|
+
detail?: string;
|
|
5
|
+
instance?: string;
|
|
6
|
+
status?: number;
|
|
7
|
+
title: string;
|
|
8
|
+
type: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* RFC 9457 Problem Details Error
|
|
14
|
+
*
|
|
15
|
+
* Core members:
|
|
16
|
+
* - type: URI reference identifying the problem type (default "about:blank")
|
|
17
|
+
* - title: Short human-readable summary
|
|
18
|
+
* - status: HTTP status code
|
|
19
|
+
* - detail: Human-readable explanation for this occurrence
|
|
20
|
+
* - instance: URI reference identifying this specific occurrence
|
|
21
|
+
* - extensions: Additional properties serialized as-is
|
|
22
|
+
*/
|
|
23
|
+
export class ProblemError extends Error {
|
|
24
|
+
public readonly status: number;
|
|
25
|
+
public readonly title: string;
|
|
26
|
+
public readonly type: string;
|
|
27
|
+
public readonly detail?: string;
|
|
28
|
+
public readonly instance?: string;
|
|
29
|
+
public readonly extensions?: Record<string, unknown>;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
type = "about:blank",
|
|
33
|
+
title: string,
|
|
34
|
+
status: number,
|
|
35
|
+
detail?: string,
|
|
36
|
+
instance?: string,
|
|
37
|
+
extensions?: Record<string, unknown>
|
|
38
|
+
) {
|
|
39
|
+
super(detail || title);
|
|
40
|
+
Object.setPrototypeOf(this, ProblemError.prototype);
|
|
41
|
+
|
|
42
|
+
this.status = status;
|
|
43
|
+
this.title = title;
|
|
44
|
+
this.type = type;
|
|
45
|
+
this.detail = detail;
|
|
46
|
+
this.instance = instance;
|
|
47
|
+
this.extensions = extensions;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
toJSON(): ProblemDocument {
|
|
51
|
+
return {
|
|
52
|
+
type: this.type,
|
|
53
|
+
title: this.title,
|
|
54
|
+
status: this.status,
|
|
55
|
+
...(this.detail ? { detail: this.detail } : {}),
|
|
56
|
+
...(this.instance ? { instance: this.instance } : {}),
|
|
57
|
+
...this.extensions,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ==========================================
|
|
63
|
+
// Factory Function
|
|
64
|
+
// ==========================================
|
|
65
|
+
|
|
66
|
+
const DEFAULT_TITLES: Record<number, string> = {
|
|
67
|
+
400: "Bad Request",
|
|
68
|
+
401: "Unauthorized",
|
|
69
|
+
402: "Payment Required",
|
|
70
|
+
403: "Forbidden",
|
|
71
|
+
404: "Not Found",
|
|
72
|
+
405: "Method Not Allowed",
|
|
73
|
+
406: "Not Acceptable",
|
|
74
|
+
409: "Conflict",
|
|
75
|
+
500: "Internal Server Error",
|
|
76
|
+
501: "Not Implemented",
|
|
77
|
+
502: "Bad Gateway",
|
|
78
|
+
503: "Service Unavailable",
|
|
79
|
+
504: "Gateway Timeout",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export interface ProblemConfig {
|
|
83
|
+
detail?: string;
|
|
84
|
+
extensions?: Record<string, unknown>;
|
|
85
|
+
instance?: string;
|
|
86
|
+
title?: string;
|
|
87
|
+
type?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 工厂函数:根据 HTTP 状态码快速创建 ProblemError
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* throw createProblem(400, { detail: 'Invalid email' })
|
|
95
|
+
* throw createProblem(409, { title: 'Duplicate', detail: 'Email already exists' })
|
|
96
|
+
*/
|
|
97
|
+
export const createProblem = (
|
|
98
|
+
status: number,
|
|
99
|
+
overrides?: ProblemConfig
|
|
100
|
+
): ProblemError => {
|
|
101
|
+
const title = overrides?.title ?? DEFAULT_TITLES[status] ?? "Unknown Error";
|
|
102
|
+
return new ProblemError(
|
|
103
|
+
overrides?.type ?? `https://httpstatuses.com/${status}`,
|
|
104
|
+
title,
|
|
105
|
+
status,
|
|
106
|
+
overrides?.detail,
|
|
107
|
+
overrides?.instance,
|
|
108
|
+
overrides?.extensions
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// ==========================================
|
|
113
|
+
// HttpError Namespace (convenience wrappers via createProblem)
|
|
114
|
+
// ==========================================
|
|
115
|
+
|
|
116
|
+
type HttpErrorFactory = (
|
|
117
|
+
detail?: string,
|
|
118
|
+
extensions?: Record<string, unknown>
|
|
119
|
+
) => ProblemError;
|
|
120
|
+
|
|
121
|
+
const factory =
|
|
122
|
+
(status: number): HttpErrorFactory =>
|
|
123
|
+
(detail, extensions) =>
|
|
124
|
+
createProblem(status, { detail, extensions });
|
|
125
|
+
|
|
126
|
+
export interface HttpErrorConstructor {
|
|
127
|
+
BadGateway: HttpErrorFactory;
|
|
128
|
+
BadRequest: HttpErrorFactory;
|
|
129
|
+
Conflict: HttpErrorFactory;
|
|
130
|
+
Forbidden: HttpErrorFactory;
|
|
131
|
+
GatewayTimeout: HttpErrorFactory;
|
|
132
|
+
InternalServerError: HttpErrorFactory;
|
|
133
|
+
MethodNotAllowed: HttpErrorFactory;
|
|
134
|
+
NotAcceptable: HttpErrorFactory;
|
|
135
|
+
NotFound: HttpErrorFactory;
|
|
136
|
+
NotImplemented: HttpErrorFactory;
|
|
137
|
+
PaymentRequired: HttpErrorFactory;
|
|
138
|
+
ServiceUnavailable: HttpErrorFactory;
|
|
139
|
+
Unauthorized: HttpErrorFactory;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const HttpError: HttpErrorConstructor = {
|
|
143
|
+
BadRequest: factory(400),
|
|
144
|
+
Unauthorized: factory(401),
|
|
145
|
+
PaymentRequired: factory(402),
|
|
146
|
+
Forbidden: factory(403),
|
|
147
|
+
NotFound: factory(404),
|
|
148
|
+
MethodNotAllowed: factory(405),
|
|
149
|
+
NotAcceptable: factory(406),
|
|
150
|
+
Conflict: factory(409),
|
|
151
|
+
InternalServerError: factory(500),
|
|
152
|
+
NotImplemented: factory(501),
|
|
153
|
+
BadGateway: factory(502),
|
|
154
|
+
ServiceUnavailable: factory(503),
|
|
155
|
+
GatewayTimeout: factory(504),
|
|
156
|
+
};
|
package/src/Error/type.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export type Code =
|
|
2
|
-
| number
|
|
3
|
-
| "PROBLEM_ERROR"
|
|
4
|
-
| "UNKNOWN"
|
|
5
|
-
| "VALIDATION"
|
|
6
|
-
| "NOT_FOUND"
|
|
7
|
-
| "PARSE"
|
|
8
|
-
| "INTERNAL_SERVER_ERROR"
|
|
9
|
-
| "INVALID_COOKIE_SIGNATURE"
|
|
10
|
-
| "INVALID_FILE_TYPE";
|
|
1
|
+
export type Code =
|
|
2
|
+
| number
|
|
3
|
+
| "PROBLEM_ERROR"
|
|
4
|
+
| "UNKNOWN"
|
|
5
|
+
| "VALIDATION"
|
|
6
|
+
| "NOT_FOUND"
|
|
7
|
+
| "PARSE"
|
|
8
|
+
| "INTERNAL_SERVER_ERROR"
|
|
9
|
+
| "INVALID_COOKIE_SIGNATURE"
|
|
10
|
+
| "INVALID_FILE_TYPE";
|
package/src/extensions/banner.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import elysiaPkg from "elysia/package.json";
|
|
2
|
-
|
|
3
|
-
const centerText = (text: string, width: number): string => {
|
|
4
|
-
if (text.length >= width) {
|
|
5
|
-
return text.slice(0, width);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const left = Math.floor((width - text.length) / 2);
|
|
9
|
-
const right = width - text.length - left;
|
|
10
|
-
return `${" ".repeat(left)}${text}${" ".repeat(right)}`;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const renderBanner = (message: string): string => {
|
|
14
|
-
const versionLine = `Elysia v${elysiaPkg.version}`;
|
|
15
|
-
const contentWidth = Math.max(message.length, versionLine.length);
|
|
16
|
-
const innerWidth = contentWidth + 4; // 2 spaces padding on both sides
|
|
17
|
-
|
|
18
|
-
const top = `┌${"─".repeat(innerWidth)}┐`;
|
|
19
|
-
const bot = `└${"─".repeat(innerWidth)}┘`;
|
|
20
|
-
const empty = `│${" ".repeat(innerWidth)}│`;
|
|
21
|
-
|
|
22
|
-
const versionRow = `│${centerText(versionLine, innerWidth)}│`;
|
|
23
|
-
const messageRow = `│ ${message}${" ".repeat(Math.max(0, innerWidth - message.length - 4))} │`;
|
|
24
|
-
|
|
25
|
-
return [top, empty, versionRow, empty, messageRow, empty, bot].join("\n");
|
|
26
|
-
};
|
|
1
|
+
import elysiaPkg from "elysia/package.json";
|
|
2
|
+
|
|
3
|
+
const centerText = (text: string, width: number): string => {
|
|
4
|
+
if (text.length >= width) {
|
|
5
|
+
return text.slice(0, width);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const left = Math.floor((width - text.length) / 2);
|
|
9
|
+
const right = width - text.length - left;
|
|
10
|
+
return `${" ".repeat(left)}${text}${" ".repeat(right)}`;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const renderBanner = (message: string): string => {
|
|
14
|
+
const versionLine = `Elysia v${elysiaPkg.version}`;
|
|
15
|
+
const contentWidth = Math.max(message.length, versionLine.length);
|
|
16
|
+
const innerWidth = contentWidth + 4; // 2 spaces padding on both sides
|
|
17
|
+
|
|
18
|
+
const top = `┌${"─".repeat(innerWidth)}┐`;
|
|
19
|
+
const bot = `└${"─".repeat(innerWidth)}┘`;
|
|
20
|
+
const empty = `│${" ".repeat(innerWidth)}│`;
|
|
21
|
+
|
|
22
|
+
const versionRow = `│${centerText(versionLine, innerWidth)}│`;
|
|
23
|
+
const messageRow = `│ ${message}${" ".repeat(Math.max(0, innerWidth - message.length - 4))} │`;
|
|
24
|
+
|
|
25
|
+
return [top, empty, versionRow, empty, messageRow, empty, bot].join("\n");
|
|
26
|
+
};
|
package/src/extensions/index.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import type { Options } from "../interfaces";
|
|
2
|
-
import { renderBanner } from "./banner";
|
|
3
|
-
|
|
4
|
-
export const startServer = (
|
|
5
|
-
server: { port?: number; hostname?: string; protocol?: string | null },
|
|
6
|
-
options: Options
|
|
7
|
-
): void => {
|
|
8
|
-
const showStartupMessage = options.startup?.show ?? true;
|
|
9
|
-
if (!showStartupMessage) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const { port, hostname, protocol } = server;
|
|
14
|
-
if (port === undefined || !hostname || !protocol) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const url = `${protocol}://${hostname}:${port}`;
|
|
19
|
-
const message = `🦊 Elysia is running at ${url}`;
|
|
20
|
-
|
|
21
|
-
const format = options.startup?.format ?? "banner";
|
|
22
|
-
if (format === "simple") {
|
|
23
|
-
console.log(message);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
console.log(renderBanner(message));
|
|
28
|
-
};
|
|
1
|
+
import type { Options } from "../interfaces";
|
|
2
|
+
import { renderBanner } from "./banner";
|
|
3
|
+
|
|
4
|
+
export const startServer = (
|
|
5
|
+
server: { port?: number; hostname?: string; protocol?: string | null },
|
|
6
|
+
options: Options
|
|
7
|
+
): void => {
|
|
8
|
+
const showStartupMessage = options.startup?.show ?? true;
|
|
9
|
+
if (!showStartupMessage) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { port, hostname, protocol } = server;
|
|
14
|
+
if (port === undefined || !hostname || !protocol) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const url = `${protocol}://${hostname}:${port}`;
|
|
19
|
+
const message = `🦊 Elysia is running at ${url}`;
|
|
20
|
+
|
|
21
|
+
const format = options.startup?.format ?? "banner";
|
|
22
|
+
if (format === "simple") {
|
|
23
|
+
console.log(message);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(renderBanner(message));
|
|
28
|
+
};
|
package/src/helpers/status.ts
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import { StatusMap } from "elysia";
|
|
2
|
-
|
|
3
|
-
const DIGITS_ONLY = /^\d+$/;
|
|
4
|
-
const DELIMITERS = /[_-]+/g;
|
|
5
|
-
const CAMEL_BOUNDARY_1 = /([a-z0-9])([A-Z])/g;
|
|
6
|
-
const CAMEL_BOUNDARY_2 = /([A-Z])([A-Z][a-z])/g;
|
|
7
|
-
const APOSTROPHES = /['’]/g;
|
|
8
|
-
const NON_ALPHANUMERIC = /[^a-z0-9\s]+/g;
|
|
9
|
-
const WHITESPACE = /\s+/g;
|
|
10
|
-
|
|
11
|
-
const normalizeStatusName = (value: string): string => {
|
|
12
|
-
// Handles common variants:
|
|
13
|
-
// - case differences: "not found" vs "Not Found"
|
|
14
|
-
// - spacing/punctuation: "Not-Found", "not_found"
|
|
15
|
-
// - camelCase/PascalCase: "InternalServerError"
|
|
16
|
-
const trimmed = value.trim();
|
|
17
|
-
if (!trimmed) {
|
|
18
|
-
return "";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return trimmed
|
|
22
|
-
.replace(DELIMITERS, " ")
|
|
23
|
-
.replace(CAMEL_BOUNDARY_1, "$1 $2")
|
|
24
|
-
.replace(CAMEL_BOUNDARY_2, "$1 $2")
|
|
25
|
-
.replace(APOSTROPHES, "")
|
|
26
|
-
.toLowerCase()
|
|
27
|
-
.replace(NON_ALPHANUMERIC, " ")
|
|
28
|
-
.replace(WHITESPACE, " ")
|
|
29
|
-
.trim();
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const STATUS_BY_NORMALIZED_NAME = (() => {
|
|
33
|
-
const map = new Map<string, number>();
|
|
34
|
-
|
|
35
|
-
for (const [name, code] of Object.entries(StatusMap)) {
|
|
36
|
-
map.set(normalizeStatusName(name), code);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return map;
|
|
40
|
-
})();
|
|
41
|
-
|
|
42
|
-
export const getStatusCode = (value: unknown): number => {
|
|
43
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (typeof value === "string") {
|
|
48
|
-
const trimmed = value.trim();
|
|
49
|
-
if (DIGITS_ONLY.test(trimmed)) {
|
|
50
|
-
return Number(trimmed);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const known = STATUS_BY_NORMALIZED_NAME.get(normalizeStatusName(trimmed));
|
|
54
|
-
return known ?? 500;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return 500;
|
|
58
|
-
};
|
|
1
|
+
import { StatusMap } from "elysia";
|
|
2
|
+
|
|
3
|
+
const DIGITS_ONLY = /^\d+$/;
|
|
4
|
+
const DELIMITERS = /[_-]+/g;
|
|
5
|
+
const CAMEL_BOUNDARY_1 = /([a-z0-9])([A-Z])/g;
|
|
6
|
+
const CAMEL_BOUNDARY_2 = /([A-Z])([A-Z][a-z])/g;
|
|
7
|
+
const APOSTROPHES = /['’]/g;
|
|
8
|
+
const NON_ALPHANUMERIC = /[^a-z0-9\s]+/g;
|
|
9
|
+
const WHITESPACE = /\s+/g;
|
|
10
|
+
|
|
11
|
+
const normalizeStatusName = (value: string): string => {
|
|
12
|
+
// Handles common variants:
|
|
13
|
+
// - case differences: "not found" vs "Not Found"
|
|
14
|
+
// - spacing/punctuation: "Not-Found", "not_found"
|
|
15
|
+
// - camelCase/PascalCase: "InternalServerError"
|
|
16
|
+
const trimmed = value.trim();
|
|
17
|
+
if (!trimmed) {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return trimmed
|
|
22
|
+
.replace(DELIMITERS, " ")
|
|
23
|
+
.replace(CAMEL_BOUNDARY_1, "$1 $2")
|
|
24
|
+
.replace(CAMEL_BOUNDARY_2, "$1 $2")
|
|
25
|
+
.replace(APOSTROPHES, "")
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.replace(NON_ALPHANUMERIC, " ")
|
|
28
|
+
.replace(WHITESPACE, " ")
|
|
29
|
+
.trim();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const STATUS_BY_NORMALIZED_NAME = (() => {
|
|
33
|
+
const map = new Map<string, number>();
|
|
34
|
+
|
|
35
|
+
for (const [name, code] of Object.entries(StatusMap)) {
|
|
36
|
+
map.set(normalizeStatusName(name), code);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return map;
|
|
40
|
+
})();
|
|
41
|
+
|
|
42
|
+
export const getStatusCode = (value: unknown): number => {
|
|
43
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof value === "string") {
|
|
48
|
+
const trimmed = value.trim();
|
|
49
|
+
if (DIGITS_ONLY.test(trimmed)) {
|
|
50
|
+
return Number(trimmed);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const known = STATUS_BY_NORMALIZED_NAME.get(normalizeStatusName(trimmed));
|
|
54
|
+
return known ?? 500;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return 500;
|
|
58
|
+
};
|