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 +3 -0
- package/.trunk/trunk.yaml +26 -0
- package/CHANGELOG.md +14 -0
- package/README.md +12 -5
- package/lint-staged.config.cjs +6 -25
- package/package.json +19 -13
- package/src/index.ts +9 -6
- package/src/logger/{index.ts → buildLogMessage.ts} +7 -54
- package/src/logger/createLogger.ts +49 -0
- package/src/logger/filter.ts +6 -9
- package/src/logger/{handle.ts → handleHttpError.ts} +7 -8
- package/src/types.ts +7 -7
- package/src/utils/colorMapping.ts +8 -7
- package/src/utils/duration.ts +7 -6
- package/src/utils/log.ts +8 -12
- package/src/utils/path.ts +1 -1
- package/src/utils/start.ts +3 -4
- package/src/utils/status.ts +13 -14
- package/.gitguardian.yml +0 -16
- package/.prettierignore +0 -2
- package/eslint.config.cjs +0 -25
- package/prettier.config.cjs +0 -11
- package/test/logixlysia.test.ts +0 -229
- package/test/types/ColorMap.test.ts +0 -24
- package/test/types/HttpError.test.ts +0 -15
- package/test/types/Logger.test.ts +0 -90
- package/test/types/RequestInfo.test.ts +0 -22
- package/test/types/Server.test.ts +0 -26
- package/test/types/StoreData.test.ts +0 -16
- package/test/utils/duration.test.ts +0 -36
- package/test/utils/log.test.ts +0 -25
- package/test/utils/method.test.ts +0 -20
- package/test/utils/path.test.ts +0 -35
- package/test/utils/start.test.ts +0 -37
- package/test/utils/status.test.ts +0 -30
- package/tsconfig.json +0 -28
package/.gitmodules
ADDED
|
@@ -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 {
|
|
17
|
+
import { logixlysia } from 'logixlysia'
|
|
18
18
|
|
|
19
19
|
const app = new Elysia({
|
|
20
20
|
name: 'Logixlysia Example'
|
|
21
21
|
}).use(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
package/lint-staged.config.cjs
CHANGED
|
@@ -1,27 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
'**/*.(
|
|
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.
|
|
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
|
|
19
|
-
"lint:fix": "eslint
|
|
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.
|
|
75
|
-
"eslint": "^9.0.0"
|
|
74
|
+
"elysia": "^1.0.21"
|
|
76
75
|
},
|
|
77
76
|
"devDependencies": {
|
|
78
|
-
"@elysiajs/eden": "^1.0.
|
|
79
|
-
"
|
|
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
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
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
|
|
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
|
-
* @
|
|
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
|
-
|
|
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
|
+
}
|
package/src/logger/filter.ts
CHANGED
|
@@ -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
|
-
|
|
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) =>
|
|
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
|
-
|
|
61
|
+
HttpError,
|
|
64
62
|
LogData,
|
|
65
63
|
Logger,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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}
|
|
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.
|
|
32
|
+
GET: chalk.green,
|
|
32
33
|
POST: chalk.yellow,
|
|
33
34
|
PUT: chalk.blue,
|
|
35
|
+
PATCH: chalk.magentaBright,
|
|
34
36
|
DELETE: chalk.red,
|
|
35
|
-
|
|
36
|
-
OPTIONS: chalk.
|
|
37
|
-
HEAD: chalk.magenta
|
|
37
|
+
HEAD: chalk.cyan,
|
|
38
|
+
OPTIONS: chalk.magenta
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
export {
|
|
41
|
+
export { HttpMethodColorMap, LogLevelColorMap }
|
package/src/utils/duration.ts
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
|
5
|
-
*
|
|
6
|
-
* @param {string} log The log level (e.g., 'INFO', 'WARNING').
|
|
6
|
+
* Converts the log level to a string.
|
|
7
7
|
*
|
|
8
|
-
* @
|
|
8
|
+
* @param {LogLevel} level The log level.
|
|
9
|
+
* @returns {string} The log level as a string.
|
|
9
10
|
*/
|
|
10
|
-
function logString(
|
|
11
|
-
const
|
|
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
package/src/utils/start.ts
CHANGED
|
@@ -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
|
|
12
|
+
const paddingLength = Math.max(0, (width - text.length) / 2)
|
|
13
13
|
const padding = ' '.repeat(paddingLength)
|
|
14
|
-
return `${padding}${text}${padding}`.
|
|
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
|
|
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(`
|
package/src/utils/status.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
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
|
-
}
|
package/prettier.config.cjs
DELETED
package/test/logixlysia.test.ts
DELETED
|
@@ -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
|
-
})
|
package/test/utils/log.test.ts
DELETED
|
@@ -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
|
-
})
|
package/test/utils/path.test.ts
DELETED
|
@@ -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
|
-
})
|
package/test/utils/start.test.ts
DELETED
|
@@ -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
|
-
}
|