Errores por pantalla + logs en archivo
This commit is contained in:
@@ -11,12 +11,14 @@ export default defineConfig({
|
||||
"@renderer": resolve("src/renderer/src"),
|
||||
},
|
||||
},
|
||||
plugins: [vue({
|
||||
plugins: [
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag === "nfc-reader",
|
||||
isCustomElement: (tag) => tag === "nfc-reader",
|
||||
},
|
||||
},
|
||||
})],
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 53 KiB |
4
src/config/base.json
Normal file
4
src/config/base.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"defultServer": "http://localhost:3000",
|
||||
"defaultPrinter": "192.168.1.254"
|
||||
}
|
||||
74
src/main/LogService.ts
Normal file
74
src/main/LogService.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { app } from "electron";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
export class LogService {
|
||||
private static instance: LogService;
|
||||
private logPath: string;
|
||||
|
||||
private constructor() {
|
||||
// Create logs directory in userData
|
||||
const userDataPath = app.getPath("userData");
|
||||
const logsDir = path.join(userDataPath, "logs");
|
||||
|
||||
if (!fs.existsSync(logsDir)) {
|
||||
fs.mkdirSync(logsDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Monthly log file
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||
const monthString = `${year}-${month}`; // YYYY-MM
|
||||
this.logPath = path.join(logsDir, `app_${monthString}.log`);
|
||||
|
||||
this.info(`--- Log initialized at ${now.toISOString()} ---`);
|
||||
console.log(`Log file: ${this.logPath}`);
|
||||
}
|
||||
/**
|
||||
* Garantizar que sea singleton
|
||||
* @returns
|
||||
*/
|
||||
public static getInstance(): LogService {
|
||||
if (!LogService.instance) {
|
||||
LogService.instance = new LogService();
|
||||
}
|
||||
return LogService.instance;
|
||||
}
|
||||
|
||||
public info(message: string): void {
|
||||
this.write("INFO", message);
|
||||
}
|
||||
|
||||
public error(message: string, error?: unknown): void {
|
||||
const errorDetail = error ? ` | Details: ${JSON.stringify(error)}` : "";
|
||||
this.write("ERROR", `${message}${errorDetail}`);
|
||||
}
|
||||
|
||||
public logOperation(
|
||||
operation: string,
|
||||
data: unknown,
|
||||
result?: unknown,
|
||||
): void {
|
||||
const entry = {
|
||||
operation,
|
||||
data,
|
||||
result,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
this.write("OP", JSON.stringify(entry));
|
||||
}
|
||||
|
||||
private write(level: string, message: string): void {
|
||||
const timestamp = new Date().toISOString();
|
||||
const logEntry = `[${timestamp}] [${level}] ${message}\n`;
|
||||
|
||||
try {
|
||||
fs.appendFileSync(this.logPath, logEntry, "utf8");
|
||||
} catch (err) {
|
||||
console.error("Failed to write to log file:", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const logger = LogService.getInstance();
|
||||
115
src/main/handlers.ts
Normal file
115
src/main/handlers.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { IpcMainInvokeEvent } from "electron";
|
||||
import { Result } from "../types/Result";
|
||||
import { CodeRequest, CodeResponse, PrinterRequest } from "../preload/index.d";
|
||||
import { createConnection } from "net";
|
||||
import { logger } from "./LogService";
|
||||
|
||||
/**
|
||||
* LLamada al servidor para obtener la etiqueta
|
||||
* @param event --
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export async function labelReqHandler(
|
||||
event: IpcMainInvokeEvent,
|
||||
data: CodeRequest,
|
||||
): Promise<Result<string, CodeResponse>> {
|
||||
logger.logOperation("labelReq", data);
|
||||
console.log("nfc:labelReq", data);
|
||||
try {
|
||||
const endpoint = "/nfc/generate";
|
||||
const url = data.serverURL + endpoint;
|
||||
console.log("fullUrl = ", url);
|
||||
const response = await fetch(data.serverURL + endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
card_id: data.card_id,
|
||||
override: data.override,
|
||||
}),
|
||||
});
|
||||
console.log("Response", response.status);
|
||||
const body: CodeResponse = await response.json();
|
||||
console.log("Codigos:", body);
|
||||
logger.info(`labelReq success: ${JSON.stringify(body)}`);
|
||||
// body puede tener otro error dentro
|
||||
return <Result<string, CodeResponse>>{
|
||||
data: body,
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const err = error as any;
|
||||
logger.error("labelReq error", err);
|
||||
console.error("IPC ERROR", err);
|
||||
let errorStr = "";
|
||||
if (String(err.cause).includes("ECONNRESET")) {
|
||||
errorStr = "Conexión perdida con el servidor";
|
||||
} else {
|
||||
errorStr = "Error de red";
|
||||
}
|
||||
return <Result<string, CodeResponse>>{
|
||||
error: errorStr,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Llamada principa a la impresora
|
||||
* TODO:
|
||||
* - Mejora de control de errores
|
||||
*/
|
||||
export async function printReqHandler(
|
||||
_event: IpcMainInvokeEvent,
|
||||
data: PrinterRequest,
|
||||
): Promise<string> {
|
||||
logger.logOperation("printReq", data);
|
||||
return new Promise((res, rej) => {
|
||||
console.log("nfc:printReq", data);
|
||||
const port = 9100;
|
||||
let finished = false;
|
||||
|
||||
const socket = createConnection(port, data.printerURL);
|
||||
socket.setTimeout(3 * 1000);
|
||||
|
||||
socket.on("connect", (e) => {
|
||||
console.log("Conectado!", e);
|
||||
logger.info("Printer connected, sending label...");
|
||||
socket.write(data.label, "utf-8", () => {
|
||||
socket.end();
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("close", () => {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
logger.info("Print job sent successfully");
|
||||
console.log("Print job sent and connection closed.");
|
||||
res("ok");
|
||||
});
|
||||
|
||||
socket.on("timeout", (err) => {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
logger.error("Print timeout", err);
|
||||
console.error("Timeout", err);
|
||||
socket.destroy();
|
||||
rej(new Error("timeout"));
|
||||
});
|
||||
|
||||
socket.on("error", (err) => {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
logger.error("Printer connection/transmission error", err);
|
||||
let errorStr = "";
|
||||
if (err.message.includes("ETIMEDOUT")) {
|
||||
errorStr = "Error de conexión con la impresora";
|
||||
rej(new Error(errorStr));
|
||||
}
|
||||
console.error("Printer Error:", err.message);
|
||||
socket.destroy();
|
||||
rej(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -3,7 +3,9 @@ import { join } from "path";
|
||||
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
||||
import icon from "../../resources/icon.png?asset";
|
||||
import { NfcService } from "../services/NfcService";
|
||||
import net, { createConnection } from "net";
|
||||
import { labelReqHandler, printReqHandler } from "./handlers";
|
||||
import { logger } from "./LogService";
|
||||
|
||||
function createWindow(): void {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
@@ -18,12 +20,16 @@ function createWindow(): void {
|
||||
},
|
||||
});
|
||||
|
||||
new NfcService((event) => {
|
||||
const nfcService = new NfcService((event) => {
|
||||
if (!mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send(`nfc:${event.type}`, event);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("nfc:getReaderName", () => {
|
||||
return nfcService.getReaderName();
|
||||
});
|
||||
|
||||
mainWindow.on("ready-to-show", () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
@@ -60,64 +66,13 @@ app.whenReady().then(() => {
|
||||
ipcMain.on("ping", () => console.log("pong"));
|
||||
|
||||
// HANDLE es bidireccionar ON es unidireccional
|
||||
ipcMain.handle("nfc:labelReq", async (_event, data) => {
|
||||
console.log("nfc:labelReq", data);
|
||||
try {
|
||||
const endpoint = "/nfc/generate";
|
||||
const url = data.serverURL + endpoint;
|
||||
console.log("fullUrl = ", url);
|
||||
const response = await fetch(data.serverURL + endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
card_id: data.card_id,
|
||||
override: data.override,
|
||||
}),
|
||||
});
|
||||
console.log("Response", response.status);
|
||||
const body = await response.json();
|
||||
console.log("Codigos:", body);
|
||||
return body;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return false;
|
||||
}
|
||||
return data;
|
||||
});
|
||||
/**
|
||||
* LLamada principal
|
||||
* Cli - card_id -> Server - etiqueta/activacion -> Cli
|
||||
*/
|
||||
ipcMain.handle("nfc:labelReq", labelReqHandler);
|
||||
|
||||
ipcMain.handle("nfc:printReq", async (_event, data) => {
|
||||
console.log("nfc:printReq", data);
|
||||
const client = new net.Socket();
|
||||
const port = 9100;
|
||||
const host = data.printerURL + port;
|
||||
|
||||
const socket = createConnection(port, data.printerURL);
|
||||
socket.setTimeout(10 * 1000);
|
||||
socket.on("connect", (e) => {
|
||||
console.log("Conectado!", e);
|
||||
socket.write(data.label);
|
||||
socket.end();
|
||||
});
|
||||
/*
|
||||
client.connect(data.printerURL + port, () => {
|
||||
console.log("Connected to ZSim Printer");
|
||||
const res = client.write(data.label, (res) => {
|
||||
console.log("resultado de la impresion", res);
|
||||
});
|
||||
|
||||
console.log("Resultado", res);
|
||||
});
|
||||
*/
|
||||
socket.on("close", () =>
|
||||
console.log("Print job sent and connection closed."),
|
||||
);
|
||||
socket.on("error", (err) =>
|
||||
console.error("Printer Error:", err.message),
|
||||
);
|
||||
return data;
|
||||
});
|
||||
ipcMain.handle("nfc:printReq", printReqHandler);
|
||||
|
||||
ipcMain.handle("ping:url", async (_event, url: string) => {
|
||||
try {
|
||||
|
||||
10
src/preload/index.d.ts
vendored
10
src/preload/index.d.ts
vendored
@@ -1,4 +1,5 @@
|
||||
import { ElectronAPI } from "@electron-toolkit/preload";
|
||||
import { Result } from "../types/Result.js";
|
||||
|
||||
export type CodeResponse = {
|
||||
error?: string;
|
||||
@@ -13,6 +14,7 @@ export type CodeResponse = {
|
||||
export type CodeRequest = {
|
||||
card_id: string; // UUIDv7
|
||||
override?: boolean;
|
||||
serverURL: string;
|
||||
};
|
||||
|
||||
export type PrinterRequest = {
|
||||
@@ -23,16 +25,14 @@ export type PrinterRequest = {
|
||||
export type PrinterResponse = unknown;
|
||||
|
||||
export interface NfcAPI {
|
||||
getReaderName: () => Promise<string>;
|
||||
onReader: (callback: (event: { name: string }) => void) => void;
|
||||
onTag: (callback: (event: { uid: string }) => void) => void;
|
||||
onRemoved: (callback: (event: { uid: string }) => void) => void;
|
||||
onError: (callback: (event: { message: string }) => void) => void;
|
||||
ping: (url: string) => Promise<boolean>;
|
||||
removeAllListeners: () => void;
|
||||
labelReq: (args: {
|
||||
serverURL: string;
|
||||
card_id: string;
|
||||
override?: boolean;
|
||||
}) => Promise<CodeResponse>;
|
||||
labelReq: (args: CodeRequest) => Promise<Result<string, CodeResponse>>;
|
||||
printReq: (args: {
|
||||
printerURL: string;
|
||||
label: string;
|
||||
|
||||
@@ -5,6 +5,12 @@ import { CodeRequest, PrinterRequest } from "./index.d.js";
|
||||
// Custom APIs for renderer
|
||||
const api = {
|
||||
nfc: {
|
||||
getReaderName: (): Promise<string> => {
|
||||
return ipcRenderer.invoke("nfc:getReaderName");
|
||||
},
|
||||
onReader: (callback: (event: { name: string }) => void): void => {
|
||||
ipcRenderer.on("nfc:reader", (_event, value) => callback(value));
|
||||
},
|
||||
onTag: (callback: (event: { uid: string }) => void): void => {
|
||||
ipcRenderer.on("nfc:tag", (_event, value) => callback(value));
|
||||
},
|
||||
@@ -24,6 +30,7 @@ const api = {
|
||||
return ipcRenderer.invoke("ping:url", url);
|
||||
},
|
||||
removeAllListeners: (): void => {
|
||||
ipcRenderer.removeAllListeners("nfc:reader");
|
||||
ipcRenderer.removeAllListeners("nfc:tag");
|
||||
ipcRenderer.removeAllListeners("nfc:removed");
|
||||
ipcRenderer.removeAllListeners("nfc:error");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Electron</title>
|
||||
<title>Lector NFC</title>
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
|
||||
@@ -30,3 +30,8 @@ body {
|
||||
);
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { type CodeResponse } from "src/preload/index.d.js";
|
||||
import { tryCatch } from "../../../types/Result.js";
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
/**
|
||||
* waiting = esperando a leer una tarjeta (igual es mejor ready)
|
||||
@@ -10,10 +11,18 @@ import { ref, onMounted, onUnmounted } from "vue";
|
||||
const readerState = ref<"waiting" | "reading" | "success" | "error">("waiting");
|
||||
const uid = ref<string | null>(null);
|
||||
const errorMsg = ref<string | null>(null);
|
||||
const labelOutput = ref<string>("");
|
||||
const labelOutputStructured = ref<CodeResponse | null>(null);
|
||||
const readerName = ref<string>("OFFLINE");
|
||||
const errors = ref<string[]>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
// Get initial reader name
|
||||
readerName.value = await window.api.nfc.getReaderName();
|
||||
|
||||
window.api.nfc.onReader((event) => {
|
||||
readerName.value = event.name;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
window.api.nfc.onTag((event) => {
|
||||
uid.value = event.uid;
|
||||
readerState.value = "success";
|
||||
@@ -64,43 +73,62 @@ const mockReadCard = (): void => {
|
||||
|
||||
|
||||
const readCard = async (): Promise<void> => {
|
||||
console.log("test readcard");
|
||||
// Reset de los errores anteriores
|
||||
errors.value = [];
|
||||
|
||||
if (readerState.value !== "waiting") return;
|
||||
readerState.value = "reading";
|
||||
|
||||
const serverURL =
|
||||
localStorage.getItem("serverUrl") || "http://localhost:3000";
|
||||
const printerURL =
|
||||
localStorage.getItem("printerUrl") || "192.168.1.254";
|
||||
const printerURL = localStorage.getItem("printerUrl") || "192.168.1.254";
|
||||
|
||||
// TODO: De momento está hardcodeado porque no se puede leer la tarjeta
|
||||
const card_id = "019cdd39-fc08-7417-b16d-a78794a24c01";
|
||||
const override = false;
|
||||
|
||||
try {
|
||||
const res: CodeResponse = await window.api.nfc.labelReq({
|
||||
const codePromise = window.api.nfc.labelReq({
|
||||
serverURL,
|
||||
card_id,
|
||||
override,
|
||||
});
|
||||
console.log("Res IPC", res);
|
||||
labelOutput.value = res.data.label;
|
||||
labelOutputStructured.value = res;
|
||||
console.log("Imprimiendo", res);
|
||||
|
||||
const impRes = await window.api.nfc.printReq({
|
||||
const res = await tryCatch(codePromise);
|
||||
console.log("RES IPC:", res);
|
||||
|
||||
// Error de la llamada -> sin conexion al servidor
|
||||
if (res.error != undefined) {
|
||||
errors.value = [...errors.value, String(res.error)];
|
||||
delayWaiting();
|
||||
return;
|
||||
}
|
||||
// Error que viene del servidor
|
||||
if (res.data?.error != undefined) {
|
||||
errors.value = [...errors.value, String(res.data.error)];
|
||||
delayWaiting();
|
||||
return;
|
||||
}
|
||||
const labelData = res.data.data.data;
|
||||
labelOutputStructured.value = res.data.data;
|
||||
console.log("label data", labelData);
|
||||
|
||||
const impPromise = window.api.nfc.printReq({
|
||||
printerURL,
|
||||
label: res.data.label,
|
||||
label: labelData.label,
|
||||
});
|
||||
|
||||
console.log("Impreso", impRes);
|
||||
|
||||
readerState.value = "waiting";
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
readerState.value = "error";
|
||||
const resPrint = await tryCatch(impPromise);
|
||||
console.log("print", resPrint);
|
||||
if (resPrint.error != undefined) {
|
||||
let errorStr = "";
|
||||
if (resPrint.error.message.includes("timeout")) {
|
||||
errorStr = "Error de conexión con la impresora";
|
||||
}
|
||||
errors.value = [...errors.value, errorStr];
|
||||
delayWaiting();
|
||||
}
|
||||
|
||||
delayWaiting();
|
||||
};
|
||||
|
||||
const delayWaiting = (): void => {
|
||||
@@ -125,8 +153,8 @@ const triggerError = (): void => {
|
||||
|
||||
<div class="top-meta">
|
||||
<div class="meta-item">
|
||||
<span class="label">MODULE</span>
|
||||
<span class="value">NFC_RD_01</span>
|
||||
<span class="label">LECTOR</span>
|
||||
<span class="value">{{ readerName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -134,7 +162,7 @@ const triggerError = (): void => {
|
||||
<div v-if="readerState === 'waiting'" class="waiting-state">
|
||||
<div class="pulse-ring"></div>
|
||||
<div class="pulse-inner"></div>
|
||||
<h1 class="status-msg">TAP CARD TO INITIALIZE</h1>
|
||||
<h1 class="status-msg">ACERCA UN NFC PARA EMPEZAR</h1>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -171,7 +199,22 @@ const triggerError = (): void => {
|
||||
</div>
|
||||
<!-- Salida por pantalla de las respuestas del servidor y la impresora -->
|
||||
<div class="display-container bg-dots">
|
||||
<!-- ERRORES -->
|
||||
<div class="error">
|
||||
<span v-for="(error, idx) in errors" :key="idx">
|
||||
<span>Error: {{ error }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="labelOutputStructured != undefined" class="flex-col">
|
||||
<!-- Errores -->
|
||||
|
||||
<span
|
||||
v-if="labelOutputStructured.error != undefined"
|
||||
class="error"
|
||||
>
|
||||
{{ JSON.stringify(labelOutputStructured.error) }}
|
||||
</span>
|
||||
<!-- Datos -->
|
||||
<span> Código {{ labelOutputStructured.data.code }} </span>
|
||||
<span>
|
||||
Etiqueta {{ labelOutputStructured.data.label }}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import PCSC, { Tag, Reader } from "@tockawa/nfc-pcsc";
|
||||
import { logger } from "../main/LogService";
|
||||
|
||||
export type NfcEvent =
|
||||
| { type: "tag"; uid: string }
|
||||
| { type: "removed"; uid: string }
|
||||
| { type: "reader"; name: string }
|
||||
| { type: "error"; message: string };
|
||||
|
||||
export class NfcService {
|
||||
private nfc: PCSC;
|
||||
private onEvent?: (event: NfcEvent) => void;
|
||||
private currentReaderName: string = "OFFLINE";
|
||||
|
||||
constructor(onEvent?: (event: NfcEvent) => void) {
|
||||
this.nfc = new PCSC.default();
|
||||
@@ -15,12 +18,22 @@ export class NfcService {
|
||||
this.init();
|
||||
}
|
||||
|
||||
public getReaderName(): string {
|
||||
return this.currentReaderName;
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
this.nfc.on("reader", (reader: Reader) => {
|
||||
console.log(`Lector detectado: ${reader.name}`);
|
||||
logger.info(`NFC Reader detected: ${reader.name}`);
|
||||
this.currentReaderName = reader.name;
|
||||
if (this.onEvent) {
|
||||
this.onEvent({ type: "reader", name: reader.name });
|
||||
}
|
||||
|
||||
reader.on("card", async (card: Tag) => {
|
||||
console.log(`Tarjeta detectada! UID: ${card.uid}`);
|
||||
logger.info(`NFC Tag detected: ${card.uid}`);
|
||||
if (this.onEvent) {
|
||||
this.onEvent({ type: "tag", uid: card.uid });
|
||||
}
|
||||
@@ -28,6 +41,7 @@ export class NfcService {
|
||||
|
||||
reader.on("card.off", async (card: Tag) => {
|
||||
console.log(`Tarjeta retirada: ${card.uid}`);
|
||||
logger.info(`NFC Tag removed: ${card.uid}`);
|
||||
if (this.onEvent) {
|
||||
this.onEvent({ type: "removed", uid: card.uid });
|
||||
}
|
||||
@@ -45,6 +59,11 @@ export class NfcService {
|
||||
|
||||
reader.on("end", () => {
|
||||
console.log(`Lector desconectado: ${reader.name}`);
|
||||
logger.info(`NFC Reader disconnected: ${reader.name}`);
|
||||
this.currentReaderName = "OFFLINE";
|
||||
if (this.onEvent) {
|
||||
this.onEvent({ type: "reader", name: "OFFLINE" });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
27
src/types/Result.ts
Normal file
27
src/types/Result.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export type Success<D> = {
|
||||
error?: undefined | null;
|
||||
data: D;
|
||||
};
|
||||
|
||||
export type Failure<E = Error> = {
|
||||
data?: undefined | null;
|
||||
error: E;
|
||||
};
|
||||
|
||||
/**
|
||||
* Result<Error,Data>
|
||||
*/
|
||||
export type Result<E, D> = Failure<E> | Success<D>;
|
||||
|
||||
export async function tryCatch<T>(func: Promise<T>): Promise<Result<Error, T>> {
|
||||
try {
|
||||
const res = await func;
|
||||
return {
|
||||
data: res,
|
||||
};
|
||||
} catch (e: unknown) {
|
||||
return {
|
||||
error: e as Error,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||
"include": [
|
||||
"src/config/**/*",
|
||||
"src/types/**/*",
|
||||
"electron.vite.config.*",
|
||||
"src/main/**/*",
|
||||
"src/preload/**/*",
|
||||
|
||||
Reference in New Issue
Block a user