This commit is contained in:
commit
d554599768
11 changed files with 334 additions and 0 deletions
12
.editorconfig
Normal file
12
.editorconfig
Normal file
|
@ -0,0 +1,12 @@
|
|||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
24
.forgejo/workflows/biomejs.yml
Normal file
24
.forgejo/workflows/biomejs.yml
Normal file
|
@ -0,0 +1,24 @@
|
|||
name: Code quality checks
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
biome:
|
||||
runs-on: docker
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Bun
|
||||
run: |
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
export BUN_INSTALL="$HOME/.bun"
|
||||
echo "$BUN_INSTALL/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Run Biome with verbose output
|
||||
run: bunx biome ci . --verbose
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
28
LICENSE
Normal file
28
LICENSE
Normal file
|
@ -0,0 +1,28 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2025, creations.works
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
50
biome.json
Normal file
50
biome.json
Normal file
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": false
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"ignore": ["dist", "types"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "tab",
|
||||
"lineEnding": "lf"
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"css": {
|
||||
"formatter": {
|
||||
"indentStyle": "tab",
|
||||
"lineEnding": "lf"
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"correctness": {
|
||||
"noUnusedImports": "error",
|
||||
"useJsxKeyInIterable": "off",
|
||||
"noUnusedVariables": "error"
|
||||
},
|
||||
"style": {
|
||||
"useConst": "error",
|
||||
"noVar": "error"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double",
|
||||
"indentStyle": "tab",
|
||||
"lineEnding": "lf",
|
||||
"jsxQuoteStyle": "double",
|
||||
"semicolons": "always"
|
||||
}
|
||||
}
|
||||
}
|
32
package.json
Normal file
32
package.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "@atums/echo",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "bunx biome check",
|
||||
"lint:fix": "bunx biome check --fix",
|
||||
"cleanup": "rm -rf logs node_modules bun.lock"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@types/bun": "^1.2.13"
|
||||
},
|
||||
"files": ["dist", "README.md", "LICENSE"],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.creations.works/creations/logger.git"
|
||||
}
|
||||
}
|
61
src/index.ts
Normal file
61
src/index.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
import {
|
||||
constants,
|
||||
type Stats,
|
||||
accessSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
statSync,
|
||||
} from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { defaultConfig, loadEnvConfig, loadLoggerConfig } from "@lib/config";
|
||||
|
||||
export class Echo {
|
||||
private readonly directory: string;
|
||||
private readonly config: Required<LoggerConfig>;
|
||||
|
||||
constructor(configOrPath?: string | LoggerConfig) {
|
||||
const fileConfig: LoggerConfig =
|
||||
typeof configOrPath === "string"
|
||||
? loadLoggerConfig(configOrPath)
|
||||
: loadLoggerConfig();
|
||||
|
||||
const overrideConfig: LoggerConfig =
|
||||
typeof configOrPath === "object" ? configOrPath : {};
|
||||
|
||||
const envConfig: LoggerConfig = loadEnvConfig();
|
||||
|
||||
this.config = {
|
||||
...defaultConfig,
|
||||
...fileConfig,
|
||||
...envConfig,
|
||||
...overrideConfig,
|
||||
};
|
||||
|
||||
this.directory = resolve(this.config.directory);
|
||||
|
||||
if (!this.config.disableFile) {
|
||||
Echo.validateDirectory(this.directory);
|
||||
}
|
||||
}
|
||||
|
||||
private static validateDirectory(dir: string): void {
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
const stat: Stats = statSync(dir);
|
||||
if (!stat.isDirectory()) {
|
||||
throw new Error(`[@atums/echo] ${dir} is not a directory`);
|
||||
}
|
||||
|
||||
accessSync(dir, constants.W_OK);
|
||||
}
|
||||
|
||||
public getDirectory(): string {
|
||||
return this.directory;
|
||||
}
|
||||
|
||||
public getConfig(): Required<LoggerConfig> {
|
||||
return this.config;
|
||||
}
|
||||
}
|
8
src/lib/char.ts
Normal file
8
src/lib/char.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
function timestampToReadable(timestamp?: number): string {
|
||||
const date: Date =
|
||||
timestamp && !Number.isNaN(timestamp) ? new Date(timestamp) : new Date();
|
||||
if (Number.isNaN(date.getTime())) return "Invalid Date";
|
||||
return date.toISOString().replace("T", " ").replace("Z", "");
|
||||
}
|
||||
|
||||
export { timestampToReadable };
|
59
src/lib/config.ts
Normal file
59
src/lib/config.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const defaultConfig: Required<LoggerConfig> = {
|
||||
directory: "logs",
|
||||
level: "info",
|
||||
disableFile: false,
|
||||
|
||||
rotate: true,
|
||||
maxSizeMB: 5,
|
||||
maxFiles: 3,
|
||||
|
||||
console: true,
|
||||
consoleColor: true,
|
||||
|
||||
dateFormat: "YYYY-MM-DD HH:mm:ss",
|
||||
timezone: "UTC",
|
||||
|
||||
silent: false,
|
||||
|
||||
pattern: "{timestamp} [{level-name}] ({file-name}:{line}){message}",
|
||||
};
|
||||
|
||||
function loadLoggerConfig(configPath = "logger.json"): LoggerConfig {
|
||||
try {
|
||||
const fullPath: string = resolve(process.cwd(), configPath);
|
||||
const raw: string = readFileSync(fullPath, "utf-8");
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function loadEnvConfig(): LoggerConfig {
|
||||
const config: LoggerConfig = {};
|
||||
|
||||
if (process.env.LOG_LEVEL) config.level = process.env.LOG_LEVEL as LogLevel;
|
||||
if (process.env.LOG_DIRECTORY) config.directory = process.env.LOG_DIRECTORY;
|
||||
if (process.env.LOG_DISABLE_FILE)
|
||||
config.disableFile = process.env.LOG_DISABLE_FILE === "true";
|
||||
if (process.env.LOG_ROTATE) config.rotate = process.env.LOG_ROTATE === "true";
|
||||
if (process.env.LOG_MAX_SIZE_MB)
|
||||
config.maxSizeMB = Number.parseInt(process.env.LOG_MAX_SIZE_MB, 10);
|
||||
if (process.env.LOG_MAX_FILES)
|
||||
config.maxFiles = Number.parseInt(process.env.LOG_MAX_FILES, 10);
|
||||
if (process.env.LOG_CONSOLE)
|
||||
config.console = process.env.LOG_CONSOLE === "true";
|
||||
if (process.env.LOG_CONSOLE_COLOR)
|
||||
config.consoleColor = process.env.LOG_CONSOLE_COLOR === "true";
|
||||
if (process.env.LOG_DATE_FORMAT)
|
||||
config.dateFormat = process.env.LOG_DATE_FORMAT;
|
||||
if (process.env.LOG_TIMEZONE) config.timezone = process.env.LOG_TIMEZONE;
|
||||
if (process.env.LOG_SILENT) config.silent = process.env.LOG_SILENT === "true";
|
||||
if (process.env.LOG_PATTERN) config.pattern = process.env.LOG_PATTERN;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export { defaultConfig, loadLoggerConfig, loadEnvConfig };
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@types/*": ["types/*"],
|
||||
"@lib/*": ["src/lib/*"]
|
||||
},
|
||||
"typeRoots": ["./types", "./node_modules/@types"],
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
},
|
||||
"include": ["src", "types", "config"]
|
||||
}
|
32
types/index.d.ts
vendored
Normal file
32
types/index.d.ts
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
const LogLevelValue = {
|
||||
trace: 10,
|
||||
debug: 20,
|
||||
info: 30,
|
||||
warn: 40,
|
||||
error: 50,
|
||||
fatal: 60,
|
||||
silent: 70,
|
||||
} as const;
|
||||
|
||||
type LogLevelValue = typeof LogLevelValue[keyof typeof LogLevelValue];
|
||||
type LogLevel = keyof typeof LogLevelValue;
|
||||
|
||||
type LoggerConfig = {
|
||||
directory?: string;
|
||||
level?: LogLevel;
|
||||
disableFile?: boolean;
|
||||
|
||||
rotate?: boolean;
|
||||
maxSizeMB?: number;
|
||||
maxFiles?: number;
|
||||
|
||||
console?: boolean;
|
||||
consoleColor?: boolean;
|
||||
|
||||
dateFormat?: string;
|
||||
timezone?: string;
|
||||
|
||||
silent?: boolean;
|
||||
|
||||
pattern?: string;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue