logixlysia 3.2.0 → 3.3.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/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule ".trunk/configs"]
2
+ path = .trunk/configs
3
+ url = git@github.com:PunGrumpy/.trunk.git
@@ -0,0 +1,26 @@
1
+ ---
2
+ version: 0.1
3
+ cli:
4
+ version: 1.22.1
5
+ plugins:
6
+ sources:
7
+ - id: trunk
8
+ ref: v1.5.0
9
+ uri: https://github.com/trunk-io/plugins
10
+ runtimes:
11
+ enabled:
12
+ - node@22.2.0
13
+ lint:
14
+ enabled:
15
+ - oxipng@9.1.1
16
+ - shfmt@3.6.0
17
+ - eslint@9.3.0
18
+ - actionlint@1.7.0
19
+ - git-diff-check
20
+ - markdownlint@0.40.0
21
+ - prettier@3.2.5
22
+ - trufflehog@3.76.3
23
+ - yamllint@1.35.1
24
+ actions:
25
+ enabled:
26
+ - npm-check
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.1](https://github.com/PunGrumpy/logixlysia/compare/v3.3.0...v3.3.1) (2024-06-01)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **deps:** move `ESLint` and related packages to dev dependencies ([98de50c](https://github.com/PunGrumpy/logixlysia/commit/98de50c6c3bffc5939963ebd04563204c7c70793)), closes [#46](https://github.com/PunGrumpy/logixlysia/issues/46)
9
+
10
+ ## [3.3.0](https://github.com/PunGrumpy/logixlysia/compare/v3.2.0...v3.3.0) (2024-05-22)
11
+
12
+
13
+ ### Features
14
+
15
+ * **color:** new color for methods (matching Postman color) ([0528886](https://github.com/PunGrumpy/logixlysia/commit/0528886ef9fdaa466e4b78b46fb09d31731b0520))
16
+
3
17
  ## [3.2.0](https://github.com/PunGrumpy/logixlysia/compare/v3.1.0...v3.2.0) (2024-05-07)
4
18
 
5
19
 
package/README.md CHANGED
@@ -14,15 +14,22 @@ bun add logixlysia
14
14
 
15
15
  ```ts
16
16
  import { Elysia } from 'elysia'
17
- import { logger } from 'logixlysia'
17
+ import { logixlysia } from 'logixlysia'
18
18
 
19
19
  const app = new Elysia({
20
20
  name: 'Logixlysia Example'
21
21
  }).use(
22
- logger({
23
- ip: false,
24
- customLogMessage:
25
- '🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}'
22
+ logixlysia({
23
+ config: {
24
+ ip: true,
25
+ customLogFormat:
26
+ '🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}',
27
+ logFilter: {
28
+ level: ['ERROR', 'WARNING'],
29
+ status: [500, 404],
30
+ method: 'GET'
31
+ }
32
+ }
26
33
  })
27
34
  )
28
35
 
@@ -1,27 +1,8 @@
1
- /* eslint-disable @typescript-eslint/no-var-requires */
2
- const { existsSync } = require('fs')
3
- const { join } = require('path')
4
-
5
- const isBun = existsSync(join(process.cwd(), 'bun.lockb'))
6
- const isYarn = existsSync(join(process.cwd(), 'yarn.lock'))
7
- const isPnpm = existsSync(join(process.cwd(), 'pnpm-lock.yaml'))
8
-
9
- const packageManager = isBun ? 'bun' : isYarn ? 'yarn' : isPnpm ? 'pnpm' : 'npm'
10
-
11
- const options = {
12
- // TypeScript & JavaScript files
13
- '**/*.(ts|tsx)': () => `${packageManager} tsc --noEmit`,
14
- '**/*.(ts|tsx|js)': filenames => [
15
- `${packageManager} eslint --fix ${filenames.join(' ')}`,
16
- `${packageManager} prettier --write ${filenames.join(' ')}`
1
+ module.exports = {
2
+ '**/*.(ts)': () => `bun tsc --noEmit`,
3
+ '**/*.(ts|js|cjs)': filenames => [
4
+ `bun eslint --fix ${filenames.join(' ')}`,
5
+ `bun prettier --write ${filenames.join(' ')}`
17
6
  ],
18
- '**/*.(css|less|scss)': filenames =>
19
- `${packageManager} test --timeout 5000 --coverage --update-snapshots ${filenames.join(
20
- ' '
21
- )}`,
22
- // Markdown & JSON files
23
- '**/*.(md|json)': filenames =>
24
- `${packageManager} prettier --write ${filenames.join(' ')}`
7
+ '**/*.(md|json)': filenames => `bun prettier --write ${filenames.join(' ')}`
25
8
  }
26
-
27
- module.exports = options
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "logixlysia",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "🦊 Logixlysia is a logger for Elysia",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",
@@ -15,15 +15,17 @@
15
15
  },
16
16
  "license": "MIT",
17
17
  "scripts": {
18
- "lint": "eslint **/*.ts --no-warn-ignored",
19
- "lint:fix": "eslint **/*.ts --fix --no-warn-ignored",
18
+ "lint": "eslint ..",
19
+ "lint:fix": "eslint .. --fix",
20
20
  "test": "bun test --timeout 5000 --coverage",
21
21
  "test:ci": "bun test --timeout 5000 --coverage",
22
22
  "publish": "npm publish",
23
23
  "dev": "bun run --watch example/basic.ts",
24
24
  "prepare": "husky",
25
25
  "lint:staged": "lint-staged",
26
- "prettier": "prettier --write ."
26
+ "prettier": "prettier --write --config .trunk/configs/.prettierrc.yaml .",
27
+ "trunk:check": "trunk check",
28
+ "trunk:fmt": "trunk fmt"
27
29
  },
28
30
  "os": [
29
31
  "darwin",
@@ -68,20 +70,24 @@
68
70
  "middleware"
69
71
  ],
70
72
  "dependencies": {
71
- "@typescript-eslint/eslint-plugin": "^7.0.0",
72
- "@typescript-eslint/parser": "^7.0.2",
73
73
  "chalk": "^5.3.0",
74
- "elysia": "^1.0.4",
75
- "eslint": "^9.0.0"
74
+ "elysia": "^1.0.21"
76
75
  },
77
76
  "devDependencies": {
78
- "@elysiajs/eden": "^1.0.4",
79
- "bun-types": "^1.0.22",
77
+ "@elysiajs/eden": "^1.0.13",
78
+ "@eslint/js": "^9.3.0",
79
+ "@trunkio/launcher": "^1.3.1",
80
+ "@typescript-eslint/eslint-plugin": "^7.10.0",
81
+ "@typescript-eslint/parser": "^7.10.0",
82
+ "bun-types": "^1.1.9",
80
83
  "commitizen": "^4.3.0",
81
84
  "cz-conventional-changelog": "^3.3.0",
82
- "husky": "^9.0.7",
83
- "lint-staged": "^15.2.0",
84
- "prettier": "^3.1.1"
85
+ "eslint": "9.x",
86
+ "eslint-plugin-simple-import-sort": "^12.1.0",
87
+ "husky": "^9.0.11",
88
+ "lint-staged": "^15.2.4",
89
+ "prettier": "^3.2.5",
90
+ "typescript-eslint": "^7.10.0"
85
91
  },
86
92
  "peerDependencies": {
87
93
  "typescript": "^5.0.0"
package/src/index.ts CHANGED
@@ -1,12 +1,13 @@
1
- import Elysia from 'elysia'
2
- import startServer from './utils/start'
3
1
  import { Server } from 'bun'
2
+ import Elysia from 'elysia'
3
+
4
+ import { createLogger } from './logger/createLogger'
5
+ import { handleHttpError } from './logger/handleHttpError'
4
6
  import { HttpError, Options } from './types'
5
- import { createLogger } from './logger'
6
- import { handleHttpError } from './logger/handle'
7
+ import startServer from './utils/start'
7
8
 
8
9
  /**
9
- * Creates a logger.
10
+ * Creates a logger plugin for ElysiaJS.
10
11
  *
11
12
  * @export
12
13
  * @module logger
@@ -18,7 +19,9 @@ import { handleHttpError } from './logger/handle'
18
19
  * @author PunGrumpy
19
20
  * @license MIT
20
21
  *
21
- * @returns {Elysia} The logger.
22
+ * @param {Options} [options] Configuration options for the logger.
23
+ *
24
+ * @returns {Elysia} The logger plugin for ElysiaJS.
22
25
  */
23
26
  export default function logixlysia(options?: Options): Elysia {
24
27
  const log = createLogger(options)
@@ -1,54 +1,23 @@
1
- import {
2
- LogData,
3
- Logger,
4
- LogLevel,
5
- Options,
6
- RequestInfo,
7
- StoreData
8
- } from '~/types'
9
- import { filterLog } from './filter'
10
1
  import chalk from 'chalk'
11
- import logString from '~/utils/log'
2
+
3
+ import { LogData, LogLevel, Options, RequestInfo, StoreData } from '~/types'
12
4
  import durationString from '~/utils/duration'
5
+ import logString from '~/utils/log'
13
6
  import methodString from '~/utils/method'
14
7
  import pathString from '~/utils/path'
15
8
  import statusString from '~/utils/status'
16
9
 
17
- /**
18
- * Logs a message.
19
- *
20
- * @param {LogLevel} level The log level.
21
- * @param {RequestInfo} request The request.
22
- * @param {LogData} data The log data.
23
- * @param {StoreData} store The store data.
24
- * @param {Options} options The options.
25
- */
26
- function log(
27
- level: LogLevel,
28
- request: RequestInfo,
29
- data: LogData,
30
- store: StoreData,
31
- options?: Options
32
- ): void {
33
- if (!filterLog(level, data.status || 200, request.method, options)) {
34
- return
35
- }
36
-
37
- const logMessage = buildLogMessage(level, request, data, store, options)
38
- console.log(logMessage)
39
- }
40
-
41
10
  /**
42
11
  * Builds a log message.
43
12
  *
44
13
  * @param {LogLevel} level The log level.
45
- * @param {RequestInfo} request The request.
14
+ * @param {RequestInfo} request The request information.
46
15
  * @param {LogData} data The log data.
47
16
  * @param {StoreData} store The store data.
48
- * @param {Options} options The options.
49
- * @returns {string} The log message.
17
+ * @param {Options} options The logger options.
18
+ * @returns {string} The formatted log message.
50
19
  */
51
- function buildLogMessage(
20
+ export function buildLogMessage(
52
21
  level: LogLevel,
53
22
  request: RequestInfo,
54
23
  data: LogData,
@@ -82,19 +51,3 @@ function buildLogMessage(
82
51
 
83
52
  return logMessage
84
53
  }
85
-
86
- /**
87
- * Creates a logger.
88
- *
89
- * @param {Options} options The options.
90
- * @returns {Logger} The logger.
91
- */
92
- function createLogger(options?: Options): Logger {
93
- return {
94
- log: (level, request, data, store) =>
95
- log(level, request, data, store, options),
96
- customLogFormat: options?.config?.customLogFormat
97
- }
98
- }
99
-
100
- export { log, buildLogMessage, createLogger }
@@ -0,0 +1,49 @@
1
+ import {
2
+ LogData,
3
+ Logger,
4
+ LogLevel,
5
+ Options,
6
+ RequestInfo,
7
+ StoreData
8
+ } from '~/types'
9
+
10
+ import { buildLogMessage } from './buildLogMessage'
11
+ import { filterLog } from './filter'
12
+
13
+ /**
14
+ * Logs a message to the console.
15
+ *
16
+ * @param {LogLevel} level The log level.
17
+ * @param {RequestInfo} request The request information.
18
+ * @param {LogData} data The log data.
19
+ * @param {StoreData} store The store data.
20
+ * @param {Options} options The logger options.
21
+ */
22
+ async function log(
23
+ level: LogLevel,
24
+ request: RequestInfo,
25
+ data: LogData,
26
+ store: StoreData,
27
+ options?: Options
28
+ ): Promise<void> {
29
+ if (!filterLog(level, data.status || 200, request.method, options)) {
30
+ return
31
+ }
32
+
33
+ const logMessage = buildLogMessage(level, request, data, store, options)
34
+ console.log(logMessage)
35
+ }
36
+
37
+ /**
38
+ * Creates a logger instance.
39
+ *
40
+ * @param {Options} options The logger options.
41
+ * @returns {Logger} The logger instance.
42
+ */
43
+ export function createLogger(options?: Options): Logger {
44
+ return {
45
+ log: async (level, request, data, store) =>
46
+ log(level, request, data, store, options),
47
+ customLogFormat: options?.config?.customLogFormat
48
+ }
49
+ }
@@ -19,31 +19,28 @@ function filterLog(
19
19
 
20
20
  if (!filter) return true
21
21
 
22
- // Level
23
22
  if (filter.level) {
24
23
  if (Array.isArray(filter.level)) {
25
24
  if (!filter.level.includes(logLevel)) return false
25
+ } else {
26
+ if (filter.level !== logLevel) return false
26
27
  }
27
- } else {
28
- if (filter.level !== logLevel) return false
29
28
  }
30
29
 
31
- // Status
32
30
  if (filter.status) {
33
31
  if (Array.isArray(filter.status)) {
34
32
  if (!filter.status.includes(status)) return false
33
+ } else {
34
+ if (filter.status !== status) return false
35
35
  }
36
- } else {
37
- if (filter.status !== status) return false
38
36
  }
39
37
 
40
- // Method
41
38
  if (filter.method) {
42
39
  if (Array.isArray(filter.method)) {
43
40
  if (!filter.method.includes(method)) return false
41
+ } else {
42
+ if (filter.method !== method) return false
44
43
  }
45
- } else {
46
- if (filter.method !== method) return false
47
44
  }
48
45
 
49
46
  return true
@@ -1,20 +1,21 @@
1
1
  import { HttpError, Options, RequestInfo, StoreData } from '~/types'
2
- import { buildLogMessage } from '.'
2
+
3
+ import { buildLogMessage } from './buildLogMessage'
3
4
 
4
5
  /**
5
- * Handles an HTTP error.
6
+ * Handles an HTTP error and logs it.
6
7
  *
7
- * @param {RequestInfo} request The request.
8
+ * @param {RequestInfo} request The request information.
8
9
  * @param {HttpError} error The HTTP error.
9
10
  * @param {StoreData} store The store data.
10
- * @param {Options} options The options.
11
+ * @param {Options} options The logger options.
11
12
  */
12
- function handleHttpError(
13
+ export async function handleHttpError(
13
14
  request: RequestInfo,
14
15
  error: HttpError,
15
16
  store: StoreData,
16
17
  options?: Options
17
- ): void {
18
+ ): Promise<void> {
18
19
  const statusCode = error.status || 500
19
20
  const logMessage = buildLogMessage(
20
21
  'ERROR',
@@ -25,5 +26,3 @@ function handleHttpError(
25
26
  )
26
27
  console.error(logMessage)
27
28
  }
28
-
29
- export { handleHttpError }
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  interface RequestInfo {
2
- headers: { get: (key: string) => any }
2
+ headers: { get: (key: string) => string | null }
3
3
  method: string
4
4
  url: string
5
5
  }
@@ -57,13 +57,13 @@ interface Options {
57
57
  }
58
58
 
59
59
  export {
60
- RequestInfo,
61
- Server,
62
60
  ColorMap,
63
- LogLevel,
61
+ HttpError,
64
62
  LogData,
65
63
  Logger,
66
- StoreData,
67
- HttpError,
68
- Options
64
+ LogLevel,
65
+ Options,
66
+ RequestInfo,
67
+ Server,
68
+ StoreData
69
69
  }
@@ -1,4 +1,5 @@
1
1
  import chalk from 'chalk'
2
+
2
3
  import { ColorMap } from '~/types'
3
4
 
4
5
  /**
@@ -22,19 +23,19 @@ const LogLevelColorMap: ColorMap = {
22
23
  * @property {chalk.Chalk} GET The color for the GET HTTP method.
23
24
  * @property {chalk.Chalk} POST The color for the POST HTTP method.
24
25
  * @property {chalk.Chalk} PUT The color for the PUT HTTP method.
25
- * @property {chalk.Chalk} DELETE The color for the DELETE HTTP method.
26
26
  * @property {chalk.Chalk} PATCH The color for the PATCH HTTP method.
27
- * @property {chalk.Chalk} OPTIONS The color for the OPTIONS HTTP method.
27
+ * @property {chalk.Chalk} DELETE The color for the DELETE HTTP method.
28
28
  * @property {chalk.Chalk} HEAD The color for the HEAD HTTP method.
29
+ * @property {chalk.Chalk} OPTIONS The color for the OPTIONS HTTP method.
29
30
  */
30
31
  const HttpMethodColorMap: ColorMap = {
31
- GET: chalk.white,
32
+ GET: chalk.green,
32
33
  POST: chalk.yellow,
33
34
  PUT: chalk.blue,
35
+ PATCH: chalk.magentaBright,
34
36
  DELETE: chalk.red,
35
- PATCH: chalk.green,
36
- OPTIONS: chalk.cyan,
37
- HEAD: chalk.magenta
37
+ HEAD: chalk.cyan,
38
+ OPTIONS: chalk.magenta
38
39
  }
39
40
 
40
- export { LogLevelColorMap, HttpMethodColorMap }
41
+ export { HttpMethodColorMap, LogLevelColorMap }
@@ -9,17 +9,18 @@ import chalk from 'chalk'
9
9
  * @returns {string} A formatted duration string including the time unit.
10
10
  */
11
11
  function durationString(beforeTime: bigint): string {
12
- const nanoseconds = Number(process.hrtime.bigint() - beforeTime)
12
+ const currentTime = process.hrtime.bigint()
13
+ const nanoseconds = Number(currentTime - beforeTime)
13
14
 
14
15
  const timeUnits = [
15
- { unit: 's', threshold: 1e9 },
16
- { unit: 'ms', threshold: 1e6 },
17
- { unit: 'µs', threshold: 1e3 }
16
+ { unit: 's', threshold: 1e9, decimalPlaces: 2 },
17
+ { unit: 'ms', threshold: 1e6, decimalPlaces: 0 },
18
+ { unit: 'µs', threshold: 1e3, decimalPlaces: 0 }
18
19
  ]
19
20
 
20
- for (const { unit, threshold } of timeUnits) {
21
+ for (const { unit, threshold, decimalPlaces } of timeUnits) {
21
22
  if (nanoseconds >= threshold) {
22
- const value = (nanoseconds / threshold).toFixed(threshold === 1e9 ? 2 : 0)
23
+ const value = (nanoseconds / threshold).toFixed(decimalPlaces)
23
24
  return formatTime(value, unit)
24
25
  }
25
26
  }
package/src/utils/log.ts CHANGED
@@ -1,20 +1,16 @@
1
+ import { LogLevel } from '~/types'
2
+
1
3
  import { LogLevelColorMap } from './colorMapping'
2
4
 
3
5
  /**
4
- * Converts a log level to a colored string representation.
5
- *
6
- * @param {string} log The log level (e.g., 'INFO', 'WARNING').
6
+ * Converts the log level to a string.
7
7
  *
8
- * @returns {string} A colored string representing the log level.
8
+ * @param {LogLevel} level The log level.
9
+ * @returns {string} The log level as a string.
9
10
  */
10
- function logString(log: string): string {
11
- const colorFunction = LogLevelColorMap[log]
12
-
13
- if (colorFunction) {
14
- return colorFunction(log.padEnd(7))
15
- }
16
-
17
- return log
11
+ function logString(level: LogLevel): string {
12
+ const levelStr = level.toUpperCase()
13
+ return LogLevelColorMap[levelStr]?.(levelStr.padEnd(7)) || levelStr
18
14
  }
19
15
 
20
16
  export default logString
package/src/utils/path.ts CHANGED
@@ -11,7 +11,7 @@ import { RequestInfo } from '~/types'
11
11
  function pathString(requestInfo: RequestInfo): string | undefined {
12
12
  try {
13
13
  return new URL(requestInfo.url).pathname
14
- } catch (error) {
14
+ } catch {
15
15
  return undefined
16
16
  }
17
17
  }
@@ -9,9 +9,9 @@ import { Server } from '~/types'
9
9
  * @returns {string} The box text.
10
10
  */
11
11
  function createBoxText(text: string, width: number): string {
12
- const paddingLength = Math.max(0, (width - text.length + 1) / 2)
12
+ const paddingLength = Math.max(0, (width - text.length) / 2)
13
13
  const padding = ' '.repeat(paddingLength)
14
- return `${padding}${text}${padding}`.slice(0, width)
14
+ return `${padding}${text}${padding}`.padEnd(width)
15
15
  }
16
16
 
17
17
  /**
@@ -26,8 +26,7 @@ function startServer(config: Server): void {
26
26
  const ELYSIA_VERSION = import.meta.require('elysia/package.json').version
27
27
  const title = `Elysia v${ELYSIA_VERSION}`
28
28
  const message = `🦊 Elysia is running at ${protocol}://${hostname}:${port}`
29
- const messageWidth = message.length
30
- const boxWidth = Math.max(title.length, messageWidth) + 4
29
+ const boxWidth = Math.max(title.length, message.length) + 4
31
30
  const border = '─'.repeat(boxWidth)
32
31
 
33
32
  console.log(`
@@ -1,25 +1,24 @@
1
1
  import chalk from 'chalk'
2
2
 
3
3
  /**
4
- * Returns the status string.
4
+ * Converts the status code to a string.
5
5
  *
6
6
  * @param {number} status The status code.
7
7
  *
8
- * @returns {string} The status string.
8
+ * @returns {string} The status code as a string.
9
9
  */
10
10
  function statusString(status: number): string {
11
- switch (true) {
12
- case status >= 500:
13
- return chalk.red(status.toString())
14
- case status >= 400:
15
- return chalk.yellow(status.toString())
16
- case status >= 300:
17
- return chalk.cyan(status.toString())
18
- case status >= 200:
19
- return chalk.green(status.toString())
20
- default:
21
- return status.toString()
22
- }
11
+ const color =
12
+ status >= 500
13
+ ? 'red'
14
+ : status >= 400
15
+ ? 'yellow'
16
+ : status >= 300
17
+ ? 'cyan'
18
+ : status >= 200
19
+ ? 'green'
20
+ : 'white'
21
+ return chalk[color](status.toString())
23
22
  }
24
23
 
25
24
  export default statusString
package/.gitguardian.yml DELETED
@@ -1,16 +0,0 @@
1
- # Required, otherwise ggshield considers the file to use the deprecated v1 format
2
- version: 2
3
-
4
- # Set to true if the desired exit code for the CLI is always 0, otherwise the
5
- # exit code will be 1 if incidents are found.
6
- exit-zero: false
7
-
8
- verbose: false
9
-
10
- instance: https://dashboard.gitguardian.com
11
-
12
- # Maximum commits to scan in a hook.
13
- max-commits-for-hook: 50
14
-
15
- # Accept self-signed certificates for the API.
16
- allow-self-signed: false
package/.prettierignore DELETED
@@ -1,2 +0,0 @@
1
- CHANGELOG.md
2
- node_modules/
package/eslint.config.cjs DELETED
@@ -1,25 +0,0 @@
1
- module.exports = {
2
- env: {
3
- es2021: true,
4
- node: true
5
- },
6
- extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
7
- parser: '@typescript-eslint/parser',
8
- parserOptions: {
9
- ecmaVersion: 'latest',
10
- sourceType: 'module'
11
- },
12
- plugins: ['@typescript-eslint'],
13
- rules: {
14
- '@typescript-eslint/ban-types': 'off',
15
- '@typescript-eslint/no-explicit-any': 'off',
16
- 'no-mixed-spaces-and-tabs': 'off',
17
- '@typescript-eslint/no-non-null-assertion': 'off',
18
- '@typescript-eslint/no-extra-semi': 'off',
19
- '@typescript-eslint/ban-ts-comment': 'off',
20
- '@typescript-eslint/no-namespace': 'off',
21
- 'no-case-declarations': 'off',
22
- 'no-extra-semi': 'off'
23
- },
24
- ignorePatterns: ['example/*', 'tests/**/*', 'lint-staged.config.js']
25
- }
@@ -1,11 +0,0 @@
1
- const options = {
2
- arrowParens: 'avoid',
3
- singleQuote: true,
4
- bracketSpacing: true,
5
- endOfLine: 'lf',
6
- semi: false,
7
- tabWidth: 2,
8
- trailingComma: 'none'
9
- }
10
-
11
- module.exports = options
@@ -1,229 +0,0 @@
1
- import { Elysia } from 'elysia'
2
- import { edenTreaty } from '@elysiajs/eden'
3
- import { describe, it, expect, beforeAll, beforeEach } from 'bun:test'
4
- import logixlysia from '../src'
5
-
6
- describe('Logixlysia with IP logging enabled', () => {
7
- let server: Elysia
8
- let app: any
9
- let logs: string[] = []
10
-
11
- beforeAll(() => {
12
- server = new Elysia()
13
- .use(
14
- logixlysia({
15
- config: {
16
- ip: true,
17
- customLogFormat:
18
- '🦊 {now} {duration} {level} {method} {pathname} {status} {message} {ip}'
19
- }
20
- })
21
- )
22
- .get('/', ctx => {
23
- const ipAddress = ctx.request.headers.get('x-forwarded-for') || 'null'
24
- return '🦊 Logixlysia Getting'
25
- })
26
- .post('logixlysia', () => '🦊 Logixlysia Posting')
27
- .listen(3000)
28
-
29
- app = edenTreaty<typeof server>('http://127.0.0.1:3000')
30
- })
31
-
32
- beforeEach(() => {
33
- logs = []
34
- })
35
-
36
- it("Logs incoming IP address for GET '/' requests when X-Forwarded-For header is present", async () => {
37
- const requestCount = 5
38
-
39
- for (let i = 0; i < requestCount; i++) {
40
- await app.get('/', {
41
- headers: { 'X-Forwarded-For': '192.168.1.1' }
42
- })
43
- }
44
-
45
- logs.forEach(log => {
46
- expect(log).toMatch(
47
- /^🦊 .+ INFO .+ .+ GET \/ .+ IP: \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
48
- )
49
- })
50
- })
51
-
52
- it("Logs 'null' for GET '/' requests when X-Forwarded-For header is not present", async () => {
53
- const requestCount = 5
54
-
55
- for (let i = 0; i < requestCount; i++) {
56
- const response = await app.get('/')
57
- }
58
-
59
- logs.forEach(log => {
60
- expect(log).toMatch(/^🦊 .+ INFO .+ .+ GET \/ .+ IP: null$/)
61
- })
62
- })
63
- })
64
-
65
- describe('Logixlysia with IP logging disabled', () => {
66
- let server: Elysia
67
- let app: any
68
- let logs: string[] = []
69
-
70
- beforeAll(() => {
71
- server = new Elysia()
72
- .use(
73
- logixlysia({
74
- config: {
75
- ip: false,
76
- customLogFormat:
77
- '🦊 {now} {duration} {level} {method} {pathname} {status} {message} {ip}'
78
- }
79
- })
80
- )
81
- .get('/', () => '🦊 Logixlysia Getting')
82
- .post('logixlysia', () => '🦊 Logixlysia Posting')
83
- .listen(3000)
84
-
85
- app = edenTreaty<typeof server>('http://127.0.0.1:3000')
86
- })
87
-
88
- beforeEach(() => {
89
- logs = []
90
- })
91
-
92
- it("Responds correctly to GET '/' requests", async () => {
93
- const requestCount = 5
94
-
95
- for (let i = 0; i < requestCount; i++) {
96
- logs.push((await app.get('/')).data)
97
- }
98
-
99
- logs.forEach(log => {
100
- expect(log).toBe('🦊 Logixlysia Getting')
101
- })
102
- })
103
-
104
- it("Responds correctly to POST '/logixlysia' requests", async () => {
105
- const requestCount = 5
106
-
107
- for (let i = 0; i < requestCount; i++) {
108
- const postResponse = await app.logixlysia.post({})
109
- logs.push(
110
- postResponse.status === 200 ? postResponse.data : postResponse.error
111
- )
112
- }
113
-
114
- logs.forEach(log => {
115
- expect(log).toBe('🦊 Logixlysia Posting')
116
- })
117
- })
118
-
119
- it('Throws an error when attempting to post to an undefined route', async () => {
120
- const response = await app.undefinedRoute.post({})
121
- const error = response.error
122
-
123
- expect(response.status).toBe(404)
124
- expect(error).toBeInstanceOf(Error)
125
- })
126
- })
127
-
128
- describe('Logixlysia with log filtering enabled', () => {
129
- let server: Elysia
130
- let app: any
131
- let logs: string[] = []
132
-
133
- beforeAll(() => {
134
- server = new Elysia()
135
- .use(
136
- logixlysia({
137
- config: {
138
- logFilter: {
139
- level: 'INFO',
140
- status: [200, 404],
141
- method: 'GET'
142
- }
143
- }
144
- })
145
- )
146
- .get('/', () => '🦊 Logixlysia Getting')
147
- .post('logixlysia', () => '🦊 Logixlysia Posting')
148
- .listen(3000)
149
-
150
- app = edenTreaty<typeof server>('http://127.0.0.1:3000')
151
- })
152
-
153
- beforeEach(() => {
154
- logs = []
155
- })
156
-
157
- it("Logs 'GET' requests with status 200 or 404 when log filtering criteria are met", async () => {
158
- const requestCount = 5
159
-
160
- for (let i = 0; i < requestCount; i++) {
161
- logs.push((await app.get('/')).data)
162
- }
163
-
164
- expect(logs.length).toBe(requestCount)
165
- logs.forEach(log => {
166
- expect(log).toMatch('🦊 Logixlysia Getting')
167
- })
168
- })
169
-
170
- it("Doesn't log 'POST' requests when log filtering criteria are not met", async () => {
171
- const requestCount = 5
172
-
173
- for (let i = 0; i < requestCount; i++) {
174
- await app.post('/logixlysia', {})
175
- }
176
-
177
- expect(logs.length).toBe(0)
178
- })
179
-
180
- const otherMethods = ['PUT', 'DELETE', 'PATCH', 'HEAD'] // OPTIONS is failed (IDK why)
181
- otherMethods.forEach(async method => {
182
- it(`Logs '${method}' requests with status 200 or 404 when log filtering criteria are met`, async () => {
183
- const requestCount = 5
184
-
185
- for (let i = 0; i < requestCount; i++) {
186
- logs.push((await app[method.toLowerCase()]('/')).data)
187
- }
188
-
189
- expect(logs.length).toBe(requestCount)
190
- })
191
- })
192
- })
193
-
194
- describe('Logixlysia with log filtering disabled', () => {
195
- let server: Elysia
196
- let app: any
197
- let logs: string[] = []
198
-
199
- beforeAll(() => {
200
- server = new Elysia()
201
- .use(
202
- logixlysia({
203
- config: {
204
- logFilter: null
205
- }
206
- })
207
- )
208
- .get('/', () => '🦊 Logixlysia Getting')
209
- .post('logixlysia', () => '🦊 Logixlysia Posting')
210
- .listen(3000)
211
-
212
- app = edenTreaty<typeof server>('http://127.0.0.1:3000')
213
- })
214
-
215
- beforeEach(() => {
216
- logs = []
217
- })
218
-
219
- it('Logs all requests when log filtering is disabled', async () => {
220
- const requestCount = 5
221
-
222
- for (let i = 0; i < requestCount; i++) {
223
- logs.push((await app.get('/')).data)
224
- logs.push((await app.post('/logixlysia', {})).data)
225
- }
226
-
227
- expect(logs.length).toBe(requestCount * 2)
228
- })
229
- })
@@ -1,24 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import { ColorMap } from '~/types'
3
-
4
- describe('Color Mapping Interface', () => {
5
- it('Defines an object with string keys mapping to functions', () => {
6
- const colorMap: ColorMap = {
7
- red: (str: string) => `Red: ${str}`,
8
- green: (str: string) => `Green: ${str}`,
9
- blue: (str: string) => `Blue: ${str}`
10
- }
11
-
12
- expect(colorMap).toEqual(
13
- expect.objectContaining({
14
- red: expect.any(Function),
15
- green: expect.any(Function),
16
- blue: expect.any(Function)
17
- })
18
- )
19
-
20
- Object.keys(colorMap).forEach(key => {
21
- expect(typeof colorMap[key]).toBe('function')
22
- })
23
- })
24
- })
@@ -1,15 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import { HttpError } from '~/types'
3
-
4
- describe('HttpError', () => {
5
- it('Should create an instance with correct status and message', () => {
6
- const status = 404
7
- const message = 'Not Found'
8
- const error = new HttpError(status, message)
9
-
10
- expect(error).toBeInstanceOf(Error)
11
- expect(error).toBeInstanceOf(HttpError)
12
- expect(error.status).toBe(status)
13
- expect(error.message).toBe(message)
14
- })
15
- })
@@ -1,90 +0,0 @@
1
- import { beforeEach, describe, expect, it, jest } from 'bun:test'
2
- import { RequestInfo, LogData, StoreData } from '~/types'
3
-
4
- interface Logger {
5
- info(request: RequestInfo, data: LogData, store: StoreData): void
6
- warning(request: RequestInfo, data: LogData, store: StoreData): void
7
- error(request: RequestInfo, data: LogData, store: StoreData): void
8
- }
9
-
10
- describe('Logger interface', () => {
11
- let logger: Logger
12
-
13
- beforeEach(() => {
14
- logger = {
15
- info: jest.fn(),
16
- warning: jest.fn(),
17
- error: jest.fn()
18
- }
19
- })
20
-
21
- it('Defines the Logger interface correctly', () => {
22
- expect(logger).toEqual(
23
- expect.objectContaining({
24
- info: expect.any(Function),
25
- warning: expect.any(Function),
26
- error: expect.any(Function)
27
- })
28
- )
29
- })
30
-
31
- it('Calls the info log function with the correct arguments', () => {
32
- const request: RequestInfo = {
33
- url: '/info',
34
- method: 'GET',
35
- headers: {
36
- get: function () {
37
- throw new Error('Function not implemented.')
38
- }
39
- }
40
- }
41
- const data: LogData = { status: 200, message: 'Info log message' }
42
- const store: StoreData = {
43
- beforeTime: 0n
44
- }
45
-
46
- logger.info(request, data, store)
47
-
48
- expect(logger.info).toHaveBeenCalledWith(request, data, store)
49
- })
50
-
51
- it('Calls the warning log function with the correct arguments', () => {
52
- const request: RequestInfo = {
53
- url: '/warning',
54
- method: 'POST',
55
- headers: {
56
- get: function () {
57
- throw new Error('Function not implemented.')
58
- }
59
- }
60
- }
61
- const data: LogData = { status: 404, message: 'Warning log message' }
62
- const store: StoreData = {
63
- beforeTime: 0n
64
- }
65
-
66
- logger.warning(request, data, store)
67
-
68
- expect(logger.warning).toHaveBeenCalledWith(request, data, store)
69
- })
70
-
71
- it('Calls the error log function with the correct arguments', () => {
72
- const request: RequestInfo = {
73
- url: '/error',
74
- method: 'DELETE',
75
- headers: {
76
- get: function () {
77
- throw new Error('Function not implemented.')
78
- }
79
- }
80
- }
81
- const data: LogData = { status: 500, message: 'Error log message' }
82
- const store: StoreData = {
83
- beforeTime: 0n
84
- }
85
-
86
- logger.error(request, data, store)
87
-
88
- expect(logger.error).toHaveBeenCalledWith(request, data, store)
89
- })
90
- })
@@ -1,22 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import { RequestInfo } from '~/types'
3
-
4
- describe('Request Infomation interface', () => {
5
- it('Defines the RequestInfo interface correctly', () => {
6
- const headers = { get: () => 'value' }
7
- const method = 'GET'
8
- const url = 'https://example.com/api'
9
-
10
- const request: RequestInfo = { headers, method, url }
11
-
12
- expect(request).toEqual(
13
- expect.objectContaining({
14
- headers: expect.objectContaining({
15
- get: expect.any(Function)
16
- }),
17
- method: expect.any(String),
18
- url: expect.any(String)
19
- })
20
- )
21
- })
22
- })
@@ -1,26 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import { Server } from '~/types'
3
-
4
- describe('Server interface', () => {
5
- it('Defines the Server interface correctly', () => {
6
- const server: Server = {
7
- hostname: 'example.com',
8
- port: 8080,
9
- protocol: 'https'
10
- }
11
-
12
- expect(server).toEqual(
13
- expect.objectContaining({
14
- hostname: expect.any(String),
15
- port: expect.any(Number),
16
- protocol: expect.any(String)
17
- })
18
- )
19
- })
20
-
21
- it('Allows optional properties in the Server interface', () => {
22
- const serverWithoutOptionalProps: Server = {}
23
-
24
- expect(serverWithoutOptionalProps).toEqual(expect.objectContaining({}))
25
- })
26
- })
@@ -1,16 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import { StoreData } from '~/types'
3
-
4
- describe('Store Data interface', () => {
5
- it('Defines the StoreData interface correctly', () => {
6
- const beforeTime: bigint = BigInt(1633393533526) // Example bigint value
7
-
8
- const storeData: StoreData = { beforeTime }
9
-
10
- expect(storeData).toEqual(
11
- expect.objectContaining({
12
- beforeTime: expect.any(BigInt)
13
- })
14
- )
15
- })
16
- })
@@ -1,36 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import durationString from '~/utils/duration'
3
-
4
- describe('Duration String', () => {
5
- const testCases = [
6
- [
7
- 'Generates a string representing the duration in Seconds (s) unit',
8
- 1e9,
9
- 's'
10
- ],
11
- [
12
- 'Generates a string representing the duration in Milliseconds (ms) unit',
13
- 1e6,
14
- 'ms'
15
- ],
16
- [
17
- 'Generates a string representing the duration in Microseconds (µs) unit',
18
- 1e3,
19
- 'µs'
20
- ]
21
- // [
22
- // 'Generates a string representing the duration in Nanoseconds (ns) unit',
23
- // 1,
24
- // 'ns'
25
- // ]
26
- ]
27
-
28
- for (const [description, nanoseconds, unit] of testCases) {
29
- it(`${description}`, () => {
30
- const beforeTime = process.hrtime.bigint() - BigInt(String(nanoseconds))
31
- const result = durationString(beforeTime)
32
-
33
- expect(result).toMatch(String(unit))
34
- })
35
- }
36
- })
@@ -1,25 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import chalk from 'chalk'
3
- import logString from '~/utils/log'
4
-
5
- describe('Log String', () => {
6
- it('Produces a green background string for INFO log level', () => {
7
- const result = logString('INFO')
8
- expect(result).toBe(chalk.bgGreen.black('INFO '))
9
- })
10
-
11
- it('Produces a yellow background string for WARNING log leve', () => {
12
- const result = logString('WARNING')
13
- expect(result).toBe(chalk.bgYellow.black('WARNING'))
14
- })
15
-
16
- it('Produces a red background string for ERROR log level', () => {
17
- const result = logString('ERROR')
18
- expect(result).toBe(chalk.bgRed.black('ERROR '))
19
- })
20
-
21
- it('Returns the unmodified input string for unrecognized log levels', () => {
22
- const result = logString('DEBUG') // Assuming 'DEBUG' is not in the colorMap
23
- expect(result).toBe('DEBUG') // No coloring, returns the original string
24
- })
25
- })
@@ -1,20 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import chalk from 'chalk'
3
- import methodString from '~/utils/method'
4
-
5
- describe('Method String', () => {
6
- it('Displays a colored string for the GET method', () => {
7
- const result = methodString('GET')
8
- expect(result).toBe(chalk.white('GET '))
9
- })
10
-
11
- it('Displays a colored string for the POST method', () => {
12
- const result = methodString('POST')
13
- expect(result).toBe(chalk.yellow('POST '))
14
- })
15
-
16
- it('Outputs the original method string if it is not recognized', () => {
17
- const result = methodString('INVALID_METHOD')
18
- expect(result).toBe('INVALID_METHOD') // No coloring, returns the original string
19
- })
20
- })
@@ -1,35 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import { RequestInfo } from '~/types'
3
- import pathString from '~/utils/path'
4
-
5
- describe('Path String', () => {
6
- it('Extracts the pathname from a valid URL', () => {
7
- const testPath: RequestInfo = {
8
- url: 'https://www.example.com/path/to/resource',
9
- headers: new Map(),
10
- method: 'GET'
11
- }
12
- const result = pathString(testPath)
13
- expect(result).toBe('/path/to/resource')
14
- })
15
-
16
- it('Handles malformed URL gracefully', () => {
17
- const testPath: RequestInfo = {
18
- url: 'invalid url',
19
- headers: new Map(),
20
- method: 'GET'
21
- }
22
- const result = pathString(testPath)
23
- expect(result).toBeUndefined()
24
- })
25
-
26
- it('Returns undefined if the URL is missing', () => {
27
- const testPath: RequestInfo = {
28
- url: '',
29
- headers: new Map(),
30
- method: 'GET'
31
- }
32
- const result = pathString(testPath)
33
- expect(result).toBeUndefined()
34
- })
35
- })
@@ -1,37 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, jest } from 'bun:test'
2
- import { Server } from '~/types'
3
- import startServer from '~/utils/start'
4
-
5
- describe('Start String', () => {
6
- let originalConsoleLog: any
7
- let mockConsoleLog: jest.Mock
8
-
9
- beforeEach(() => {
10
- originalConsoleLog = console.log
11
- mockConsoleLog = jest.fn()
12
- console.log = mockConsoleLog
13
- })
14
-
15
- afterEach(() => {
16
- console.log = originalConsoleLog
17
- })
18
-
19
- it('Correctly logs the expected message upon server start', () => {
20
- const config: Server = {
21
- hostname: 'localhost',
22
- port: 3000,
23
- protocol: 'http'
24
- }
25
-
26
- startServer(config)
27
-
28
- const expectedMessage = `🦊 Elysia is running at http://localhost:3000`
29
-
30
- // Extract the arguments passed to console.log during the function call
31
- const logMessage = mockConsoleLog.mock.calls
32
- .map((args: any[]) => args.join(' '))
33
- .join(' ')
34
-
35
- expect(logMessage).toMatch(expectedMessage)
36
- })
37
- })
@@ -1,30 +0,0 @@
1
- import chalk from 'chalk'
2
- import { describe, expect, it } from 'bun:test'
3
- import statusString from '~/utils/status'
4
-
5
- describe('Status String', () => {
6
- it('Presents the status string in green for a 200 status code', () => {
7
- const result = statusString(200)
8
- expect(result).toBe(chalk.green('200'))
9
- })
10
-
11
- it('Presents the status string in green for a 301 status code', () => {
12
- const result = statusString(301)
13
- expect(result).toBe(chalk.cyan('301'))
14
- })
15
-
16
- it('Presents the status string in green for a 404 status code', () => {
17
- const result = statusString(404)
18
- expect(result).toBe(chalk.yellow('404'))
19
- })
20
-
21
- it('Presents the status string in green for a 500 status code', () => {
22
- const result = statusString(500)
23
- expect(result).toBe(chalk.red('500'))
24
- })
25
-
26
- it('Presents the status string in green for a 100 status code', () => {
27
- const result = statusString(100)
28
- expect(result).toBe('100')
29
- })
30
- })
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "$schema": "https://json.schemastore.org/tsconfig",
3
- "compilerOptions": {
4
- "noUncheckedIndexedAccess": true,
5
- "strict": true,
6
- "allowImportingTsExtensions": true,
7
- "module": "ESNext",
8
- "moduleResolution": "Bundler",
9
- "paths": {
10
- "~/*": ["./src/*"]
11
- },
12
- "resolveJsonModule": true,
13
- "types": ["bun-types"],
14
- "downlevelIteration": true,
15
- "noEmit": true,
16
- "allowJs": true,
17
- "allowSyntheticDefaultImports": true,
18
- "forceConsistentCasingInFileNames": true,
19
- "jsx": "react",
20
- "jsxFactory": "ElysiaJSX",
21
- "jsxFragmentFactory": "ElysiaJSX.Fragment",
22
- "lib": ["ESNext"],
23
- "moduleDetection": "force",
24
- "target": "ESNext",
25
- "composite": true,
26
- "skipLibCheck": true
27
- }
28
- }