logixlysia 3.5.0 โ†’ 3.6.1

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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.6.1](https://github.com/PunGrumpy/logixlysia/compare/v3.6.0...v3.6.1) (2024-08-21)
4
+
5
+
6
+ ### Performance Improvements
7
+
8
+ * **logger:** improve performance and fix TypeScript errors ([d3ed751](https://github.com/PunGrumpy/logixlysia/commit/d3ed751041443b9bd2ce53350994e1443df40971))
9
+
10
+ ## [3.6.0](https://github.com/PunGrumpy/logixlysia/compare/v3.5.0...v3.6.0) (2024-07-24)
11
+
12
+
13
+ ### Features
14
+
15
+ * **custom-log:** add unix epoch timestamp option on `customLogFormat` configuration ([58d3e5b](https://github.com/PunGrumpy/logixlysia/commit/58d3e5b89bfef86aed1f5daa70d9a39982750073)), closes [#56](https://github.com/PunGrumpy/logixlysia/issues/56)
16
+ * **transports:** add custom tranporter support for flexible logging ([69e1b89](https://github.com/PunGrumpy/logixlysia/commit/69e1b8991d323d8463fb81cf5bf1b441f678318b)), closes [#51](https://github.com/PunGrumpy/logixlysia/issues/51)
17
+
3
18
  ## [3.5.0](https://github.com/PunGrumpy/logixlysia/compare/v3.4.0...v3.5.0) (2024-07-11)
4
19
 
5
20
 
package/README.md CHANGED
@@ -25,7 +25,7 @@ const app = new Elysia({
25
25
  ip: true,
26
26
  logFilePath: './logs/example.log',
27
27
  customLogFormat:
28
- '๐ŸฆŠ {now} {level} {duration} {method} {pathname} {status} {message} {ip}',
28
+ '๐ŸฆŠ {now} {level} {duration} {method} {pathname} {status} {message} {ip} {epoch}',
29
29
  logFilter: {
30
30
  level: ['ERROR', 'WARNING'],
31
31
  status: [500, 404],
@@ -38,6 +38,9 @@ const app = new Elysia({
38
38
  app.listen(3000)
39
39
  ```
40
40
 
41
+ > [!NOTE]
42
+ > You can discover more about example in the [example](example) directory.
43
+
41
44
  ## `๐Ÿ“š` Documentation
42
45
 
43
46
  ### Options
@@ -50,6 +53,20 @@ app.listen(3000)
50
53
  | `logFilter` | `object` | Filter the logs based on the level, method, and status | `null` |
51
54
  | `logFilePath` | `string` | Path to the log file | `./logs/elysia.log` |
52
55
 
56
+ ### Custom Log Message
57
+
58
+ | Placeholder | Description |
59
+ | ------------ | --------------------------------------------------------------------------- |
60
+ | `{now}` | Current date and time in `YYYY-MM-DD HH:mm:ss` format |
61
+ | `{level}` | Log level (`INFO`, `WARNING`, `ERROR`) |
62
+ | `{duration}` | Request duration in milliseconds |
63
+ | `{method}` | Request method (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`) |
64
+ | `{pathname}` | Request pathname |
65
+ | `{status}` | Response status code |
66
+ | `{message}` | Custom message |
67
+ | `{ip}` | Incoming IP address |
68
+ | `{epoch}` | Current date and time in Unix epoch format (seconds since January 1, 1970 |
69
+
53
70
  ## `๐Ÿ“„` License
54
71
 
55
72
  Licensed under the [MIT License](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "logixlysia",
3
- "version": "3.5.0",
3
+ "version": "3.6.1",
4
4
  "description": "๐ŸฆŠ Logixlysia is a logger for Elysia",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",
@@ -15,10 +15,10 @@
15
15
  },
16
16
  "license": "MIT",
17
17
  "scripts": {
18
- "lint": "eslint ..",
19
- "lint:fix": "eslint .. --fix",
18
+ "lint": "eslint .",
19
+ "lint:fix": "eslint . --fix",
20
20
  "test": "bun test --timeout 5000 --coverage",
21
- "test:ci": "bun test --timeout 5000 --coverage",
21
+ "test:ci": "bun test --timeout 5000 --coverage --coverage-reporter=lcov",
22
22
  "publish": "npm publish",
23
23
  "dev": "bun run --watch example/basic.ts",
24
24
  "prepare": "husky",
@@ -71,25 +71,26 @@
71
71
  ],
72
72
  "dependencies": {
73
73
  "chalk": "^5.3.0",
74
- "elysia": "^1.0.24"
74
+ "elysia": "^1.1.4"
75
75
  },
76
76
  "devDependencies": {
77
- "@elysiajs/eden": "^1.0.14",
78
- "@eslint/js": "^9.5.0",
77
+ "@elysiajs/eden": "^1.1.1",
78
+ "@eslint/js": "^9.8.0",
79
79
  "@trunkio/launcher": "^1.3.1",
80
- "@typescript-eslint/eslint-plugin": "^7.13.1",
81
- "@typescript-eslint/parser": "^7.13.1",
82
- "bun-types": "^1.1.13",
80
+ "@typescript-eslint/eslint-plugin": "^8.0.1",
81
+ "@typescript-eslint/parser": "^8.0.1",
82
+ "bun-types": "^1.1.20",
83
83
  "commitizen": "^4.3.0",
84
84
  "cz-conventional-changelog": "^3.3.0",
85
85
  "eslint": "9.x",
86
- "eslint-plugin-simple-import-sort": "^12.1.0",
87
- "husky": "^9.0.11",
86
+ "eslint-plugin-simple-import-sort": "^12.1.1",
87
+ "globals": "^15.9.0",
88
+ "husky": "^9.1.1",
88
89
  "lint-staged": "^15.2.7",
89
- "prettier": "^3.3.2",
90
- "typescript-eslint": "^7.13.1"
90
+ "prettier": "^3.3.3",
91
+ "typescript-eslint": "^8.0.1"
91
92
  },
92
93
  "peerDependencies": {
93
- "typescript": "^5.0.0"
94
+ "typescript": "^5.2.2"
94
95
  }
95
96
  }
package/src/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Server } from 'bun'
2
2
  import Elysia from 'elysia'
3
3
 
4
- import { createLogger, handleHttpError } from './logger'
5
- import { HttpError, Options } from './types'
6
- import startServer from './utils/start'
4
+ import { createLogger, handleHttpError } from '~/logger'
5
+ import { HttpError, Options } from '~/types'
6
+ import startServer from '~/utils/start'
7
7
 
8
8
  /**
9
9
  * Creates a logger plugin for ElysiaJS.
@@ -41,3 +41,6 @@ export default function logixlysia(options?: Options): Elysia {
41
41
  )
42
42
  })
43
43
  }
44
+
45
+ export { createLogger } from '~/logger'
46
+ export { handleHttpError } from '~/logger'
@@ -1,12 +1,22 @@
1
1
  import chalk from 'chalk'
2
2
 
3
- import { LogData, LogLevel, Options, RequestInfo, StoreData } from '~/types'
3
+ import {
4
+ LogComponents,
5
+ LogData,
6
+ LogLevel,
7
+ Options,
8
+ RequestInfo,
9
+ StoreData
10
+ } from '~/types'
4
11
  import durationString from '~/utils/duration'
5
12
  import logString from '~/utils/log'
6
13
  import methodString from '~/utils/method'
7
14
  import pathString from '~/utils/path'
8
15
  import statusString from '~/utils/status'
9
16
 
17
+ const defaultLogFormat =
18
+ '๐ŸฆŠ {now} {level} {duration} {method} {pathname} {status} {message} {ip}'
19
+
10
20
  /**
11
21
  * Builds a log message.
12
22
  *
@@ -26,31 +36,30 @@ export function buildLogMessage(
26
36
  options?: Options,
27
37
  useColors: boolean = true
28
38
  ): string {
29
- const nowStr = useColors
30
- ? chalk.bgYellow(chalk.black(new Date().toLocaleString()))
31
- : new Date().toLocaleString()
32
- const levelStr = logString(level, useColors)
33
- const durationStr = durationString(store.beforeTime, useColors)
34
- const methodStr = methodString(request.method, useColors)
35
- const pathnameStr = pathString(request)
36
- const statusStr = statusString(data.status || 200, useColors)
37
- const messageStr = data.message || ''
38
- const ipStr =
39
- options?.config?.ip && request.headers.get('x-forwarded-for')
40
- ? `IP: ${request.headers.get('x-forwarded-for')}`
41
- : ''
39
+ const now = new Date()
40
+ const components: LogComponents = {
41
+ now: useColors
42
+ ? chalk.bgYellow(chalk.black(now.toLocaleString()))
43
+ : now.toLocaleString(),
44
+ epoch: Math.floor(now.getTime() / 1000).toString(),
45
+ level: logString(level, useColors),
46
+ duration: durationString(store.beforeTime, useColors),
47
+ method: methodString(request.method, useColors),
48
+ pathname: pathString(request),
49
+ status: statusString(data.status || 200, useColors),
50
+ message: data.message || '',
51
+ ip:
52
+ options?.config?.ip && request.headers.get('x-forwarded-for')
53
+ ? `IP: ${request.headers.get('x-forwarded-for')}`
54
+ : ''
55
+ }
42
56
 
43
- const logFormat =
44
- options?.config?.customLogFormat ||
45
- '๐ŸฆŠ {now} {level} {duration} {method} {pathname} {status} {message} {ip}'
57
+ const logFormat = options?.config?.customLogFormat || defaultLogFormat
46
58
 
47
- return logFormat
48
- .replace('{now}', nowStr)
49
- .replace('{level}', levelStr)
50
- .replace('{duration}', durationStr)
51
- .replace('{method}', methodStr)
52
- .replace('{pathname}', pathnameStr || '')
53
- .replace('{status}', statusStr)
54
- .replace('{message}', messageStr)
55
- .replace('{ip}', ipStr || '')
59
+ return logFormat.replace(/{(\w+)}/g, (_, key: string) => {
60
+ if (key in components) {
61
+ return components[key as keyof LogComponents] || ''
62
+ }
63
+ return ''
64
+ })
56
65
  }
@@ -1,3 +1,7 @@
1
+ import { buildLogMessage } from '~/logger/buildLogMessage'
2
+ import { filterLog } from '~/logger/filter'
3
+ import { logToFile } from '~/logger/logToFile'
4
+ import { logToTransports } from '~/transports'
1
5
  import {
2
6
  LogData,
3
7
  Logger,
@@ -7,10 +11,6 @@ import {
7
11
  StoreData
8
12
  } from '~/types'
9
13
 
10
- import { buildLogMessage } from './buildLogMessage'
11
- import { filterLog } from './filter'
12
- import { logToFile } from './logToFile'
13
-
14
14
  /**
15
15
  * Logs a message to the console and optionally to a file.
16
16
  *
@@ -32,16 +32,26 @@ async function log(
32
32
  const logMessage = buildLogMessage(level, request, data, store, options, true)
33
33
  console.log(logMessage)
34
34
 
35
+ const promises = []
36
+
35
37
  if (options?.config?.logFilePath) {
36
- await logToFile(
37
- options.config.logFilePath,
38
- level,
39
- request,
40
- data,
41
- store,
42
- options
38
+ promises.push(
39
+ logToFile(
40
+ options.config.logFilePath,
41
+ level,
42
+ request,
43
+ data,
44
+ store,
45
+ options
46
+ )
43
47
  )
44
48
  }
49
+
50
+ if (options?.config?.transports?.length) {
51
+ promises.push(logToTransports(level, request, data, store, options))
52
+ }
53
+
54
+ await Promise.all(promises)
45
55
  }
46
56
 
47
57
  /**
@@ -52,7 +62,7 @@ async function log(
52
62
  */
53
63
  export function createLogger(options?: Options): Logger {
54
64
  return {
55
- log: async (level, request, data, store) =>
65
+ log: (level, request, data, store) =>
56
66
  log(level, request, data, store, options),
57
67
  customLogFormat: options?.config?.customLogFormat
58
68
  }
@@ -1,5 +1,10 @@
1
1
  import { LogLevel, Options } from '~/types'
2
2
 
3
+ const checkFilter = (filterValue: any, value: any) =>
4
+ Array.isArray(filterValue)
5
+ ? filterValue.includes(value)
6
+ : filterValue === value
7
+
3
8
  /**
4
9
  * Filters log messages.
5
10
  *
@@ -18,11 +23,6 @@ export function filterLog(
18
23
  const filter = options?.config?.logFilter
19
24
  if (!filter) return true
20
25
 
21
- const checkFilter = (filterValue: any, value: any) =>
22
- Array.isArray(filterValue)
23
- ? filterValue.includes(value)
24
- : filterValue === value
25
-
26
26
  return (
27
27
  (!filter.level || checkFilter(filter.level, logLevel)) &&
28
28
  (!filter.status || checkFilter(filter.status, status)) &&
@@ -1,7 +1,6 @@
1
+ import { buildLogMessage } from '~/logger/buildLogMessage'
1
2
  import { HttpError, Options, RequestInfo, StoreData } from '~/types'
2
3
 
3
- import { buildLogMessage } from './buildLogMessage'
4
-
5
4
  /**
6
5
  * Handles an HTTP error and logs it.
7
6
  *
@@ -10,19 +9,14 @@ import { buildLogMessage } from './buildLogMessage'
10
9
  * @param {StoreData} store The store data.
11
10
  * @param {Options} options The logger options.
12
11
  */
13
- export async function handleHttpError(
12
+ export function handleHttpError(
14
13
  request: RequestInfo,
15
14
  error: HttpError,
16
15
  store: StoreData,
17
16
  options?: Options
18
- ): Promise<void> {
17
+ ): void {
19
18
  const statusCode = error.status || 500
20
- const logMessage = buildLogMessage(
21
- 'ERROR',
22
- request,
23
- { status: statusCode },
24
- store,
25
- options
19
+ console.error(
20
+ buildLogMessage('ERROR', request, { status: statusCode }, store, options)
26
21
  )
27
- console.error(logMessage)
28
22
  }
@@ -1,2 +1,2 @@
1
- export { createLogger } from './createLogger'
2
- export { handleHttpError } from './handleHttpError'
1
+ export { createLogger } from '~/logger/createLogger'
2
+ export { handleHttpError } from '~/logger/handleHttpError'
@@ -1,9 +1,10 @@
1
1
  import { promises as fs } from 'fs'
2
2
  import { dirname } from 'path'
3
3
 
4
+ import { buildLogMessage } from '~/logger/buildLogMessage'
4
5
  import { LogData, LogLevel, Options, RequestInfo, StoreData } from '~/types'
5
6
 
6
- import { buildLogMessage } from './buildLogMessage'
7
+ const dirCache = new Set<string>()
7
8
 
8
9
  /**
9
10
  * Ensures that the directory exists. If not, it creates the directory.
@@ -12,7 +13,10 @@ import { buildLogMessage } from './buildLogMessage'
12
13
  */
13
14
  async function ensureDirectoryExists(filePath: string): Promise<void> {
14
15
  const dir = dirname(filePath)
15
- await fs.mkdir(dir, { recursive: true })
16
+ if (!dirCache.has(dir)) {
17
+ await fs.mkdir(dir, { recursive: true })
18
+ dirCache.add(dir)
19
+ }
16
20
  }
17
21
 
18
22
  /**
@@ -36,5 +40,5 @@ export async function logToFile(
36
40
  await ensureDirectoryExists(filePath)
37
41
  const logMessage =
38
42
  buildLogMessage(level, request, data, store, options, false) + '\n'
39
- await fs.appendFile(filePath, logMessage)
43
+ await fs.appendFile(filePath, logMessage, { flag: 'a' })
40
44
  }
@@ -0,0 +1,22 @@
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
+ }
package/src/types.ts CHANGED
@@ -31,6 +31,18 @@ interface Logger {
31
31
  customLogFormat?: string
32
32
  }
33
33
 
34
+ interface LogComponents {
35
+ now: string
36
+ epoch: string
37
+ level: string
38
+ duration: string
39
+ method: string
40
+ pathname: string | undefined
41
+ status: string
42
+ message: string
43
+ ip: string
44
+ }
45
+
34
46
  interface StoreData {
35
47
  beforeTime: bigint
36
48
  }
@@ -44,9 +56,24 @@ class HttpError extends Error {
44
56
  }
45
57
  }
46
58
 
59
+ interface TransportFunction {
60
+ (
61
+ level: LogLevel,
62
+ message: string,
63
+ meta: {
64
+ request: RequestInfo
65
+ data: LogData
66
+ store: StoreData
67
+ }
68
+ ): Promise<void> | void
69
+ }
70
+
71
+ interface Transport {
72
+ log: TransportFunction
73
+ }
74
+
47
75
  interface Options {
48
76
  config?: {
49
- ip?: boolean
50
77
  customLogFormat?: string
51
78
  logFilePath?: string
52
79
  logFilter?: {
@@ -54,18 +81,23 @@ interface Options {
54
81
  method?: string | string[]
55
82
  status?: number | number[]
56
83
  } | null
84
+ ip?: boolean
57
85
  showBanner?: boolean
86
+ transports?: Transport[]
58
87
  }
59
88
  }
60
89
 
61
90
  export {
62
91
  ColorMap,
63
92
  HttpError,
93
+ LogComponents,
64
94
  LogData,
65
95
  Logger,
66
96
  LogLevel,
67
97
  Options,
68
98
  RequestInfo,
69
99
  Server,
70
- StoreData
100
+ StoreData,
101
+ Transport,
102
+ TransportFunction
71
103
  }
@@ -1,5 +1,12 @@
1
1
  import chalk from 'chalk'
2
2
 
3
+ const timeUnits = [
4
+ { unit: 's', threshold: 1e9, decimalPlaces: 2 },
5
+ { unit: 'ms', threshold: 1e6, decimalPlaces: 0 },
6
+ { unit: 'ยตs', threshold: 1e3, decimalPlaces: 0 },
7
+ { unit: 'ns', threshold: 1, decimalPlaces: 0 }
8
+ ]
9
+
3
10
  /**
4
11
  * Converts a time difference into a formatted string with the most appropriate time unit.
5
12
  *
@@ -8,38 +15,19 @@ import chalk from 'chalk'
8
15
  * @returns {string} A formatted duration string including the time unit.
9
16
  */
10
17
  function durationString(beforeTime: bigint, useColors: boolean): string {
11
- const currentTime = process.hrtime.bigint()
12
- const nanoseconds = Number(currentTime - beforeTime)
13
-
14
- const timeUnits = [
15
- { unit: 's', threshold: 1e9, decimalPlaces: 2 },
16
- { unit: 'ms', threshold: 1e6, decimalPlaces: 0 },
17
- { unit: 'ยตs', threshold: 1e3, decimalPlaces: 0 }
18
- ]
18
+ const nanoseconds = Number(process.hrtime.bigint() - beforeTime)
19
19
 
20
20
  for (const { unit, threshold, decimalPlaces } of timeUnits) {
21
21
  if (nanoseconds >= threshold) {
22
22
  const value = (nanoseconds / threshold).toFixed(decimalPlaces)
23
- return formatTime(value, unit, useColors)
23
+ const timeStr = `${value}${unit}`.padStart(8).padEnd(16)
24
+ return useColors ? chalk.gray(timeStr) : timeStr
24
25
  }
25
26
  }
26
27
 
27
- return formatTime(nanoseconds.toString(), 'ns', useColors)
28
- }
29
-
30
- /**
31
- * Formats the time value with the given unit and applies chalk styling.
32
- *
33
- * @param {string} value - The time value.
34
- * @param {string} unit - The time unit.
35
- * @param {boolean} useColors - Whether to apply colors to the output.
36
- * @returns {string} Styled time string.
37
- */
38
- function formatTime(value: string, unit: string, useColors: boolean): string {
39
- const timeStr = `${value}${unit}`
40
28
  return useColors
41
- ? chalk.gray(timeStr).padStart(8).padEnd(16)
42
- : timeStr.padStart(8).padEnd(16)
29
+ ? chalk.gray('0ns'.padStart(8).padEnd(16))
30
+ : '0ns'.padStart(8).padEnd(16)
43
31
  }
44
32
 
45
33
  export default durationString
package/src/utils/log.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { LogLevel } from '~/types'
2
-
3
- import { LogLevelColorMap } from './colorMapping'
2
+ import { LogLevelColorMap } from '~/utils/colorMapping'
4
3
 
5
4
  /**
6
5
  * Converts the log level to a string.
@@ -9,7 +8,7 @@ import { LogLevelColorMap } from './colorMapping'
9
8
  * @param {boolean} useColors - Whether to apply colors to the output.
10
9
  * @returns {string} The log level as a string.
11
10
  */
12
- function logString(level: LogLevel, useColors: boolean): string {
11
+ const logString = (level: LogLevel, useColors: boolean): string => {
13
12
  const levelStr = level.toUpperCase()
14
13
  return useColors
15
14
  ? LogLevelColorMap[levelStr]?.(levelStr.padEnd(7)) || levelStr
@@ -1,4 +1,4 @@
1
- import { HttpMethodColorMap } from './colorMapping'
1
+ import { HttpMethodColorMap } from '~/utils/colorMapping'
2
2
 
3
3
  /**
4
4
  * Converts an HTTP request method to a colored string representation.
package/src/utils/path.ts CHANGED
@@ -6,7 +6,7 @@ import { RequestInfo } from '~/types'
6
6
  * @param {RequestInfo} requestInfo The request info.
7
7
  * @returns {string | undefined} The path string.
8
8
  */
9
- function pathString(requestInfo: RequestInfo): string | undefined {
9
+ const pathString = (requestInfo: RequestInfo): string | undefined => {
10
10
  try {
11
11
  return new URL(requestInfo.url).pathname
12
12
  } catch {
@@ -7,7 +7,7 @@ import { Options, Server } from '~/types'
7
7
  * @param {number} width The box width.
8
8
  * @returns {string} The box text.
9
9
  */
10
- function createBoxText(text: string, width: number): string {
10
+ const createBoxText = (text: string, width: number): string => {
11
11
  const paddingLength = Math.max(0, (width - text.length) / 2)
12
12
  const padding = ' '.repeat(paddingLength)
13
13
  return `${padding}${text}${padding}`.padEnd(width)
@@ -29,14 +29,15 @@ function startServer(config: Server, options?: Options): void {
29
29
  const message = `๐ŸฆŠ Elysia is running at ${protocol}://${hostname}:${port}`
30
30
  const boxWidth = Math.max(title.length, message.length) + 4
31
31
  const border = 'โ”€'.repeat(boxWidth)
32
+ const emptyLine = createBoxText('', boxWidth)
32
33
 
33
34
  console.log(`
34
35
  โ”Œ${border}โ”
35
- โ”‚${createBoxText('', boxWidth)}โ”‚
36
+ โ”‚${emptyLine}โ”‚
36
37
  โ”‚${createBoxText(title, boxWidth)}โ”‚
37
- โ”‚${createBoxText('', boxWidth)}โ”‚
38
+ โ”‚${emptyLine}โ”‚
38
39
  โ”‚${createBoxText(message, boxWidth)}โ”‚
39
- โ”‚${createBoxText('', boxWidth)}โ”‚
40
+ โ”‚${emptyLine}โ”‚
40
41
  โ””${border}โ”˜
41
42
  `)
42
43
  } else {
@@ -7,7 +7,7 @@ import chalk from 'chalk'
7
7
  * @param {boolean} useColors - Whether to apply colors to the output.
8
8
  * @returns {string} The status code as a string.
9
9
  */
10
- function statusString(status: number, useColors: boolean): string {
10
+ const statusString = (status: number, useColors: boolean): string => {
11
11
  const color =
12
12
  status >= 500
13
13
  ? 'red'