@pori15/logixlysia 6.0.10 → 6.0.11

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/README.md CHANGED
@@ -1,79 +1,79 @@
1
- <div align="center">
2
- <h1><code>🦊</code> Logixlysia</h1>
3
- <strong>Logixlysia is a logging library for ElysiaJS</strong>
4
- <img src="https://github.com/PunGrumpy/logixlysia/blob/main/apps/docs/app/opengraph-image.png?raw=true" alt="Logixlysia" width="100%" height="auto" />
5
- </div>
6
-
7
- ## `📩` Installation
8
-
9
- ```bash
10
- bun add @pori15/logixlysia
11
- ```
12
-
13
- ## `📝` Usage
14
-
15
- ### Basic
16
-
17
- ```ts
18
- import { Elysia } from 'elysia'
19
- import logixlysia from 'logixlysia'
20
-
21
- const app = new Elysia()
22
- .use(logixlysia())
23
- .get('/', () => 'Hello World')
24
- .listen(3000)
25
- ```
26
-
27
- ### With Configuration
28
-
29
- ```ts
30
- app.use(logixlysia({
31
- startup: {
32
- show: true,
33
- format: 'simple',
34
- },
35
- format: {
36
- timestamp: 'yyyy-mm-dd HH:MM:ss.SSS',
37
- showIp: true,
38
- template: '🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}',
39
- },
40
- file: {
41
- path: './logs/example.log',
42
- rotation: {
43
- maxSize: '10m',
44
- maxFiles: 7,
45
- compress: true,
46
- },
47
- },
48
- logLevel: ['ERROR', 'WARNING'],
49
- }))
50
- ```
51
-
52
- ## `⚙️` Options
53
-
54
- | Option | Type | Default | Description |
55
- |--------|------|---------|-------------|
56
- | `startup.show` | `boolean` | `true` | Show startup message |
57
- | `startup.format` | `"simple" \| "banner"` | `"banner"` | Startup message style |
58
- | `format.colors` | `boolean` | `true` (TTY) | Enable colored output |
59
- | `format.timestamp` | `string` | — | Timestamp format pattern (e.g. `'yyyy-mm-dd HH:MM:ss.SSS'`) |
60
- | `format.template` | `string` | — | Custom log format template |
61
- | `format.showIp` | `boolean` | `false` | Show IP address in logs |
62
- | `logLevel` | `LogLevel \| LogLevel[]` | — | Filter logs by level(s) |
63
- | `file` | `false \| { path, rotation? }` | — | File logging config (`false` to disable) |
64
- | `file.path` | `string` | — | Log file path (required when file logging enabled) |
65
- | `file.rotation` | `LogRotationConfig` | — | Log file rotation settings |
66
- | `transports` | `Transport[] \| { targets, only? }` | — | Custom transports |
67
- | `pino` | `PinoLoggerOptions` | — | Pino logger options |
68
- | `error.typeBaseUrl` | `string` | — | Base URL for error types (RFC 9457) |
69
- | `error.errorMap` | `Record<string, ErrorMapping>` | — | Error code to HTTP status mapping |
70
- | `error.resolve` | `ErrorResolver` | — | Custom error resolver function |
71
- | `error.verbose` | `boolean` | `false` | Show full error details in console |
72
-
73
- ## `📚` Documentation
74
-
75
- Check out the [website](https://logixlysia.vercel.app) for more detailed documentation and examples.
76
-
77
- ## `📄` License
78
-
79
- Licensed under the [MIT License](LICENSE).
1
+ <div align="center">
2
+ <h1><code>🦊</code> Logixlysia</h1>
3
+ <strong>Logixlysia is a logging library for ElysiaJS</strong>
4
+ <img src="https://github.com/PunGrumpy/logixlysia/blob/main/apps/docs/app/opengraph-image.png?raw=true" alt="Logixlysia" width="100%" height="auto" />
5
+ </div>
6
+
7
+ ## `📩` Installation
8
+
9
+ ```bash
10
+ bun add @pori15/logixlysia
11
+ ```
12
+
13
+ ## `📝` Usage
14
+
15
+ ### Basic
16
+
17
+ ```ts
18
+ import { Elysia } from 'elysia'
19
+ import logixlysia from 'logixlysia'
20
+
21
+ const app = new Elysia()
22
+ .use(logixlysia())
23
+ .get('/', () => 'Hello World')
24
+ .listen(3000)
25
+ ```
26
+
27
+ ### With Configuration
28
+
29
+ ```ts
30
+ app.use(logixlysia({
31
+ startup: {
32
+ show: true,
33
+ format: 'simple',
34
+ },
35
+ format: {
36
+ timestamp: 'yyyy-mm-dd HH:MM:ss.SSS',
37
+ showIp: true,
38
+ template: '🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}',
39
+ },
40
+ file: {
41
+ path: './logs/example.log',
42
+ rotation: {
43
+ maxSize: '10m',
44
+ maxFiles: 7,
45
+ compress: true,
46
+ },
47
+ },
48
+ logLevel: ['ERROR', 'WARNING'],
49
+ }))
50
+ ```
51
+
52
+ ## `⚙️` Options
53
+
54
+ | Option | Type | Default | Description |
55
+ |--------|------|---------|-------------|
56
+ | `startup.show` | `boolean` | `true` | Show startup message |
57
+ | `startup.format` | `"simple" \| "banner"` | `"banner"` | Startup message style |
58
+ | `format.colors` | `boolean` | `true` (TTY) | Enable colored output |
59
+ | `format.timestamp` | `string` | — | Timestamp format pattern (e.g. `'yyyy-mm-dd HH:MM:ss.SSS'`) |
60
+ | `format.template` | `string` | — | Custom log format template |
61
+ | `format.showIp` | `boolean` | `false` | Show IP address in logs |
62
+ | `logLevel` | `LogLevel \| LogLevel[]` | — | Filter logs by level(s) |
63
+ | `file` | `false \| { path, rotation? }` | — | File logging config (`false` to disable) |
64
+ | `file.path` | `string` | — | Log file path (required when file logging enabled) |
65
+ | `file.rotation` | `LogRotationConfig` | — | Log file rotation settings |
66
+ | `transports` | `Transport[] \| { targets, only? }` | — | Custom transports |
67
+ | `pino` | `PinoLoggerOptions` | — | Pino logger options |
68
+ | `error.typeBaseUrl` | `string` | — | Base URL for error types (RFC 9457) |
69
+ | `error.errorMap` | `Record<string, ErrorMapping>` | — | Error code to HTTP status mapping |
70
+ | `error.resolve` | `ErrorResolver` | — | Custom error resolver function |
71
+ | `error.verbose` | `boolean` | `false` | Show full error details in console |
72
+
73
+ ## `📚` Documentation
74
+
75
+ Check out the [website](https://logixlysia.vercel.app) for more detailed documentation and examples.
76
+
77
+ ## `📄` License
78
+
79
+ Licensed under the [MIT License](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pori15/logixlysia",
3
- "version": "6.0.10",
3
+ "version": "6.0.11",
4
4
  "description": "🦊 Logixlysia is a logger for Elysia",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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
+ };
@@ -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";
@@ -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
+ };
@@ -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
+ };