diff --git a/src/GitHub/branch.ts b/src/GitHub/branch.ts index 6aa754b4..cdac3610 100644 --- a/src/GitHub/branch.ts +++ b/src/GitHub/branch.ts @@ -4,7 +4,7 @@ import i18next from "i18next"; import { Notice } from "obsidian"; import { FilesManagement } from "src/GitHub/files"; import type Enveloppe from "src/main"; -import type { Logs } from "../utils/logs"; +import { EnveloppeErrors, type Logs } from "../utils/logs"; export class GithubBranch extends FilesManagement { console: Logs; @@ -235,52 +235,51 @@ export class GithubBranch extends FilesManagement { async checkRepository(prop: Properties | Properties[], silent = true): Promise { prop = Array.isArray(prop) ? prop : [prop]; for (const repo of prop) { - try { - const repoExist = await this.octokit - .request("GET /repos/{owner}/{repo}", { - owner: repo.owner, - repo: repo.repo, - }) - .catch((e) => { - //check the error code - if (e.status === 404) { - new Notice(i18next.t("commands.checkValidity.inRepo.error404", { repo })); - } else if (e.status === 403) { - new Notice(i18next.t("commands.checkValidity.inRepo.error403", { repo })); - } else if (e.status === 301) { - new Notice(i18next.t("commands.checkValidity.inRepo.error301", { repo })); - } - }); - //@ts-ignore - if (repoExist.status === 200) { - this.console.info( - i18next.t("commands.checkValidity.repoExistsTestBranch", { repo }) - ); - - const branchExist = await this.findMainBranch(repo); - if (!branchExist) { - const errorMsg = new Error( - i18next.t("commands.checkValidity.inBranch.error404", { - repo, - }) + const repoExist = await this.octokit + .request("GET /repos/{owner}/{repo}", { + owner: repo.owner, + repo: repo.repo, + }) + .catch((e) => { + //check the error code + if (e.status === 404) { + new Notice(i18next.t("commands.checkValidity.inRepo.error404", { repo })); + throw new EnveloppeErrors( + i18next.t("commands.checkValidity.inRepo.error404", { repo }) ); - this.console.fatal(errorMsg); - - break; - } - this.console.info(i18next.t("commands.checkValidity.success", { repo })); - if (!silent) { - this.console.noticeSuccess( - i18next.t("commands.checkValidity.success", { repo }) + } else if (e.status === 403) { + new Notice(i18next.t("commands.checkValidity.inRepo.error403", { repo })); + throw new EnveloppeErrors( + i18next.t("commands.checkValidity.inRepo.error403", { repo }) + ); + } else if (e.status === 301) { + new Notice(i18next.t("commands.checkValidity.inRepo.error301", { repo })); + throw new EnveloppeErrors( + i18next.t("commands.checkValidity.inRepo.error301", { repo }) ); } + }); + //@ts-ignore + if (repoExist.status === 200) { + this.console.info( + i18next.t("commands.checkValidity.repoExistsTestBranch", { repo }) + ); + + const branchExist = await this.findMainBranch(repo); + if (!branchExist) { + throw new EnveloppeErrors( + i18next.t("commands.checkValidity.inBranch.error404", { + repo, + }), + { cause: "Branch not found" } + ); + } + this.console.info(i18next.t("commands.checkValidity.success", { repo })); + if (!silent) { + this.console.noticeSuccess( + i18next.t("commands.checkValidity.success", { repo }) + ); } - } catch (e) { - const err = new Error(i18next.t("commands.checkValidity.error")); - err.stack = (e as Error).stack; - this.console.fatal(err); - this.console.info(e); - break; } } } diff --git a/src/GitHub/delete.ts b/src/GitHub/delete.ts index 7df95cb2..5df77fae 100644 --- a/src/GitHub/delete.ts +++ b/src/GitHub/delete.ts @@ -25,7 +25,6 @@ import type { FilesManagement } from "src/GitHub/files"; import { trimObject } from "src/utils"; import { isAttachment, verifyRateLimitAPI } from "src/utils/data_validation_test"; import { frontmatterSettingsRepository } from "src/utils/parse_frontmatter"; -import type Enveloppe from "../main"; /** * Delete file from github, based on a list of file in the original vault @@ -122,34 +121,30 @@ async function deleteFromGithubOneRepo( const isNeedToBeDeleted = isInObsidian ? isMarkdownForAnotherRepo : true; if (isNeedToBeDeleted) { const checkingIndex = file.file.contains(settings.upload.folderNote.rename) - ? await checkIndexFiles(octokit, plugin, file.file, repo) + ? await checkIndexFiles(octokit, file.file, repo) : false; - try { - if (!checkingIndex) { - pconsole.trace( - `trying to delete file : ${file.file} from ${repo.owner}/${repo.repo}` - ); - const response = await octokit.request( - "DELETE /repos/{owner}/{repo}/contents/{path}", - { - owner: repo.owner, - repo: repo.repo, - path: file.file, - message: `DELETE FILE : ${file.file}`, - sha: file.sha, - branch: branchName, - } - ); - if (response.status === 200) { - deletedSuccess++; - result.deleted.push(file.file); - } else { - deletedFailed++; - result.undeleted.push(file.file); + if (!checkingIndex) { + pconsole.trace( + `trying to delete file : ${file.file} from ${repo.owner}/${repo.repo}` + ); + const response = await octokit.request( + "DELETE /repos/{owner}/{repo}/contents/{path}", + { + owner: repo.owner, + repo: repo.repo, + path: file.file, + message: `DELETE FILE : ${file.file}`, + sha: file.sha, + branch: branchName, } + ); + if (response.status === 200) { + deletedSuccess++; + result.deleted.push(file.file); + } else { + deletedFailed++; + result.undeleted.push(file.file); } - } catch (e) { - pconsole.fatal(e as Error); } } } @@ -248,7 +243,6 @@ function parseYamlFrontmatter(contents: string): unknown { * - autoClean: false * - share: false * @param {Octokit} octokit GitHub API - * @param plugin * @param {string} path path of the file to check * @param {Properties} prop repository informations * @return {Promise} true if the file must be deleted @@ -256,40 +250,29 @@ function parseYamlFrontmatter(contents: string): unknown { async function checkIndexFiles( octokit: Octokit, - plugin: Enveloppe, path: string, prop: Properties ): Promise { - try { - const fileRequest = await octokit.request( - "GET /repos/{owner}/{repo}/contents/{path}", - { - owner: prop.owner, - repo: prop.repo, - path, - } + const fileRequest = await octokit.request("GET /repos/{owner}/{repo}/contents/{path}", { + owner: prop.owner, + repo: prop.repo, + path, + }); + if (fileRequest.status === 200) { + // @ts-ignore + const fileContent = Base64.decode(fileRequest.data.content); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const fileFrontmatter = parseYamlFrontmatter(fileContent) as any; + // if not share => don't delete + // Key preventing deletion : + // - index: true + // - delete: false + // return true for NO DELETION + return ( + fileFrontmatter.index === "true" || + fileFrontmatter.delete === "false" || + !fileFrontmatter.share ); - if (fileRequest.status === 200) { - // @ts-ignore - const fileContent = Base64.decode(fileRequest.data.content); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const fileFrontmatter = parseYamlFrontmatter(fileContent) as any; - // if not share => don't delete - // Key preventing deletion : - // - index: true - // - delete: false - // return true for NO DELETION - return ( - fileFrontmatter.index === "true" || - fileFrontmatter.delete === "false" || - !fileFrontmatter.share - ); - } - } catch (e) { - if (!(e instanceof DOMException)) { - plugin.console.fatal(e as Error); - return false; - } } return false; } diff --git a/src/GitHub/upload.ts b/src/GitHub/upload.ts index 46676266..bf46f24c 100644 --- a/src/GitHub/upload.ts +++ b/src/GitHub/upload.ts @@ -45,7 +45,7 @@ import { } from "src/utils/parse_frontmatter"; import { ShareStatusBar } from "src/utils/status_bar"; import { merge } from "ts-deepmerge"; -import type { Logs } from "../utils/logs"; +import { EnveloppeErrors, type Logs } from "../utils/logs"; /** Class to manage the branch * @extends FilesManagement @@ -137,14 +137,14 @@ export default class Publisher { } catch (e) { new Notice(i18next.t("error.unablePublishNote", { file: file.name })); fileError.push(file.name); - this.console.fatal(e as Error); + this.console.error(e as Error); } } statusBar.finish(8000); } catch (e) { - this.console.fatal(e as Error); this.console.noticeErrorUpload(prop); statusBar.error(prop); + throw new EnveloppeErrors((e as Error).message, { cause: e }); } } return { @@ -210,88 +210,77 @@ export default class Publisher { ) { return false; } - try { - this.console.trace(`Publishing file: ${file.path}`); - fileHistory.push(file); - const frontmatterSettingsFromFile = getFrontmatterSettings( - frontmatter, - this.settings, - repo.repository - ); + this.console.trace(`Publishing file: ${file.path}`); + fileHistory.push(file); + const frontmatterSettingsFromFile = getFrontmatterSettings( + frontmatter, + this.settings, + repo.repository + ); - const frontmatterRepository = frontmatterSettingsRepository( - this.plugin, - repo.repository - ); - const frontmatterSettings = merge.withOptions( - { allowUndefinedOverrides: false }, - frontmatterRepository, - frontmatterSettingsFromFile - ); - const multiProperties: MultiProperties = { + const frontmatterRepository = frontmatterSettingsRepository( + this.plugin, + repo.repository + ); + const frontmatterSettings = merge.withOptions( + { allowUndefinedOverrides: false }, + frontmatterRepository, + frontmatterSettingsFromFile + ); + const multiProperties: MultiProperties = { + plugin: this.plugin, + frontmatter: { + general: frontmatterSettings, + prop: repo.frontmatter, + }, + repository: repo.repository, + filepath: filePath, + }; + + let embedFiles = shareFiles.getSharedEmbed(file, frontmatterSettings); + embedFiles = await shareFiles.getMetadataLinks(file, embedFiles, frontmatterSettings); + const linkedFiles = shareFiles.getLinkedByEmbedding(file); + let text = await this.vault.cachedRead(file); + text = await mainConverting(text, file, frontmatter, linkedFiles, multiProperties); + const path = multiProperties.filepath; + const props = Array.isArray(repo.frontmatter) ? repo.frontmatter : [repo.frontmatter]; + let multiRepMsg = ""; + for (const repo of props) { + multiRepMsg += `[${repo.owner}/${repo.repo}/${repo.branch}] `; + } + const msg = `Publishing ${file.name} to ${multiRepMsg}`; + this.console.trace(msg); + const fileDeleted: Deleted[] = []; + const updated: UploadedFiles[][] = []; + const fileError: string[] = []; + for (const repo of props) { + const monoProperties: MonoProperties = { plugin: this.plugin, frontmatter: { general: frontmatterSettings, - prop: repo.frontmatter, + prop: repo, + source: sourceFrontmatter, }, - repository: repo.repository, - filepath: filePath, + repository: multiProperties.repository, + filepath: multiProperties.filepath, }; - - let embedFiles = shareFiles.getSharedEmbed(file, frontmatterSettings); - embedFiles = await shareFiles.getMetadataLinks( + const deleted = await this.uploadOnMultipleRepo( file, + text, + path, embedFiles, - frontmatterSettings + fileHistory, + deepScan, + shareFiles, + autoclean, + monoProperties ); - const linkedFiles = shareFiles.getLinkedByEmbedding(file); - let text = await this.vault.cachedRead(file); - text = await mainConverting(text, file, frontmatter, linkedFiles, multiProperties); - const path = multiProperties.filepath; - const prop = Array.isArray(repo.frontmatter) - ? repo.frontmatter - : [repo.frontmatter]; - let multiRepMsg = ""; - for (const repo of prop) { - multiRepMsg += `[${repo.owner}/${repo.repo}/${repo.branch}] `; - } - const msg = `Publishing ${file.name} to ${multiRepMsg}`; - this.console.trace(msg); - const fileDeleted: Deleted[] = []; - const updated: UploadedFiles[][] = []; - const fileError: string[] = []; - for (const repo of prop) { - const monoProperties: MonoProperties = { - plugin: this.plugin, - frontmatter: { - general: frontmatterSettings, - prop: repo, - source: sourceFrontmatter, - }, - repository: multiProperties.repository, - filepath: multiProperties.filepath, - }; - const deleted = await this.uploadOnMultipleRepo( - file, - text, - path, - embedFiles, - fileHistory, - deepScan, - shareFiles, - autoclean, - monoProperties - ); - fileDeleted.push(deleted.deleted); - // convert to UploadedFiles[] - updated.push(deleted.uploaded); - fileError.push(...deleted.error); - } - return { deleted: fileDeleted[0], uploaded: updated[0], error: fileError }; - } catch (e) { - this.console.fatal(e as Error); - return false; + fileDeleted.push(deleted.deleted); + // convert to UploadedFiles[] + updated.push(deleted.uploaded); + fileError.push(...deleted.error); } + return { deleted: fileDeleted[0], uploaded: updated[0], error: fileError }; } /** diff --git a/src/commands/purge.ts b/src/commands/purge.ts index 57edfe6e..63a6b978 100644 --- a/src/commands/purge.ts +++ b/src/commands/purge.ts @@ -57,26 +57,24 @@ async function purge( branchName: string, monoRepo: MonoRepoProperties ): Promise { - try { - const noticeFragment = document.createDocumentFragment(); - noticeFragment.createSpan({ cls: ["enveloppe", "notification"] }).innerHTML = - i18next.t("informations.startingClean", { repo: monoRepo.frontmatter }); - new Notice(noticeFragment); - const isValid = await checkRepositoryValidityWithProperties( - PublisherManager, - monoRepo.frontmatter - ); - if (!isValid) return false; - if (!PublisherManager.settings.github.dryRun.enable) - await PublisherManager.newBranch(monoRepo.frontmatter); - const deleted = await deleteFromGithub(false, branchName, PublisherManager, monoRepo); - if (!PublisherManager.settings.github.dryRun.enable) - await PublisherManager.updateRepository(monoRepo.frontmatter); - if (PublisherManager.settings.plugin.displayModalRepoEditing) - new ListChangedFiles(PublisherManager.plugin.app, deleted).open(); - } catch (e) { - PublisherManager.plugin.console.fatal(e as Error); - } + const noticeFragment = document.createDocumentFragment(); + noticeFragment.createSpan({ cls: ["enveloppe", "notification"] }).innerHTML = i18next.t( + "informations.startingClean", + { repo: monoRepo.frontmatter } + ); + new Notice(noticeFragment); + const isValid = await checkRepositoryValidityWithProperties( + PublisherManager, + monoRepo.frontmatter + ); + if (!isValid) return false; + if (!PublisherManager.settings.github.dryRun.enable) + await PublisherManager.newBranch(monoRepo.frontmatter); + const deleted = await deleteFromGithub(false, branchName, PublisherManager, monoRepo); + if (!PublisherManager.settings.github.dryRun.enable) + await PublisherManager.updateRepository(monoRepo.frontmatter); + if (PublisherManager.settings.plugin.displayModalRepoEditing) + new ListChangedFiles(PublisherManager.plugin.app, deleted).open(); } /** diff --git a/src/commands/share/unique_note.ts b/src/commands/share/unique_note.ts index 268947fe..792cb13b 100644 --- a/src/commands/share/unique_note.ts +++ b/src/commands/share/unique_note.ts @@ -79,70 +79,56 @@ export async function shareOneNote( sourceFrontmatter, PublisherManager.settings.plugin.shareKey ); - try { - const prop = getProperties(plugin, repository, frontmatter); - let isValid: boolean; - if (prop instanceof Array) { - const isValidArray = []; - for (const repo of prop) { - isValidArray.push( - await checkRepositoryValidityWithProperties(PublisherManager, repo) - ); - } - isValid = isValidArray.every((v) => v); - } else isValid = await checkRepositoryValidityWithProperties(PublisherManager, prop); + const prop = getProperties(plugin, repository, frontmatter); + let isValid: boolean; + if (prop instanceof Array) { + const isValidArray = []; + for (const repo of prop) { + isValidArray.push( + await checkRepositoryValidityWithProperties(PublisherManager, repo) + ); + } + isValid = isValidArray.every((v) => v); + } else isValid = await checkRepositoryValidityWithProperties(PublisherManager, prop); - const multiRepo: MultiRepoProperties = { - frontmatter: prop, - repository, - }; - if (!isValid) return false; - if (!settings.github.dryRun.enable) await PublisherManager.newBranch(prop); - const publishSuccess = await PublisherManager.publish( - file, - true, - multiRepo, - [], - true, - sourceFrontmatter - ); - if (publishSuccess) { - if (settings.upload.metadataExtractorPath.length > 0 && Platform.isDesktop) { - const metadataExtractor = await getSettingsOfMetadataExtractor(app, settings); - if (metadataExtractor) { - await PublisherManager.uploadMetadataExtractorFiles(metadataExtractor, prop); - } + const multiRepo: MultiRepoProperties = { + frontmatter: prop, + repository, + }; + if (!isValid) return false; + if (!settings.github.dryRun.enable) await PublisherManager.newBranch(prop); + const publishSuccess = await PublisherManager.publish( + file, + true, + multiRepo, + [], + true, + sourceFrontmatter + ); + if (publishSuccess) { + if (settings.upload.metadataExtractorPath.length > 0 && Platform.isDesktop) { + const metadataExtractor = await getSettingsOfMetadataExtractor(app, settings); + if (metadataExtractor) { + await PublisherManager.uploadMetadataExtractorFiles(metadataExtractor, prop); } - const update = await PublisherManager.updateRepository( - prop, - settings.github.dryRun.enable - ); - if (update) { - await plugin.console.publisherNotification( - PublisherManager, - title, - settings, - prop + } + const update = await PublisherManager.updateRepository( + prop, + settings.github.dryRun.enable + ); + if (update) { + await plugin.console.publisherNotification(PublisherManager, title, settings, prop); + await createLink(file, multiRepo, plugin); + if (settings.plugin.displayModalRepoEditing) { + const listEdited = createListEdited( + publishSuccess.uploaded, + publishSuccess.deleted, + publishSuccess.error ); - await createLink(file, multiRepo, plugin); - if (settings.plugin.displayModalRepoEditing) { - const listEdited = createListEdited( - publishSuccess.uploaded, - publishSuccess.deleted, - publishSuccess.error - ); - new ListChangedFiles(app, listEdited).open(); - } - } else { - plugin.console.noticeErrorUpload(prop); + new ListChangedFiles(app, listEdited).open(); } - } - } catch (error) { - if (!(error instanceof DOMException)) { - plugin.console.fatal(error as Error); - plugin.console.noticeErrorUpload( - getProperties(plugin, repository, frontmatter, true) - ); + } else { + plugin.console.noticeErrorUpload(prop); } } } diff --git a/src/main.ts b/src/main.ts index 12467c7a..2258aa3b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -215,7 +215,7 @@ export default class Enveloppe extends Plugin { returnNull: false, returnEmptyString: false, }); - + await this.app.vault.adapter.remove(normalizePath(`${this.manifest.dir}/logs.txt`)); await this.loadSettings(); this.console = new Logs(this); this.console.info( @@ -333,7 +333,13 @@ export default class Enveloppe extends Plugin { } this.addCommand(refreshOpenedSet(this)); this.addCommand(refreshAllSets(this)); - this.app.vault.adapter.removeFile(normalizePath(`${this.manifest.dir}/logs.txt`)); + window.addEventListener("unhandledrejection", (e) => { + if (e.reason.stack.includes(this.manifest.id)) + this.console.writeToLog(e.reason, "fatal"); + }); + window.addEventListener("error", (e) => { + if (e.error.stack.includes(this.manifest.id)) this.console.writeToLog(e, "error"); + }); } /** diff --git a/src/settings/modals/token_path.ts b/src/settings/modals/token_path.ts index 3d79469d..2443653f 100644 --- a/src/settings/modals/token_path.ts +++ b/src/settings/modals/token_path.ts @@ -4,7 +4,7 @@ import { type App, Modal, Notice, Setting } from "obsidian"; import type Enveloppe from "src/main"; import { migrateToken } from "src/settings/migrate"; import { createTokenPath } from "src/utils"; -import type { Logs } from "../../utils/logs"; +import { EnveloppeErrors, type Logs } from "../../utils/logs"; export class TokenEditPath extends Modal { plugin: Enveloppe; @@ -97,7 +97,7 @@ export class TokenEditPath extends Modal { input.controlEl.querySelector("input")!.style.border = "1px solid red"; new Notice(i18next.t("error.reading-token-file")); this.tokenPath = "error"; - this.console.fatal(e as Error); + throw new EnveloppeErrors((e as Error).message, { cause: e }); } }); }); diff --git a/src/utils/logs.ts b/src/utils/logs.ts index 4f2d9d0d..376ab6d2 100644 --- a/src/utils/logs.ts +++ b/src/utils/logs.ts @@ -1,30 +1,53 @@ import type { EnveloppeSettings, Properties } from "@interfaces"; import i18next from "i18next"; import { Notice, Platform, TFile, sanitizeHTMLToDom, setIcon } from "obsidian"; -import { type ILogObj, Logger } from "tslog"; import type Publisher from "../GitHub/upload"; import type Enveloppe from "../main"; export class Logs { plugin: Enveloppe; - logger: Logger; constructor(plugin: Enveloppe) { this.plugin = plugin; - const minLevel = plugin.settings.plugin?.dev ? 0 : 2; - this.logger = new Logger({ - name: "Enveloppe", - minLevel, - prettyLogTemplate: "{{logLevelName}} ", - prettyErrorTemplate: - "\n{{errorName}} {{errorMessage}}\nerror stack:\n{{errorStack}}", - prettyErrorStackTemplate: " • {{fileName}}\t{{method}}\n\t{{filePathWithLine}}", - type: "hidden", - }); + } + + private errorToMessage(error: unknown) { + if (error instanceof Object) { + if (error instanceof Error && error.message) { + return error.message; + } + const parsedError = JSON.parse(JSON.stringify(error)); + if (Array.isArray(parsedError)) { + return parsedError.join(", "); + } + return JSON.stringify(parsedError); + } + if (typeof error === "string") { + return error; + } + return JSON.stringify(error); + } + + writeToLog( + error: unknown, + type: "silly" | "debug" | "info" | "warn" | "error" | "fatal" + ) { if (this.plugin.settings.plugin?.dev) { const logFile = `${this.plugin.manifest.dir}/logs.txt`; - this.logger.attachTransport(async (logObj) => { - await this.plugin.app.vault.adapter.append(logFile, JSON.stringify(logObj)); - }); + const err = error as Error; + const errMessage = this.errorToMessage(error); + const stack = err.stack + ? `\t\t${err.stack.replace(errMessage, "").trim().replaceAll("Error: \n", "").trim().replaceAll("\n", "\n\t")}\n\n` + : "\n"; + const header = `${new Date().toLocaleString()} [${type}]: \n\t${errMessage.replaceAll("\n", "\n\t")}\n${stack}`; + const exists = this.plugin.app.vault.adapter.exists(logFile); + if (!exists) + this.plugin.app.vault.adapter + .write(logFile, header) + .catch((e) => console.error(e)); + else + this.plugin.app.vault.adapter + .append(logFile, header) + .catch((e) => console.error(e)); } } @@ -38,44 +61,44 @@ export class Logs { error(error: Error) { console.error(error); - this.logger.error(error); this.noticeError(error.message); + this.writeToLog(error, "error"); } warn(...messages: unknown[]) { console.warn(...messages); - this.logger.warn(...messages); this.notif(messages); + this.writeToLog(messages, "warn"); } info(...messages: unknown[]) { console.info(...messages); - this.logger.info(...messages); this.notif(messages); + this.writeToLog(messages, "info"); } debug(...messages: unknown[]) { - if (this.plugin.settings.plugin?.dev) console.debug(...messages); - this.logger.debug(...messages); - this.notif(messages); + if (this.plugin.settings.plugin?.dev) { + console.debug(...messages); + this.notif(messages); + this.writeToLog(messages, "debug"); + } } trace(...messages: unknown[]) { - if (this.plugin.settings.plugin?.dev) console.trace(...messages); - this.logger.trace(...messages); - this.notif(messages); + if (this.plugin.settings.plugin?.dev) { + console.trace(...messages); + this.notif(messages); + this.writeToLog(messages, "debug"); + } } silly(...messages: unknown[]) { - if (this.plugin.settings.plugin?.dev) console.log(...messages); - this.logger.silly(...messages); - this.notif(messages); - } - - fatal(error: Error) { - console.error(error); - this.logger.fatal(error); - this.noticeError(error.message); + if (this.plugin.settings.plugin?.dev) { + console.log(...messages); + this.notif(messages); + this.writeToLog(messages, "silly"); + } } /** @@ -225,3 +248,17 @@ export class Logs { } } } + +export class EnveloppeErrors extends Error { + constructor( + message: string, + options?: { name?: string; cause?: unknown; stack?: string } + ) { + super(); + this.message = message; + const { name, cause, stack } = options || {}; + if (name) this.name = name; + if (cause) this.cause = cause; + if (stack) this.stack = stack; + } +}