Replace eslint and prettier with Biome

This commit is contained in:
Jesse Wierzbinski 2024-04-06 19:30:49 -10:00
parent 4a5a2ea590
commit af0d627f19
No known key found for this signature in database
199 changed files with 16493 additions and 16361 deletions

View file

@ -1,12 +1,12 @@
import { appendFile } from "node:fs/promises";
import type { BunFile } from "bun";
import { appendFile } from "fs/promises";
export enum LogLevel {
DEBUG = "debug",
INFO = "info",
WARNING = "warning",
ERROR = "error",
CRITICAL = "critical",
DEBUG = "debug",
INFO = "info",
WARNING = "warning",
ERROR = "error",
CRITICAL = "critical",
}
/**
@ -14,161 +14,165 @@ export enum LogLevel {
* @param output BunFile of output (can be a normal file or something like Bun.stdout)
*/
export class LogManager {
constructor(private output: BunFile) {
void this.write(
`--- INIT LogManager at ${new Date().toISOString()} ---`
);
}
constructor(private output: BunFile) {
void this.write(
`--- INIT LogManager at ${new Date().toISOString()} ---`,
);
}
/**
* Logs a message to the output
* @param level Importance of the log
* @param entity Emitter of the log
* @param message Message to log
* @param showTimestamp Whether to show the timestamp in the log
*/
async log(
level: LogLevel,
entity: string,
message: string,
showTimestamp = true
) {
await this.write(
`${showTimestamp ? new Date().toISOString() + " " : ""}[${level.toUpperCase()}] ${entity}: ${message}`
);
}
/**
* Logs a message to the output
* @param level Importance of the log
* @param entity Emitter of the log
* @param message Message to log
* @param showTimestamp Whether to show the timestamp in the log
*/
async log(
level: LogLevel,
entity: string,
message: string,
showTimestamp = true,
) {
await this.write(
`${
showTimestamp ? `${new Date().toISOString()} ` : ""
}[${level.toUpperCase()}] ${entity}: ${message}`,
);
}
private async write(text: string) {
if (this.output == Bun.stdout) {
await Bun.write(Bun.stdout, text + "\n");
} else {
if (!(await this.output.exists())) {
// Create file if it doesn't exist
await Bun.write(this.output, "", {
createPath: true,
});
}
await appendFile(this.output.name ?? "", text + "\n");
}
}
private async write(text: string) {
if (this.output === Bun.stdout) {
await Bun.write(Bun.stdout, `${text}\n`);
} else {
if (!(await this.output.exists())) {
// Create file if it doesn't exist
await Bun.write(this.output, "", {
createPath: true,
});
}
await appendFile(this.output.name ?? "", `${text}\n`);
}
}
/**
* Logs an error to the output, wrapper for log
* @param level Importance of the log
* @param entity Emitter of the log
* @param error Error to log
*/
async logError(level: LogLevel, entity: string, error: Error) {
await this.log(level, entity, error.message);
}
/**
* Logs an error to the output, wrapper for log
* @param level Importance of the log
* @param entity Emitter of the log
* @param error Error to log
*/
async logError(level: LogLevel, entity: string, error: Error) {
await this.log(level, entity, error.message);
}
/**
* Logs a request to the output
* @param req Request to log
* @param ip IP of the request
* @param logAllDetails Whether to log all details of the request
*/
async logRequest(req: Request, ip?: string, logAllDetails = false) {
let string = ip ? `${ip}: ` : "";
/**
* Logs a request to the output
* @param req Request to log
* @param ip IP of the request
* @param logAllDetails Whether to log all details of the request
*/
async logRequest(req: Request, ip?: string, logAllDetails = false) {
let string = ip ? `${ip}: ` : "";
string += `${req.method} ${req.url}`;
string += `${req.method} ${req.url}`;
if (logAllDetails) {
string += `\n`;
string += ` [Headers]\n`;
// Pretty print headers
for (const [key, value] of req.headers.entries()) {
string += ` ${key}: ${value}\n`;
}
if (logAllDetails) {
string += "\n";
string += " [Headers]\n";
// Pretty print headers
for (const [key, value] of req.headers.entries()) {
string += ` ${key}: ${value}\n`;
}
// Pretty print body
string += ` [Body]\n`;
const content_type = req.headers.get("Content-Type");
// Pretty print body
string += " [Body]\n";
const content_type = req.headers.get("Content-Type");
if (content_type && content_type.includes("application/json")) {
const json = await req.json();
const stringified = JSON.stringify(json, null, 4)
.split("\n")
.map(line => ` ${line}`)
.join("\n");
if (content_type?.includes("application/json")) {
const json = await req.json();
const stringified = JSON.stringify(json, null, 4)
.split("\n")
.map((line) => ` ${line}`)
.join("\n");
string += `${stringified}\n`;
} else if (
content_type &&
(content_type.includes("application/x-www-form-urlencoded") ||
content_type.includes("multipart/form-data"))
) {
const formData = await req.formData();
for (const [key, value] of formData.entries()) {
if (value.toString().length < 300) {
string += ` ${key}: ${value.toString()}\n`;
} else {
string += ` ${key}: <${value.toString().length} bytes>\n`;
}
}
} else {
const text = await req.text();
string += ` ${text}\n`;
}
}
await this.log(LogLevel.INFO, "Request", string);
}
string += `${stringified}\n`;
} else if (
content_type &&
(content_type.includes("application/x-www-form-urlencoded") ||
content_type.includes("multipart/form-data"))
) {
const formData = await req.formData();
for (const [key, value] of formData.entries()) {
if (value.toString().length < 300) {
string += ` ${key}: ${value.toString()}\n`;
} else {
string += ` ${key}: <${
value.toString().length
} bytes>\n`;
}
}
} else {
const text = await req.text();
string += ` ${text}\n`;
}
}
await this.log(LogLevel.INFO, "Request", string);
}
}
/**
* Outputs to multiple LogManager instances at once
*/
export class MultiLogManager {
constructor(private logManagers: LogManager[]) {}
constructor(private logManagers: LogManager[]) {}
/**
* Logs a message to all logManagers
* @param level Importance of the log
* @param entity Emitter of the log
* @param message Message to log
* @param showTimestamp Whether to show the timestamp in the log
*/
async log(
level: LogLevel,
entity: string,
message: string,
showTimestamp = true
) {
for (const logManager of this.logManagers) {
await logManager.log(level, entity, message, showTimestamp);
}
}
/**
* Logs a message to all logManagers
* @param level Importance of the log
* @param entity Emitter of the log
* @param message Message to log
* @param showTimestamp Whether to show the timestamp in the log
*/
async log(
level: LogLevel,
entity: string,
message: string,
showTimestamp = true,
) {
for (const logManager of this.logManagers) {
await logManager.log(level, entity, message, showTimestamp);
}
}
/**
* Logs an error to all logManagers
* @param level Importance of the log
* @param entity Emitter of the log
* @param error Error to log
*/
async logError(level: LogLevel, entity: string, error: Error) {
for (const logManager of this.logManagers) {
await logManager.logError(level, entity, error);
}
}
/**
* Logs an error to all logManagers
* @param level Importance of the log
* @param entity Emitter of the log
* @param error Error to log
*/
async logError(level: LogLevel, entity: string, error: Error) {
for (const logManager of this.logManagers) {
await logManager.logError(level, entity, error);
}
}
/**
* Logs a request to all logManagers
* @param req Request to log
* @param ip IP of the request
* @param logAllDetails Whether to log all details of the request
*/
async logRequest(req: Request, ip?: string, logAllDetails = false) {
for (const logManager of this.logManagers) {
await logManager.logRequest(req, ip, logAllDetails);
}
}
/**
* Logs a request to all logManagers
* @param req Request to log
* @param ip IP of the request
* @param logAllDetails Whether to log all details of the request
*/
async logRequest(req: Request, ip?: string, logAllDetails = false) {
for (const logManager of this.logManagers) {
await logManager.logRequest(req, ip, logAllDetails);
}
}
/**
* Create a MultiLogManager from multiple LogManager instances
* @param logManagers LogManager instances to use
* @returns
*/
static fromLogManagers(...logManagers: LogManager[]) {
return new MultiLogManager(logManagers);
}
/**
* Create a MultiLogManager from multiple LogManager instances
* @param logManagers LogManager instances to use
* @returns
*/
static fromLogManagers(...logManagers: LogManager[]) {
return new MultiLogManager(logManagers);
}
}

View file

@ -2,5 +2,5 @@
"name": "log-manager",
"version": "0.0.0",
"main": "index.ts",
"dependencies": { }
}
"dependencies": {}
}

View file

@ -1,117 +1,117 @@
// FILEPATH: /home/jessew/Dev/lysand/packages/log-manager/log-manager.test.ts
import { LogManager, LogLevel, MultiLogManager } from "../index";
import type fs from "fs/promises";
import {
describe,
it,
beforeEach,
expect,
jest,
mock,
type Mock,
test,
type Mock,
beforeEach,
describe,
expect,
it,
jest,
mock,
test,
} from "bun:test";
import type fs from "node:fs/promises";
import type { BunFile } from "bun";
// FILEPATH: /home/jessew/Dev/lysand/packages/log-manager/log-manager.test.ts
import { LogLevel, LogManager, MultiLogManager } from "../index";
describe("LogManager", () => {
let logManager: LogManager;
let mockOutput: BunFile;
let mockAppend: Mock<typeof fs.appendFile>;
let logManager: LogManager;
let mockOutput: BunFile;
let mockAppend: Mock<typeof fs.appendFile>;
beforeEach(async () => {
mockOutput = Bun.file("test.log");
mockAppend = jest.fn();
await mock.module("fs/promises", () => ({
appendFile: mockAppend,
}));
logManager = new LogManager(mockOutput);
});
beforeEach(async () => {
mockOutput = Bun.file("test.log");
mockAppend = jest.fn();
await mock.module("fs/promises", () => ({
appendFile: mockAppend,
}));
logManager = new LogManager(mockOutput);
});
it("should initialize and write init log", () => {
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("--- INIT LogManager at")
);
});
it("should initialize and write init log", () => {
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("--- INIT LogManager at"),
);
});
it("should log message with timestamp", async () => {
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("[INFO] TestEntity: Test message")
);
});
it("should log message with timestamp", async () => {
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("[INFO] TestEntity: Test message"),
);
});
it("should log message without timestamp", async () => {
await logManager.log(
LogLevel.INFO,
"TestEntity",
"Test message",
false
);
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
"[INFO] TestEntity: Test message\n"
);
});
it("should log message without timestamp", async () => {
await logManager.log(
LogLevel.INFO,
"TestEntity",
"Test message",
false,
);
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
"[INFO] TestEntity: Test message\n",
);
});
test.skip("should write to stdout", async () => {
logManager = new LogManager(Bun.stdout);
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
test.skip("should write to stdout", async () => {
logManager = new LogManager(Bun.stdout);
await logManager.log(LogLevel.INFO, "TestEntity", "Test message");
const writeMock = jest.fn();
const writeMock = jest.fn();
await mock.module("Bun", () => ({
stdout: Bun.stdout,
write: writeMock,
}));
await mock.module("Bun", () => ({
stdout: Bun.stdout,
write: writeMock,
}));
expect(writeMock).toHaveBeenCalledWith(
Bun.stdout,
expect.stringContaining("[INFO] TestEntity: Test message")
);
});
expect(writeMock).toHaveBeenCalledWith(
Bun.stdout,
expect.stringContaining("[INFO] TestEntity: Test message"),
);
});
it("should throw error if output file does not exist", () => {
mockAppend.mockImplementationOnce(() => {
return Promise.reject(
new Error("Output file doesnt exist (and isnt stdout)")
);
});
expect(
logManager.log(LogLevel.INFO, "TestEntity", "Test message")
).rejects.toThrow(Error);
});
it("should throw error if output file does not exist", () => {
mockAppend.mockImplementationOnce(() => {
return Promise.reject(
new Error("Output file doesnt exist (and isnt stdout)"),
);
});
expect(
logManager.log(LogLevel.INFO, "TestEntity", "Test message"),
).rejects.toThrow(Error);
});
it("should log error message", async () => {
const error = new Error("Test error");
await logManager.logError(LogLevel.ERROR, "TestEntity", error);
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("[ERROR] TestEntity: Test error")
);
});
it("should log error message", async () => {
const error = new Error("Test error");
await logManager.logError(LogLevel.ERROR, "TestEntity", error);
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("[ERROR] TestEntity: Test error"),
);
});
it("should log basic request details", async () => {
const req = new Request("http://localhost/test", { method: "GET" });
await logManager.logRequest(req, "127.0.0.1");
it("should log basic request details", async () => {
const req = new Request("http://localhost/test", { method: "GET" });
await logManager.logRequest(req, "127.0.0.1");
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("127.0.0.1: GET http://localhost/test")
);
});
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining("127.0.0.1: GET http://localhost/test"),
);
});
describe("Request logger", () => {
it("should log all request details for JSON content type", async () => {
const req = new Request("http://localhost/test", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ test: "value" }),
});
await logManager.logRequest(req, "127.0.0.1", true);
describe("Request logger", () => {
it("should log all request details for JSON content type", async () => {
const req = new Request("http://localhost/test", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ test: "value" }),
});
await logManager.logRequest(req, "127.0.0.1", true);
const expectedLog = `127.0.0.1: POST http://localhost/test
const expectedLog = `127.0.0.1: POST http://localhost/test
[Headers]
content-type: application/json
[Body]
@ -120,112 +120,112 @@ describe("LogManager", () => {
}
`;
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining(expectedLog)
);
});
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining(expectedLog),
);
});
it("should log all request details for text content type", async () => {
const req = new Request("http://localhost/test", {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: "Test body",
});
await logManager.logRequest(req, "127.0.0.1", true);
it("should log all request details for text content type", async () => {
const req = new Request("http://localhost/test", {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: "Test body",
});
await logManager.logRequest(req, "127.0.0.1", true);
const expectedLog = `127.0.0.1: POST http://localhost/test
const expectedLog = `127.0.0.1: POST http://localhost/test
[Headers]
content-type: text/plain
[Body]
Test body
`;
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining(expectedLog)
);
});
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining(expectedLog),
);
});
it("should log all request details for FormData content-type", async () => {
const formData = new FormData();
formData.append("test", "value");
const req = new Request("http://localhost/test", {
method: "POST",
body: formData,
});
await logManager.logRequest(req, "127.0.0.1", true);
it("should log all request details for FormData content-type", async () => {
const formData = new FormData();
formData.append("test", "value");
const req = new Request("http://localhost/test", {
method: "POST",
body: formData,
});
await logManager.logRequest(req, "127.0.0.1", true);
const expectedLog = `127.0.0.1: POST http://localhost/test
const expectedLog = `127.0.0.1: POST http://localhost/test
[Headers]
content-type: multipart/form-data; boundary=${
req.headers.get("Content-Type")?.split("boundary=")[1] ?? ""
}
req.headers.get("Content-Type")?.split("boundary=")[1] ?? ""
}
[Body]
test: value
`;
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining(
expectedLog.replace("----", expect.any(String))
)
);
});
});
expect(mockAppend).toHaveBeenCalledWith(
mockOutput.name,
expect.stringContaining(
expectedLog.replace("----", expect.any(String)),
),
);
});
});
});
describe("MultiLogManager", () => {
let multiLogManager: MultiLogManager;
let mockLogManagers: LogManager[];
let mockLog: jest.Mock;
let mockLogError: jest.Mock;
let mockLogRequest: jest.Mock;
let multiLogManager: MultiLogManager;
let mockLogManagers: LogManager[];
let mockLog: jest.Mock;
let mockLogError: jest.Mock;
let mockLogRequest: jest.Mock;
beforeEach(() => {
mockLog = jest.fn();
mockLogError = jest.fn();
mockLogRequest = jest.fn();
mockLogManagers = [
{
log: mockLog,
logError: mockLogError,
logRequest: mockLogRequest,
},
{
log: mockLog,
logError: mockLogError,
logRequest: mockLogRequest,
},
] as unknown as LogManager[];
multiLogManager = MultiLogManager.fromLogManagers(...mockLogManagers);
});
beforeEach(() => {
mockLog = jest.fn();
mockLogError = jest.fn();
mockLogRequest = jest.fn();
mockLogManagers = [
{
log: mockLog,
logError: mockLogError,
logRequest: mockLogRequest,
},
{
log: mockLog,
logError: mockLogError,
logRequest: mockLogRequest,
},
] as unknown as LogManager[];
multiLogManager = MultiLogManager.fromLogManagers(...mockLogManagers);
});
it("should log message to all logManagers", async () => {
await multiLogManager.log(LogLevel.INFO, "TestEntity", "Test message");
expect(mockLog).toHaveBeenCalledTimes(2);
expect(mockLog).toHaveBeenCalledWith(
LogLevel.INFO,
"TestEntity",
"Test message",
true
);
});
it("should log message to all logManagers", async () => {
await multiLogManager.log(LogLevel.INFO, "TestEntity", "Test message");
expect(mockLog).toHaveBeenCalledTimes(2);
expect(mockLog).toHaveBeenCalledWith(
LogLevel.INFO,
"TestEntity",
"Test message",
true,
);
});
it("should log error to all logManagers", async () => {
const error = new Error("Test error");
await multiLogManager.logError(LogLevel.ERROR, "TestEntity", error);
expect(mockLogError).toHaveBeenCalledTimes(2);
expect(mockLogError).toHaveBeenCalledWith(
LogLevel.ERROR,
"TestEntity",
error
);
});
it("should log error to all logManagers", async () => {
const error = new Error("Test error");
await multiLogManager.logError(LogLevel.ERROR, "TestEntity", error);
expect(mockLogError).toHaveBeenCalledTimes(2);
expect(mockLogError).toHaveBeenCalledWith(
LogLevel.ERROR,
"TestEntity",
error,
);
});
it("should log request to all logManagers", async () => {
const req = new Request("http://localhost/test", { method: "GET" });
await multiLogManager.logRequest(req, "127.0.0.1", true);
expect(mockLogRequest).toHaveBeenCalledTimes(2);
expect(mockLogRequest).toHaveBeenCalledWith(req, "127.0.0.1", true);
});
it("should log request to all logManagers", async () => {
const req = new Request("http://localhost/test", { method: "GET" });
await multiLogManager.logRequest(req, "127.0.0.1", true);
expect(mockLogRequest).toHaveBeenCalledTimes(2);
expect(mockLogRequest).toHaveBeenCalledWith(req, "127.0.0.1", true);
});
});