import { join } from 'node:path'; import semver from 'semver'; import { pathExistsSync } from 'fs-extra'; import { DEFAULT_SERVICE_SETTINGS } from '../config'; import { ifUndefined } from '../jsUtils'; interface RecipeData { id: string; name: string; version: string; aliases?: string[]; path: string; config: { serviceURL?: string; hasDirectMessages?: boolean; hasIndirectMessages?: boolean; hasNotificationSound?: boolean; hasTeamId?: boolean; hasCustomUrl?: boolean; hasHostedOption?: boolean; urlInputPrefix?: string; urlInputSuffix?: string; disablewebsecurity?: boolean; partition?: string; local?: boolean; message?: string; allowFavoritesDelineationInUnreadCount?: boolean; }; } export interface IRecipe { id: string; name: string; description: string; version: string; aliases: string[]; serviceURL: string; hasDirectMessages: boolean; hasIndirectMessages: boolean; hasNotificationSound: boolean; hasTeamId: boolean; hasCustomUrl: boolean; hasHostedOption: boolean; urlInputPrefix: string; urlInputSuffix: string; message: string; allowFavoritesDelineationInUnreadCount: boolean; disablewebsecurity: boolean; path: string; partition: string; local: boolean; // eslint-disable-next-line @typescript-eslint/ban-types readonly overrideUserAgent?: null | Function; // eslint-disable-next-line @typescript-eslint/ban-types readonly buildUrl?: null | Function; // eslint-disable-next-line @typescript-eslint/ban-types readonly modifyRequestHeaders?: null | Function; // eslint-disable-next-line @typescript-eslint/ban-types readonly knownCertificateHosts?: null | Function; readonly events?: null | ((key: string) => string); // TODO: [TS DEBT] Need to check if below properties are needed and where is inherited / implemented from author?: string[]; hasDarkMode?: boolean; validateUrl?: (url: string) => boolean; icons?: any; } export default class Recipe implements IRecipe { id = ''; name = ''; description = ''; version = ''; // Removing this specific type will cause a typescript error // even while it's the exact same as the interface aliases: string[] = []; serviceURL = ''; hasDirectMessages = DEFAULT_SERVICE_SETTINGS.hasDirectMessages; hasIndirectMessages = DEFAULT_SERVICE_SETTINGS.hasIndirectMessages; hasNotificationSound = DEFAULT_SERVICE_SETTINGS.hasNotificationSound; hasTeamId = DEFAULT_SERVICE_SETTINGS.hasTeamId; hasCustomUrl = DEFAULT_SERVICE_SETTINGS.hasCustomUrl; hasHostedOption = DEFAULT_SERVICE_SETTINGS.hasHostedOption; urlInputPrefix = ''; urlInputSuffix = ''; message = ''; allowFavoritesDelineationInUnreadCount = DEFAULT_SERVICE_SETTINGS.allowFavoritesDelineationInUnreadCount; disablewebsecurity = DEFAULT_SERVICE_SETTINGS.disablewebsecurity; path = ''; partition = ''; // TODO: Is this being used? local = false; // TODO: [TS DEBT] introduced to address missing function but need to check how validateUrl is inherited / implemented in recipe validateUrl?: (url: string) => boolean; // TODO: Need to reconcile which of these are optional/mandatory constructor(data: RecipeData) { if (!data) { throw new Error('Recipe config not valid'); } if (!data.id) { // Ferdium 4 recipes do not have an Id throw new Error(`Recipe '${data.name}' requires Id`); } if (!semver.valid(data.version)) { throw new Error( `Version ${data.version} of recipe '${data.name}' is not a valid semver version`, ); } // from the recipe this.id = ifUndefined(data.id, this.id); this.name = ifUndefined(data.name, this.name); this.version = ifUndefined(data.version, this.version); this.aliases = ifUndefined(data.aliases, this.aliases); this.serviceURL = ifUndefined( data.config.serviceURL, this.serviceURL, ); this.hasDirectMessages = ifUndefined( data.config.hasDirectMessages, this.hasDirectMessages, ); this.hasIndirectMessages = ifUndefined( data.config.hasIndirectMessages, this.hasIndirectMessages, ); this.hasNotificationSound = ifUndefined( data.config.hasNotificationSound, this.hasNotificationSound, ); this.hasTeamId = ifUndefined( data.config.hasTeamId, this.hasTeamId, ); this.hasCustomUrl = ifUndefined( data.config.hasCustomUrl, this.hasCustomUrl, ); this.hasHostedOption = ifUndefined( data.config.hasHostedOption, this.hasHostedOption, ); this.urlInputPrefix = ifUndefined( data.config.urlInputPrefix, this.urlInputPrefix, ); this.urlInputSuffix = ifUndefined( data.config.urlInputSuffix, this.urlInputSuffix, ); this.disablewebsecurity = ifUndefined( data.config.disablewebsecurity, this.disablewebsecurity, ); this.local = ifUndefined(data.config.local, this.local); this.message = ifUndefined(data.config.message, this.message); this.allowFavoritesDelineationInUnreadCount = ifUndefined( data.config.allowFavoritesDelineationInUnreadCount, this.allowFavoritesDelineationInUnreadCount, ); // computed this.path = data.path; this.partition = ifUndefined(data.config.partition, this.partition); } // TODO: Need to remove this if its not used anywhere get author(): string[] { return []; } get hasDarkMode(): boolean { return pathExistsSync(join(this.path, 'darkmode.css')); } }