logixlysia 3.6.1 → 3.7.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.7.0](https://github.com/PunGrumpy/logixlysia/compare/v3.6.2...v3.7.0) (2024-09-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * **optional:** add startup message config ([c7a76db](https://github.com/PunGrumpy/logixlysia/commit/c7a76dbc0f30f4c9c607b52dedc49979e26b67ec))
9
+
10
+ ## [3.6.2](https://github.com/PunGrumpy/logixlysia/compare/v3.6.1...v3.6.2) (2024-09-18)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **logtofile:** store error to log file ([1bb2d07](https://github.com/PunGrumpy/logixlysia/commit/1bb2d07ae0c815742a9eb7c930ac8487ac287b2c)), closes [#63](https://github.com/PunGrumpy/logixlysia/issues/63)
16
+
3
17
  ## [3.6.1](https://github.com/PunGrumpy/logixlysia/compare/v3.6.0...v3.6.1) (2024-08-21)
4
18
 
5
19
 
package/README.md CHANGED
@@ -21,7 +21,8 @@ const app = new Elysia({
21
21
  }).use(
22
22
  logixlysia({
23
23
  config: {
24
- showBanner: true,
24
+ showStartupMessage: true,
25
+ startupMessageFormat: 'simple',
25
26
  ip: true,
26
27
  logFilePath: './logs/example.log',
27
28
  customLogFormat:
@@ -45,13 +46,14 @@ app.listen(3000)
45
46
 
46
47
  ### Options
47
48
 
48
- | Option | Type | Description | Default |
49
- | ------------------ | --------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------- |
50
- | `showBanner` | `boolean` | Display the banner on the console | `true` |
51
- | `ip` | `boolean` | Display the incoming IP address based on the `X-Forwarded-For` header | `false` |
52
- | `customLogMessage` | `string` | Custom log message to display | `🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}` |
53
- | `logFilter` | `object` | Filter the logs based on the level, method, and status | `null` |
54
- | `logFilePath` | `string` | Path to the log file | `./logs/elysia.log` |
49
+ | Option | Type | Description | Default |
50
+ | ---------------------- | ------------------------ | --------------------------------------------------------------------- | ------------------------------------------------------------------------- |
51
+ | `showStartupMessage` | `boolean` | Display the startup message | `true` |
52
+ | `startupMessageFormat` | `"banner"` \| `"simple"` | Choose the startup message format | `"banner"` |
53
+ | `ip` | `boolean` | Display the incoming IP address based on the `X-Forwarded-For` header | `false` |
54
+ | `customLogMessage` | `string` | Custom log message to display | `🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}` |
55
+ | `logFilter` | `object` | Filter the logs based on the level, method, and status | `null` |
56
+ | `logFilePath` | `string` | Path to the log file | `./logs/elysia.log` |
55
57
 
56
58
  ### Custom Log Message
57
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "logixlysia",
3
- "version": "3.6.1",
3
+ "version": "3.7.0",
4
4
  "description": "🦊 Logixlysia is a logger for Elysia",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",
@@ -71,24 +71,21 @@
71
71
  ],
72
72
  "dependencies": {
73
73
  "chalk": "^5.3.0",
74
- "elysia": "^1.1.4"
74
+ "elysia": "^1.1.16"
75
75
  },
76
76
  "devDependencies": {
77
- "@elysiajs/eden": "^1.1.1",
78
- "@eslint/js": "^9.8.0",
79
- "@trunkio/launcher": "^1.3.1",
80
- "@typescript-eslint/eslint-plugin": "^8.0.1",
81
- "@typescript-eslint/parser": "^8.0.1",
82
- "bun-types": "^1.1.20",
83
- "commitizen": "^4.3.0",
84
- "cz-conventional-changelog": "^3.3.0",
85
- "eslint": "9.x",
77
+ "@elysiajs/eden": "^1.1.3",
78
+ "@eslint/js": "^9.11.1",
79
+ "@trunkio/launcher": "^1.3.2",
80
+ "@typescript-eslint/eslint-plugin": "^8.7.0",
81
+ "@typescript-eslint/parser": "^8.7.0",
82
+ "bun-types": "^1.1.29",
86
83
  "eslint-plugin-simple-import-sort": "^12.1.1",
87
84
  "globals": "^15.9.0",
88
- "husky": "^9.1.1",
89
- "lint-staged": "^15.2.7",
85
+ "husky": "^9.1.6",
86
+ "lint-staged": "^15.2.10",
90
87
  "prettier": "^3.3.3",
91
- "typescript-eslint": "^8.0.1"
88
+ "typescript-eslint": "^8.7.0"
92
89
  },
93
90
  "peerDependencies": {
94
91
  "typescript": "^5.2.2"
@@ -7,27 +7,25 @@ import {
7
7
  Options,
8
8
  RequestInfo,
9
9
  StoreData
10
- } from '~/types'
11
- import durationString from '~/utils/duration'
12
- import logString from '~/utils/log'
13
- import methodString from '~/utils/method'
14
- import pathString from '~/utils/path'
15
- import statusString from '~/utils/status'
10
+ } from '../types'
11
+ import {
12
+ durationString,
13
+ logString,
14
+ methodString,
15
+ pathString,
16
+ statusString
17
+ } from '../utils'
16
18
 
17
19
  const defaultLogFormat =
18
20
  '🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}'
19
21
 
20
- /**
21
- * Builds a log message.
22
- *
23
- * @param {LogLevel} level The log level.
24
- * @param {RequestInfo} request The request information.
25
- * @param {LogData} data The log data.
26
- * @param {StoreData} store The store data.
27
- * @param {Options} options The logger options.
28
- * @param {boolean} useColors Whether to apply colors to the log message.
29
- * @returns {string} The formatted log message.
30
- */
22
+ function shouldUseColors(useColors: boolean, options?: Options): boolean {
23
+ if (options?.config?.useColors !== undefined) {
24
+ return options.config.useColors && process.env.NO_COLOR === undefined
25
+ }
26
+ return useColors && process.env.NO_COLOR === undefined
27
+ }
28
+
31
29
  export function buildLogMessage(
32
30
  level: LogLevel,
33
31
  request: RequestInfo,
@@ -36,17 +34,18 @@ export function buildLogMessage(
36
34
  options?: Options,
37
35
  useColors: boolean = true
38
36
  ): string {
37
+ const actuallyUseColors = shouldUseColors(useColors, options)
39
38
  const now = new Date()
40
39
  const components: LogComponents = {
41
- now: useColors
40
+ now: actuallyUseColors
42
41
  ? chalk.bgYellow(chalk.black(now.toLocaleString()))
43
42
  : now.toLocaleString(),
44
43
  epoch: Math.floor(now.getTime() / 1000).toString(),
45
- level: logString(level, useColors),
46
- duration: durationString(store.beforeTime, useColors),
47
- method: methodString(request.method, useColors),
44
+ level: logString(level, actuallyUseColors),
45
+ duration: durationString(store.beforeTime, actuallyUseColors),
46
+ method: methodString(request.method, actuallyUseColors),
48
47
  pathname: pathString(request),
49
- status: statusString(data.status || 200, useColors),
48
+ status: statusString(data.status || 200, actuallyUseColors),
50
49
  message: data.message || '',
51
50
  ip:
52
51
  options?.config?.ip && request.headers.get('x-forwarded-for')
@@ -1,7 +1,5 @@
1
- import { buildLogMessage } from '~/logger/buildLogMessage'
2
- import { filterLog } from '~/logger/filter'
3
- import { logToFile } from '~/logger/logToFile'
4
- import { logToTransports } from '~/transports'
1
+ import { logToTransports } from '../transports'
2
+ import { logToFile } from '../transports'
5
3
  import {
6
4
  LogData,
7
5
  Logger,
@@ -9,17 +7,11 @@ import {
9
7
  Options,
10
8
  RequestInfo,
11
9
  StoreData
12
- } from '~/types'
10
+ } from '../types'
11
+ import { buildLogMessage } from './buildLogMessage'
12
+ import { filterLog } from './filter'
13
+ import { handleHttpError } from './handleHttpError'
13
14
 
14
- /**
15
- * Logs a message to the console and optionally to a file.
16
- *
17
- * @param {LogLevel} level The log level.
18
- * @param {RequestInfo} request The request information.
19
- * @param {LogData} data The log data.
20
- * @param {StoreData} store The store data.
21
- * @param {Options} options The logger options.
22
- */
23
15
  async function log(
24
16
  level: LogLevel,
25
17
  request: RequestInfo,
@@ -54,16 +46,12 @@ async function log(
54
46
  await Promise.all(promises)
55
47
  }
56
48
 
57
- /**
58
- * Creates a logger instance.
59
- *
60
- * @param {Options} options The logger options.
61
- * @returns {Logger} The logger instance.
62
- */
63
49
  export function createLogger(options?: Options): Logger {
64
50
  return {
65
51
  log: (level, request, data, store) =>
66
52
  log(level, request, data, store, options),
53
+ handleHttpError: (request, error, store) =>
54
+ handleHttpError(request, error, store, options),
67
55
  customLogFormat: options?.config?.customLogFormat
68
56
  }
69
57
  }
@@ -1,19 +1,10 @@
1
- import { LogLevel, Options } from '~/types'
1
+ import { LogLevel, Options } from '../types'
2
2
 
3
3
  const checkFilter = (filterValue: any, value: any) =>
4
4
  Array.isArray(filterValue)
5
5
  ? filterValue.includes(value)
6
6
  : filterValue === value
7
7
 
8
- /**
9
- * Filters log messages.
10
- *
11
- * @param {LogLevel} logLevel The log level.
12
- * @param {number} status The status code.
13
- * @param {string} method The method.
14
- * @param {Options} options The options.
15
- * @returns {boolean} `true` if the log message should be logged, otherwise `false`.
16
- */
17
8
  export function filterLog(
18
9
  logLevel: LogLevel,
19
10
  status: number,
@@ -0,0 +1,30 @@
1
+ import { logToFile } from '../transports'
2
+ import { HttpError, Options, RequestInfo, StoreData } from '../types'
3
+ import { buildLogMessage } from './buildLogMessage'
4
+
5
+ export function handleHttpError(
6
+ request: RequestInfo,
7
+ error: HttpError,
8
+ store: StoreData,
9
+ options?: Options
10
+ ): void {
11
+ const statusCode = error.status || 500
12
+ console.error(
13
+ buildLogMessage('ERROR', request, { status: statusCode }, store, options)
14
+ )
15
+
16
+ const promises = []
17
+
18
+ if (options?.config?.logFilePath) {
19
+ promises.push(
20
+ logToFile(
21
+ options.config.logFilePath,
22
+ 'ERROR',
23
+ request,
24
+ { status: statusCode },
25
+ store,
26
+ options
27
+ )
28
+ )
29
+ }
30
+ }
@@ -0,0 +1,2 @@
1
+ export { createLogger } from './createLogger'
2
+ export { handleHttpError } from './handleHttpError'
package/src/index.ts CHANGED
@@ -1,31 +1,19 @@
1
- import { Server } from 'bun'
2
- import Elysia from 'elysia'
1
+ import { Elysia } from 'elysia'
3
2
 
4
- import { createLogger, handleHttpError } from '~/logger'
5
- import { HttpError, Options } from '~/types'
6
- import startServer from '~/utils/start'
3
+ import { createLogger } from './core'
4
+ import { startServer } from './plugins'
5
+ import { HttpError, Options, Server } from './types'
7
6
 
8
- /**
9
- * Creates a logger plugin for ElysiaJS.
10
- *
11
- * @export
12
- * @module logger
13
- * @category Logger
14
- * @subcategory Functions
15
- *
16
- * @name Logixlysia
17
- * @description Logixlysia is a logger plugin for ElysiaJS.
18
- * @param {Options} [options] Configuration options for the logger.
19
- *
20
- * @returns {Elysia} The logger plugin for ElysiaJS.
21
- */
22
7
  export default function logixlysia(options?: Options): Elysia {
23
8
  const log = createLogger(options)
24
9
 
25
10
  return new Elysia({
26
11
  name: 'Logixlysia'
27
12
  })
28
- .onStart(ctx => startServer(ctx.server as Server, options))
13
+ .onStart(ctx => {
14
+ const showStartupMessage = options?.config?.showStartupMessage ?? true
15
+ if (showStartupMessage) startServer(ctx.server as Server, options)}
16
+ )
29
17
  .onRequest(ctx => {
30
18
  ctx.store = { beforeTime: process.hrtime.bigint() }
31
19
  })
@@ -33,14 +21,14 @@ export default function logixlysia(options?: Options): Elysia {
33
21
  log.log('INFO', request, { status: 200 }, store as { beforeTime: bigint })
34
22
  })
35
23
  .onError({ as: 'global' }, ({ request, error, store }) => {
36
- handleHttpError(
24
+ log.handleHttpError(
37
25
  request,
38
26
  error as HttpError,
39
- store as { beforeTime: bigint },
40
- options
27
+ store as { beforeTime: bigint }
41
28
  )
42
29
  })
43
30
  }
44
31
 
45
- export { createLogger } from '~/logger'
46
- export { handleHttpError } from '~/logger'
32
+ export { createLogger, handleHttpError } from './core'
33
+ export { logToTransports } from './transports'
34
+
@@ -0,0 +1 @@
1
+ export { default as startServer } from './startServer'
@@ -1,27 +1,14 @@
1
- import { Options, Server } from '~/types'
1
+ import { Options, Server } from '../types'
2
2
 
3
- /**
4
- * Creates a box text.
5
- *
6
- * @param {string} text The text.
7
- * @param {number} width The box width.
8
- * @returns {string} The box text.
9
- */
10
3
  const createBoxText = (text: string, width: number): string => {
11
4
  const paddingLength = Math.max(0, (width - text.length) / 2)
12
5
  const padding = ' '.repeat(paddingLength)
13
6
  return `${padding}${text}${padding}`.padEnd(width)
14
7
  }
15
8
 
16
- /**
17
- * Starts the server string.
18
- *
19
- * @param {Server} config The server configuration.
20
- * @returns {void} The server string.
21
- */
22
- function startServer(config: Server, options?: Options): void {
9
+ export default function startServer(config: Server, options?: Options): void {
23
10
  const { hostname, port, protocol } = config
24
- const showBanner = options?.config?.showBanner ?? true
11
+ const showBanner = options?.config?.startupMessageFormat !== 'simple'
25
12
 
26
13
  if (showBanner) {
27
14
  const ELYSIA_VERSION = import.meta.require('elysia/package.json').version
@@ -44,5 +31,3 @@ function startServer(config: Server, options?: Options): void {
44
31
  console.log(`🦊 Elysia is running at ${protocol}://${hostname}:${port}`)
45
32
  }
46
33
  }
47
-
48
- export default startServer
@@ -0,0 +1,22 @@
1
+ import { buildLogMessage } from '../core/buildLogMessage'
2
+ import { LogData, LogLevel, Options, RequestInfo, StoreData } from '../types'
3
+
4
+ export async function logToTransports(
5
+ level: LogLevel,
6
+ request: RequestInfo,
7
+ data: LogData,
8
+ store: StoreData,
9
+ options?: Options
10
+ ): Promise<void> {
11
+ if (!options?.config?.transports || options.config.transports.length === 0) {
12
+ return
13
+ }
14
+
15
+ const message = buildLogMessage(level, request, data, store, options, false)
16
+
17
+ const promises = options.config.transports.map(transport =>
18
+ transport.log(level, message, { request, data, store })
19
+ )
20
+
21
+ await Promise.all(promises)
22
+ }
@@ -1,16 +1,11 @@
1
1
  import { promises as fs } from 'fs'
2
2
  import { dirname } from 'path'
3
3
 
4
- import { buildLogMessage } from '~/logger/buildLogMessage'
5
- import { LogData, LogLevel, Options, RequestInfo, StoreData } from '~/types'
4
+ import { buildLogMessage } from '../core/buildLogMessage'
5
+ import { LogData, LogLevel, Options, RequestInfo, StoreData } from '../types'
6
6
 
7
7
  const dirCache = new Set<string>()
8
8
 
9
- /**
10
- * Ensures that the directory exists. If not, it creates the directory.
11
- *
12
- * @param {string} filePath The path to the log file.
13
- */
14
9
  async function ensureDirectoryExists(filePath: string): Promise<void> {
15
10
  const dir = dirname(filePath)
16
11
  if (!dirCache.has(dir)) {
@@ -19,16 +14,6 @@ async function ensureDirectoryExists(filePath: string): Promise<void> {
19
14
  }
20
15
  }
21
16
 
22
- /**
23
- * Logs a message to a file.
24
- *
25
- * @param {string} filePath The path to the log file.
26
- * @param {LogLevel} level The log level.
27
- * @param {RequestInfo} request The request information.
28
- * @param {LogData} data The log data.
29
- * @param {StoreData} store The store data.
30
- * @param {Options} options The logger options.
31
- */
32
17
  export async function logToFile(
33
18
  filePath: string,
34
19
  level: LogLevel,
@@ -1,22 +1,2 @@
1
- import { buildLogMessage } from '~/logger/buildLogMessage'
2
- import { LogData, LogLevel, Options, RequestInfo, StoreData } from '~/types'
3
-
4
- export async function logToTransports(
5
- level: LogLevel,
6
- request: RequestInfo,
7
- data: LogData,
8
- store: StoreData,
9
- options?: Options
10
- ): Promise<void> {
11
- if (!options?.config?.transports || options.config.transports.length === 0) {
12
- return
13
- }
14
-
15
- const message = buildLogMessage(level, request, data, store, options, false)
16
-
17
- const promises = options.config.transports.map(transport =>
18
- transport.log(level, message, { request, data, store })
19
- )
20
-
21
- await Promise.all(promises)
22
- }
1
+ export { logToTransports } from './console'
2
+ export { logToFile } from './file'
@@ -1,37 +1,42 @@
1
- interface RequestInfo {
1
+ export interface RequestInfo {
2
2
  headers: { get: (key: string) => string | null }
3
3
  method: string
4
4
  url: string
5
5
  }
6
6
 
7
- interface Server {
7
+ export interface Server {
8
8
  hostname?: string
9
9
  port?: number
10
10
  protocol?: string
11
11
  }
12
12
 
13
- interface ColorMap {
13
+ export interface ColorMap {
14
14
  [key: string]: (str: string) => string
15
15
  }
16
16
 
17
- type LogLevel = 'INFO' | 'WARNING' | 'ERROR' | string
17
+ export type LogLevel = 'INFO' | 'WARNING' | 'ERROR' | string
18
18
 
19
- interface LogData {
19
+ export interface LogData {
20
20
  status?: number
21
21
  message?: string
22
22
  }
23
23
 
24
- interface Logger {
24
+ export interface Logger {
25
25
  log(
26
26
  level: LogLevel,
27
27
  request: RequestInfo,
28
28
  data: LogData,
29
29
  store: StoreData
30
30
  ): void
31
+ handleHttpError(
32
+ request: RequestInfo,
33
+ error: HttpError,
34
+ store: StoreData
35
+ ): void
31
36
  customLogFormat?: string
32
37
  }
33
38
 
34
- interface LogComponents {
39
+ export interface LogComponents {
35
40
  now: string
36
41
  epoch: string
37
42
  level: string
@@ -43,11 +48,11 @@ interface LogComponents {
43
48
  ip: string
44
49
  }
45
50
 
46
- interface StoreData {
51
+ export interface StoreData {
47
52
  beforeTime: bigint
48
53
  }
49
54
 
50
- class HttpError extends Error {
55
+ export class HttpError extends Error {
51
56
  status: number
52
57
 
53
58
  constructor(status: number, message: string) {
@@ -56,7 +61,7 @@ class HttpError extends Error {
56
61
  }
57
62
  }
58
63
 
59
- interface TransportFunction {
64
+ export interface TransportFunction {
60
65
  (
61
66
  level: LogLevel,
62
67
  message: string,
@@ -68,11 +73,11 @@ interface TransportFunction {
68
73
  ): Promise<void> | void
69
74
  }
70
75
 
71
- interface Transport {
76
+ export interface Transport {
72
77
  log: TransportFunction
73
78
  }
74
79
 
75
- interface Options {
80
+ export interface Options {
76
81
  config?: {
77
82
  customLogFormat?: string
78
83
  logFilePath?: string
@@ -82,22 +87,9 @@ interface Options {
82
87
  status?: number | number[]
83
88
  } | null
84
89
  ip?: boolean
85
- showBanner?: boolean
90
+ useColors?: boolean
91
+ showStartupMessage?: boolean
92
+ startupMessageFormat?: 'banner' | 'simple'
86
93
  transports?: Transport[]
87
94
  }
88
95
  }
89
-
90
- export {
91
- ColorMap,
92
- HttpError,
93
- LogComponents,
94
- LogData,
95
- Logger,
96
- LogLevel,
97
- Options,
98
- RequestInfo,
99
- Server,
100
- StoreData,
101
- Transport,
102
- TransportFunction
103
- }
@@ -1,24 +1,14 @@
1
1
  import chalk from 'chalk'
2
2
 
3
- import { ColorMap } from '~/types'
3
+ import { ColorMap } from '../types'
4
4
 
5
- /**
6
- * The color map for the log levels.
7
- *
8
- * @type {ColorMap}
9
- */
10
- const LogLevelColorMap: ColorMap = {
5
+ export const LogLevelColorMap: ColorMap = {
11
6
  INFO: chalk.bgGreen.black,
12
7
  WARNING: chalk.bgYellow.black,
13
8
  ERROR: chalk.bgRed.black
14
9
  }
15
10
 
16
- /**
17
- * The color map for the HTTP methods.
18
- *
19
- * @type {ColorMap}
20
- */
21
- const HttpMethodColorMap: ColorMap = {
11
+ export const HttpMethodColorMap: ColorMap = {
22
12
  GET: chalk.green,
23
13
  POST: chalk.yellow,
24
14
  PUT: chalk.blue,
@@ -27,5 +17,3 @@ const HttpMethodColorMap: ColorMap = {
27
17
  HEAD: chalk.cyan,
28
18
  OPTIONS: chalk.magenta
29
19
  }
30
-
31
- export { HttpMethodColorMap, LogLevelColorMap }
@@ -7,14 +7,10 @@ const timeUnits = [
7
7
  { unit: 'ns', threshold: 1, decimalPlaces: 0 }
8
8
  ]
9
9
 
10
- /**
11
- * Converts a time difference into a formatted string with the most appropriate time unit.
12
- *
13
- * @param {bigint} beforeTime - The timestamp taken before the request.
14
- * @param {boolean} useColors - Whether to apply colors to the output.
15
- * @returns {string} A formatted duration string including the time unit.
16
- */
17
- function durationString(beforeTime: bigint, useColors: boolean): string {
10
+ export default function durationString(
11
+ beforeTime: bigint,
12
+ useColors: boolean
13
+ ): string {
18
14
  const nanoseconds = Number(process.hrtime.bigint() - beforeTime)
19
15
 
20
16
  for (const { unit, threshold, decimalPlaces } of timeUnits) {
@@ -29,5 +25,3 @@ function durationString(beforeTime: bigint, useColors: boolean): string {
29
25
  ? chalk.gray('0ns'.padStart(8).padEnd(16))
30
26
  : '0ns'.padStart(8).padEnd(16)
31
27
  }
32
-
33
- export default durationString
@@ -0,0 +1,6 @@
1
+ export { HttpMethodColorMap, LogLevelColorMap } from './colorMapping'
2
+ export { default as durationString } from './duration'
3
+ export { default as logString } from './log'
4
+ export { default as methodString } from './method'
5
+ export { default as pathString } from './path'
6
+ export { default as statusString } from './status'
package/src/utils/log.ts CHANGED
@@ -1,18 +1,9 @@
1
- import { LogLevel } from '~/types'
2
- import { LogLevelColorMap } from '~/utils/colorMapping'
1
+ import { LogLevel } from '../types'
2
+ import { LogLevelColorMap } from './colorMapping'
3
3
 
4
- /**
5
- * Converts the log level to a string.
6
- *
7
- * @param {LogLevel} level The log level.
8
- * @param {boolean} useColors - Whether to apply colors to the output.
9
- * @returns {string} The log level as a string.
10
- */
11
- const logString = (level: LogLevel, useColors: boolean): string => {
4
+ export default function logString(level: LogLevel, useColors: boolean): string {
12
5
  const levelStr = level.toUpperCase()
13
6
  return useColors
14
7
  ? LogLevelColorMap[levelStr]?.(levelStr.padEnd(7)) || levelStr
15
8
  : levelStr.padEnd(7)
16
9
  }
17
-
18
- export default logString
@@ -1,17 +1,11 @@
1
- import { HttpMethodColorMap } from '~/utils/colorMapping'
1
+ import { HttpMethodColorMap } from './colorMapping'
2
2
 
3
- /**
4
- * Converts an HTTP request method to a colored string representation.
5
- *
6
- * @param {string} method The HTTP request method (e.g., 'GET', 'POST').
7
- * @param {boolean} useColors - Whether to apply colors to the output.
8
- * @returns {string} A string representing the method.
9
- */
10
- function methodString(method: string, useColors: boolean): string {
3
+ export default function methodString(
4
+ method: string,
5
+ useColors: boolean
6
+ ): string {
11
7
  const colorFunction = HttpMethodColorMap[method]
12
8
  return useColors && colorFunction
13
9
  ? colorFunction(method.padEnd(7))
14
10
  : method.padEnd(7)
15
11
  }
16
-
17
- export default methodString
package/src/utils/path.ts CHANGED
@@ -1,17 +1,11 @@
1
- import { RequestInfo } from '~/types'
1
+ import { RequestInfo } from '../types'
2
2
 
3
- /**
4
- * Returns the path string.
5
- *
6
- * @param {RequestInfo} requestInfo The request info.
7
- * @returns {string | undefined} The path string.
8
- */
9
- const pathString = (requestInfo: RequestInfo): string | undefined => {
3
+ export default function pathString(
4
+ requestInfo: RequestInfo
5
+ ): string | undefined {
10
6
  try {
11
7
  return new URL(requestInfo.url).pathname
12
8
  } catch {
13
9
  return undefined
14
10
  }
15
11
  }
16
-
17
- export default pathString
@@ -1,13 +1,9 @@
1
1
  import chalk from 'chalk'
2
2
 
3
- /**
4
- * Converts the status code to a string.
5
- *
6
- * @param {number} status The status code.
7
- * @param {boolean} useColors - Whether to apply colors to the output.
8
- * @returns {string} The status code as a string.
9
- */
10
- const statusString = (status: number, useColors: boolean): string => {
3
+ export default function statusString(
4
+ status: number,
5
+ useColors: boolean
6
+ ): string {
11
7
  const color =
12
8
  status >= 500
13
9
  ? 'red'
@@ -20,5 +16,3 @@ const statusString = (status: number, useColors: boolean): string => {
20
16
  : 'white'
21
17
  return useColors ? chalk[color](status.toString()) : status.toString()
22
18
  }
23
-
24
- export default statusString
@@ -0,0 +1,9 @@
1
+ import { mock } from 'bun:test'
2
+
3
+ export const mockAppend = mock(() => Promise.resolve())
4
+ export const mockMkdir = mock(() => Promise.resolve())
5
+
6
+ export const promises = {
7
+ appendFile: mockAppend,
8
+ mkdir: mockMkdir
9
+ }
@@ -0,0 +1,45 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import { buildLogMessage } from '../../src/core/buildLogMessage'
4
+ import { LogData, LogLevel, Options, StoreData } from '../../src/types'
5
+ import { createMockRequest } from '../helpers'
6
+
7
+ test('buildLogMessage', () => {
8
+ const level: LogLevel = 'INFO'
9
+ const request = createMockRequest()
10
+ const data: LogData = { status: 200, message: 'Test message' }
11
+ const store: StoreData = { beforeTime: BigInt(0) }
12
+ const options: Options = {
13
+ config: {
14
+ ip: true,
15
+ customLogFormat: '{level} {message} {ip}'
16
+ }
17
+ }
18
+
19
+ const message = buildLogMessage(level, request, data, store, options, false)
20
+ expect(message).toContain('INFO')
21
+ expect(message).toContain('Test message')
22
+ expect(message).toContain('127.0.0.1')
23
+
24
+ const colorMessage = buildLogMessage(
25
+ level,
26
+ request,
27
+ data,
28
+ store,
29
+ options,
30
+ true
31
+ )
32
+
33
+ expect(colorMessage).toContain('INFO')
34
+ expect(colorMessage).toContain('Test message')
35
+ expect(colorMessage).toContain('127.0.0.1')
36
+
37
+ const hasAnsiCodes = /\\x1B\[[0-9;]*m/.test(colorMessage)
38
+ if (hasAnsiCodes) {
39
+ expect(colorMessage).not.toBe(message)
40
+ } else {
41
+ console.warn(
42
+ 'No ANSI color codes detected. Colors might be disabled in this environment.'
43
+ )
44
+ }
45
+ })
@@ -0,0 +1,35 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import { filterLog } from '../../src/core/filter'
4
+ import { Options } from '../../src/types'
5
+
6
+ test('filterLog', () => {
7
+ const options: Options = {
8
+ config: {
9
+ logFilter: {
10
+ level: 'ERROR',
11
+ method: 'POST',
12
+ status: 500
13
+ }
14
+ }
15
+ }
16
+
17
+ expect(filterLog('ERROR', 500, 'POST', options)).toBe(true)
18
+ expect(filterLog('INFO', 200, 'GET', options)).toBe(false)
19
+ expect(filterLog('WARNING', 400, 'PUT', options)).toBe(false)
20
+
21
+ // Test with array filters
22
+ const arrayOptions: Options = {
23
+ config: {
24
+ logFilter: {
25
+ level: ['ERROR', 'WARNING'],
26
+ method: ['POST', 'PUT'],
27
+ status: [500, 400]
28
+ }
29
+ }
30
+ }
31
+
32
+ expect(filterLog('ERROR', 500, 'POST', arrayOptions)).toBe(true)
33
+ expect(filterLog('WARNING', 400, 'PUT', arrayOptions)).toBe(true)
34
+ expect(filterLog('INFO', 200, 'GET', arrayOptions)).toBe(false)
35
+ })
@@ -0,0 +1,19 @@
1
+ import { mock } from 'bun:test'
2
+
3
+ import { RequestInfo } from '../src/types'
4
+
5
+ export function createMockRequest(
6
+ method: string = 'GET',
7
+ url: string = 'http://localhost:3000/'
8
+ ): RequestInfo {
9
+ return {
10
+ headers: {
11
+ get: (key: string) => (key === 'x-forwarded-for' ? '127.0.0.1' : null)
12
+ },
13
+ method,
14
+ url
15
+ }
16
+ }
17
+
18
+ export const mockConsoleLog = mock(() => {})
19
+ export const mockConsoleError = mock(() => {})
@@ -0,0 +1,8 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import logixlysia from '../src/index'
4
+
5
+ test('logixlysia', () => {
6
+ const elysia = logixlysia()
7
+ expect(elysia).toBeDefined()
8
+ })
@@ -0,0 +1,29 @@
1
+ import { expect, mock, test } from 'bun:test'
2
+
3
+ import { logToTransports } from '../../src/transports'
4
+ import { LogData, LogLevel, Options, StoreData } from '../../src/types'
5
+ import { createMockRequest } from '../helpers'
6
+
7
+ test('logToTransports', async () => {
8
+ const level: LogLevel = 'INFO'
9
+ const request = createMockRequest()
10
+ const data: LogData = { status: 200, message: 'Test message' }
11
+ const store: StoreData = { beforeTime: BigInt(0) }
12
+
13
+ const mockTransport = {
14
+ log: mock(() => Promise.resolve())
15
+ }
16
+
17
+ const options: Options = {
18
+ config: {
19
+ transports: [mockTransport]
20
+ }
21
+ }
22
+
23
+ await logToTransports(level, request, data, store, options)
24
+ expect(mockTransport.log).toHaveBeenCalled()
25
+
26
+ // Test with no transports
27
+ await logToTransports(level, request, data, store, {})
28
+ expect(mockTransport.log).toHaveBeenCalledTimes(1) // Should not be called again
29
+ })
package/tsconfig.json CHANGED
@@ -6,9 +6,6 @@
6
6
  "allowImportingTsExtensions": true,
7
7
  "module": "ESNext",
8
8
  "moduleResolution": "Bundler",
9
- "paths": {
10
- "~/*": ["./src/*"]
11
- },
12
9
  "resolveJsonModule": true,
13
10
  "types": ["bun-types"],
14
11
  "downlevelIteration": true,
@@ -1,22 +0,0 @@
1
- import { buildLogMessage } from '~/logger/buildLogMessage'
2
- import { HttpError, Options, RequestInfo, StoreData } from '~/types'
3
-
4
- /**
5
- * Handles an HTTP error and logs it.
6
- *
7
- * @param {RequestInfo} request The request information.
8
- * @param {HttpError} error The HTTP error.
9
- * @param {StoreData} store The store data.
10
- * @param {Options} options The logger options.
11
- */
12
- export function handleHttpError(
13
- request: RequestInfo,
14
- error: HttpError,
15
- store: StoreData,
16
- options?: Options
17
- ): void {
18
- const statusCode = error.status || 500
19
- console.error(
20
- buildLogMessage('ERROR', request, { status: statusCode }, store, options)
21
- )
22
- }
@@ -1,2 +0,0 @@
1
- export { createLogger } from '~/logger/createLogger'
2
- export { handleHttpError } from '~/logger/handleHttpError'