meta: change naming convention of interfaces and use importsNotUsedAsValues: 'error' in tsconfig for clearer imports
This commit is contained in:
parent
a807c6f2be
commit
09dc14726d
|
@ -150,7 +150,7 @@
|
||||||
{
|
{
|
||||||
"selector": "interface",
|
"selector": "interface",
|
||||||
"format": ["PascalCase"],
|
"format": ["PascalCase"],
|
||||||
"prefix": ["I"]
|
"suffix": ["Interface"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"@typescript-eslint/explicit-member-accessibility": "error",
|
"@typescript-eslint/explicit-member-accessibility": "error",
|
||||||
|
|
11
src/main.ts
11
src/main.ts
|
@ -4,21 +4,20 @@ import './main/core/install';
|
||||||
|
|
||||||
import { app } from 'electron';
|
import { app } from 'electron';
|
||||||
import { isDev } from './main/core/env';
|
import { isDev } from './main/core/env';
|
||||||
import { IAppWindow } from './main/modules/app-window/i-app-window';
|
import type { AppWindowInterface } from './main/modules/app-window/app-window-interface';
|
||||||
import { ILogger } from './main/modules/logger/i-logger';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* have a read: https://github.com/nodejs/node/issues/20392, over 100 comments as of 2020-07-26
|
* have a read: https://github.com/nodejs/node/issues/20392, over 100 comments as of 2020-07-26
|
||||||
* https://nodejs.org/api/process.html#process_event_unhandledrejection
|
* https://nodejs.org/api/process.html#process_event_unhandledrejection
|
||||||
*/
|
*/
|
||||||
process.on('unhandledRejection', (reason) => {
|
process.on('unhandledRejection', (reason) => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
void logger.fatal(`Unhandled Rejection, see ${logger.getExceptionsLogFile()}`);
|
void logger.fatal(`Unhandled Rejection, see ${logger.getExceptionsLogFile()}`);
|
||||||
throw reason;
|
throw reason;
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('uncaughtException', (error) => {
|
process.on('uncaughtException', (error) => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
void logger.exception(error);
|
void logger.exception(error);
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
// eslint-disable-next-line no-console -- only for development purposes
|
// eslint-disable-next-line no-console -- only for development purposes
|
||||||
|
@ -27,7 +26,7 @@ process.on('uncaughtException', (error) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function createWindow(): Promise<void> {
|
async function createWindow(): Promise<void> {
|
||||||
const appWindowMain: IAppWindow = container.get('app-window-main');
|
const appWindowMain: AppWindowInterface = container.get('app-window-main');
|
||||||
await appWindowMain.open();
|
await appWindowMain.open();
|
||||||
|
|
||||||
appWindowMain.window?.on('closed', () => {
|
appWindowMain.window?.on('closed', () => {
|
||||||
|
@ -48,7 +47,7 @@ app.on('window-all-closed', () => {
|
||||||
app.on('activate', async () => {
|
app.on('activate', async () => {
|
||||||
// On OS X it"s common to re-create a window in the app when the
|
// On OS X it"s common to re-create a window in the app when the
|
||||||
// dock icon is clicked and there are no other windows open.
|
// dock icon is clicked and there are no other windows open.
|
||||||
const appWindowMain: IAppWindow = container.get('app-window-main');
|
const appWindowMain: AppWindowInterface = container.get('app-window-main');
|
||||||
if (appWindowMain.isClosed()) {
|
if (appWindowMain.isClosed()) {
|
||||||
await createWindow();
|
await createWindow();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { Connection, createConnection as ormCreateConnection } from 'typeorm';
|
import { Connection, createConnection as ormCreateConnection } from 'typeorm';
|
||||||
import { BetterSqlite3ConnectionOptions } from 'typeorm/driver/better-sqlite3/BetterSqlite3ConnectionOptions';
|
import type { BetterSqlite3ConnectionOptions } from 'typeorm/driver/better-sqlite3/BetterSqlite3ConnectionOptions';
|
||||||
import { appPath } from './app-path';
|
import { appPath } from './app-path';
|
||||||
|
|
||||||
export enum Database {
|
export enum Database {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { Author } from './author';
|
import { Author } from './author';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class AuthorName implements IIdentifiableEntity, INameEntity {
|
export class AuthorName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { AuthorRole } from './author-role';
|
import { AuthorRole } from './author-role';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class AuthorRoleName implements IIdentifiableEntity, INameEntity {
|
export class AuthorRoleName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { WorkAuthor } from './work-author';
|
||||||
* Examples: story writing, drawing, animating, publishing, ...
|
* Examples: story writing, drawing, animating, publishing, ...
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class AuthorRole implements IIdentifiableEntity, IMultiNamedEntity, IDescribableEntity {
|
export class AuthorRole implements IdentifiableEntityInterface, MultiNamedEntityInterface, DescribableEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { WorkAuthor } from './work-author';
|
||||||
* This entity represents a single real-world entity, be it a person or named group of persons.
|
* This entity represents a single real-world entity, be it a person or named group of persons.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Author implements IIdentifiableEntity, IMultiNamedEntity {
|
export class Author implements IdentifiableEntityInterface, MultiNamedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { WorkCharacter } from './work-character';
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
@PercentCheck('weight')
|
@PercentCheck('weight')
|
||||||
export class CharacterTag implements IIdentifiableEntity, IWeightedEntity {
|
export class CharacterTag implements IdentifiableEntityInterface, WeightedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { Collection } from './collection';
|
import { Collection } from './collection';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class CollectionName implements IIdentifiableEntity, INameEntity {
|
export class CollectionName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Work } from './work';
|
||||||
* The main use case is chronological ordering.
|
* The main use case is chronological ordering.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class CollectionPart implements IIdentifiableEntity, IOrderableEntity {
|
export class CollectionPart implements IdentifiableEntityInterface, OrderableEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { CollectionPart } from './collection-part';
|
||||||
* If authors of works see them as belonging together, they are a collection
|
* If authors of works see them as belonging together, they are a collection
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Collection implements IIdentifiableEntity, IMultiNamedEntity {
|
export class Collection implements IdentifiableEntityInterface, MultiNamedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Work } from './work';
|
||||||
* Multiple works can have multiple copies (think of different scans of a physical work, or lossy compression).
|
* Multiple works can have multiple copies (think of different scans of a physical work, or lossy compression).
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Copy implements IIdentifiableEntity {
|
export class Copy implements IdentifiableEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Entities extending this one have a user-maintained description.
|
* Entities extending this one have a user-maintained description.
|
||||||
*/
|
*/
|
||||||
declare interface IDescribableEntity {
|
interface DescribableEntityInterface {
|
||||||
/**
|
/**
|
||||||
* a text describing this entity
|
* a text describing this entity
|
||||||
*/
|
*/
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Entities implementing this interface build a hierarchy.
|
* Entities implementing this interface build a hierarchy.
|
||||||
*/
|
*/
|
||||||
declare interface IHierachicalEntity<T> {
|
interface HierarchicalEntityInterface<T> {
|
||||||
/**
|
/**
|
||||||
* parent entities
|
* parent entities
|
||||||
*/
|
*/
|
|
@ -2,7 +2,7 @@
|
||||||
* Every database entity should implement this one.
|
* Every database entity should implement this one.
|
||||||
* It does nothing more but guarantee there is an id column.
|
* It does nothing more but guarantee there is an id column.
|
||||||
*/
|
*/
|
||||||
declare interface IIdentifiableEntity {
|
interface IdentifiableEntityInterface {
|
||||||
/**
|
/**
|
||||||
* the entity id
|
* the entity id
|
||||||
*/
|
*/
|
|
@ -8,7 +8,7 @@ import { WorkCharacter } from './work-character';
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
@PercentCheck('weight')
|
@PercentCheck('weight')
|
||||||
export class InteractionTag implements IIdentifiableEntity, IWeightedEntity {
|
export class InteractionTag implements IdentifiableEntityInterface, WeightedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Entities extending this interface can have multiple names.
|
* Entities extending this interface can have multiple names.
|
||||||
*/
|
*/
|
||||||
declare interface IMultiNamedEntity {
|
interface MultiNamedEntityInterface {
|
||||||
/**
|
/**
|
||||||
* the name which is displayed in the user interface
|
* the name which is displayed in the user interface
|
||||||
*/
|
*/
|
||||||
|
@ -10,5 +10,5 @@ declare interface IMultiNamedEntity {
|
||||||
/**
|
/**
|
||||||
* other names for the entity
|
* other names for the entity
|
||||||
*/
|
*/
|
||||||
names: Promise<INameEntity[]>;
|
names: Promise<NameEntityInterface[]>;
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* This entity describes a single name of an entity with multiple names.
|
* This entity describes a single name of an entity with multiple names.
|
||||||
*/
|
*/
|
||||||
declare interface INameEntity {
|
interface NameEntityInterface {
|
||||||
/**
|
/**
|
||||||
* the name
|
* the name
|
||||||
*/
|
*/
|
||||||
|
@ -10,5 +10,5 @@ declare interface INameEntity {
|
||||||
/**
|
/**
|
||||||
* the entity to which the names belong
|
* the entity to which the names belong
|
||||||
*/
|
*/
|
||||||
entity: Promise<IMultiNamedEntity>;
|
entity: Promise<MultiNamedEntityInterface>;
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Entities implementing this interface can be ordered.
|
* Entities implementing this interface can be ordered.
|
||||||
*/
|
*/
|
||||||
declare interface IOrderableEntity {
|
interface OrderableEntityInterface {
|
||||||
/**
|
/**
|
||||||
* a lower number means a higher ordering
|
* a lower number means a higher ordering
|
||||||
*/
|
*/
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { Site } from './site';
|
import { Site } from './site';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class SiteName implements IIdentifiableEntity, INameEntity {
|
export class SiteName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Source } from './source';
|
||||||
* This non-user-maintained entity describes an online provider of works which can be scraped.
|
* This non-user-maintained entity describes an online provider of works which can be scraped.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Site implements IIdentifiableEntity, IMultiNamedEntity {
|
export class Site implements IdentifiableEntityInterface, MultiNamedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Site } from './site';
|
||||||
* This entity describes an external source of a copy, in most cases that is a website.
|
* This entity describes an external source of a copy, in most cases that is a website.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Source implements IIdentifiableEntity {
|
export class Source implements IdentifiableEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { Tag } from './tag';
|
import { Tag } from './tag';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class TagName implements IIdentifiableEntity, INameEntity {
|
export class TagName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,12 @@ import { WorkTag } from './work-tag';
|
||||||
* They can also be in a hierarchy
|
* They can also be in a hierarchy
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Tag implements IIdentifiableEntity, IMultiNamedEntity, IDescribableEntity, IHierachicalEntity<Tag> {
|
export class Tag
|
||||||
|
implements
|
||||||
|
IdentifiableEntityInterface,
|
||||||
|
MultiNamedEntityInterface,
|
||||||
|
DescribableEntityInterface,
|
||||||
|
HierarchicalEntityInterface<Tag> {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { TransformationType } from './transformation-type';
|
import { TransformationType } from './transformation-type';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class TransformationTypeName implements IIdentifiableEntity, INameEntity {
|
export class TransformationTypeName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { TransformationTypeName } from './transformation-type-name';
|
||||||
* Possible type: translation, decensor, collection, ...
|
* Possible type: translation, decensor, collection, ...
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class TransformationType implements IIdentifiableEntity, IMultiNamedEntity, IDescribableEntity {
|
export class TransformationType
|
||||||
|
implements IdentifiableEntityInterface, MultiNamedEntityInterface, DescribableEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Work } from './work';
|
||||||
* This entity describes how one work is transformed to another.
|
* This entity describes how one work is transformed to another.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Transformation implements IIdentifiableEntity, IOrderableEntity {
|
export class Transformation implements IdentifiableEntityInterface, OrderableEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* An entity implementing this interface has a weight property.
|
* An entity implementing this interface has a weight property.
|
||||||
*/
|
*/
|
||||||
declare interface IWeightedEntity {
|
interface WeightedEntityInterface {
|
||||||
/**
|
/**
|
||||||
* the weight, mathematically a number (0,1], practically between (0,Number.MAX_SAFE_INTEGER]
|
* the weight, mathematically a number (0,1], practically between (0,Number.MAX_SAFE_INTEGER]
|
||||||
* the weight can also be not not defined, null in the database
|
* the weight can also be not not defined, null in the database
|
|
@ -7,7 +7,7 @@ import { Work } from './work';
|
||||||
* This entity connects authors with their work and their role therein.
|
* This entity connects authors with their work and their role therein.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class WorkAuthor implements IIdentifiableEntity, IOrderableEntity {
|
export class WorkAuthor implements IdentifiableEntityInterface, OrderableEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { WorkCharacter } from './work-character';
|
import { WorkCharacter } from './work-character';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class WorkCharacterName implements IIdentifiableEntity, INameEntity {
|
export class WorkCharacterName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { WorldCharacter } from './world-character';
|
||||||
* The character can be original or based on one or more existing characters.
|
* The character can be original or based on one or more existing characters.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class WorkCharacter implements IIdentifiableEntity, IMultiNamedEntity {
|
export class WorkCharacter implements IdentifiableEntityInterface, MultiNamedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { Work } from './work';
|
import { Work } from './work';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class WorkName implements IIdentifiableEntity, INameEntity {
|
export class WorkName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { Work } from './work';
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
@PercentCheck('weight')
|
@PercentCheck('weight')
|
||||||
export class WorkTag implements IIdentifiableEntity, IWeightedEntity {
|
export class WorkTag implements IdentifiableEntityInterface, WeightedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { World } from './world';
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
@PercentCheck('rating')
|
@PercentCheck('rating')
|
||||||
export class Work implements IIdentifiableEntity, IMultiNamedEntity {
|
export class Work implements IdentifiableEntityInterface, MultiNamedEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { WorldCharacter } from './world-character';
|
import { WorldCharacter } from './world-character';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class WorldCharacterName implements IIdentifiableEntity, INameEntity {
|
export class WorldCharacterName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { WorldCharacterName } from './world-character-name';
|
||||||
* This entity describes a canon character in a fictional world.
|
* This entity describes a canon character in a fictional world.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class WorldCharacter implements IIdentifiableEntity, IMultiNamedEntity, IHierachicalEntity<WorldCharacter> {
|
export class WorldCharacter
|
||||||
|
implements IdentifiableEntityInterface, MultiNamedEntityInterface, HierarchicalEntityInterface<WorldCharacter> {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { World } from './world';
|
import { World } from './world';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class WorldName implements IIdentifiableEntity, INameEntity {
|
export class WorldName implements IdentifiableEntityInterface, NameEntityInterface {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { WorldName } from './world-name';
|
||||||
* This entity describes a fictional world.
|
* This entity describes a fictional world.
|
||||||
*/
|
*/
|
||||||
@Entity()
|
@Entity()
|
||||||
export class World implements IIdentifiableEntity, IMultiNamedEntity, IHierachicalEntity<World> {
|
export class World
|
||||||
|
implements IdentifiableEntityInterface, MultiNamedEntityInterface, HierarchicalEntityInterface<World> {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id!: number;
|
public id!: number;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import type { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
export class initialMigration1597705000730 implements MigrationInterface {
|
export class initialMigration1597705000730 implements MigrationInterface {
|
||||||
name = 'initialMigration1597705000730';
|
name = 'initialMigration1597705000730';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import type { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
export class initialMigration1587511036078 implements MigrationInterface {
|
export class initialMigration1587511036078 implements MigrationInterface {
|
||||||
name = 'initialMigration1587511036078';
|
name = 'initialMigration1587511036078';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import BrowserWindow = Electron.BrowserWindow;
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
export interface IAppWindow {
|
interface AppWindowInterface {
|
||||||
window: BrowserWindow | null;
|
window: BrowserWindow | null;
|
||||||
open(): Promise<void>;
|
open(): Promise<void>;
|
||||||
close(force?: boolean): void;
|
close(force?: boolean): void;
|
|
@ -2,8 +2,8 @@ import { app, BrowserWindow, Event, LoadFileOptions, LoadURLOptions, NewWindowWe
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { isDev } from '../../core/env';
|
import { isDev } from '../../core/env';
|
||||||
import { ISessionHelper } from '../session/i-session-helper';
|
import type { SessionHelperInterface } from '../session/session-helper-interface';
|
||||||
import { IAppWindow } from './i-app-window';
|
import type { AppWindowInterface } from './app-window-interface';
|
||||||
import BrowserWindowConstructorOptions = Electron.BrowserWindowConstructorOptions;
|
import BrowserWindowConstructorOptions = Electron.BrowserWindowConstructorOptions;
|
||||||
|
|
||||||
let defaultOptions: BrowserWindowConstructorOptions = {
|
let defaultOptions: BrowserWindowConstructorOptions = {
|
||||||
|
@ -30,12 +30,12 @@ switch (os.platform()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AppWindow implements IAppWindow {
|
export abstract class AppWindow implements AppWindowInterface {
|
||||||
protected static default = {};
|
protected static default = {};
|
||||||
|
|
||||||
protected _window: BrowserWindow | null = null;
|
protected _window: BrowserWindow | null = null;
|
||||||
|
|
||||||
protected readonly sessionHelper: ISessionHelper;
|
protected readonly sessionHelper: SessionHelperInterface;
|
||||||
|
|
||||||
protected options: BrowserWindowConstructorOptions;
|
protected options: BrowserWindowConstructorOptions;
|
||||||
|
|
||||||
|
@ -43,7 +43,11 @@ export abstract class AppWindow implements IAppWindow {
|
||||||
|
|
||||||
protected abstract loadOptions: LoadFileOptions | LoadURLOptions;
|
protected abstract loadOptions: LoadFileOptions | LoadURLOptions;
|
||||||
|
|
||||||
protected constructor(sessionHelper: ISessionHelper, uri: string, options: BrowserWindowConstructorOptions = {}) {
|
protected constructor(
|
||||||
|
sessionHelper: SessionHelperInterface,
|
||||||
|
uri: string,
|
||||||
|
options: BrowserWindowConstructorOptions = {}
|
||||||
|
) {
|
||||||
this.sessionHelper = sessionHelper;
|
this.sessionHelper = sessionHelper;
|
||||||
this.options = { ...defaultOptions, ...options };
|
this.options = { ...defaultOptions, ...options };
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
|
@ -85,7 +89,7 @@ export abstract class AppWindow implements IAppWindow {
|
||||||
return !this._window;
|
return !this._window;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getCsp(): IContentSecurityPolicy {
|
protected getCsp(): Session.ContentSecurityPolicy {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { BrowserWindow, BrowserWindowConstructorOptions, LoadFileOptions } from 'electron';
|
import type { BrowserWindow, BrowserWindowConstructorOptions, LoadFileOptions } from 'electron';
|
||||||
import { ISessionHelper } from '../session/i-session-helper';
|
import type { SessionHelperInterface } from '../session/session-helper-interface';
|
||||||
import { AppWindow } from './app-window';
|
import { AppWindow } from './app-window';
|
||||||
|
|
||||||
export abstract class FileAppWindow extends AppWindow {
|
export abstract class FileAppWindow extends AppWindow {
|
||||||
protected loadOptions: LoadFileOptions;
|
protected loadOptions: LoadFileOptions;
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
sessionHelper: ISessionHelper,
|
sessionHelper: SessionHelperInterface,
|
||||||
uri: string,
|
uri: string,
|
||||||
options: BrowserWindowConstructorOptions = {},
|
options: BrowserWindowConstructorOptions = {},
|
||||||
loadOptions: LoadFileOptions = {}
|
loadOptions: LoadFileOptions = {}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { IUrlAppWindow } from './i-url-app-window';
|
|
||||||
|
|
||||||
export interface ISiteAppWindow extends IUrlAppWindow {
|
|
||||||
/**
|
|
||||||
* @see IMutex.acquire
|
|
||||||
*/
|
|
||||||
acquireLock(): Promise<Mutex.ReleaseFunction>;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { LoadURLOptions } from 'electron';
|
|
||||||
import { IAppWindow } from './i-app-window';
|
|
||||||
|
|
||||||
export interface IUrlAppWindow extends IAppWindow {
|
|
||||||
downloadUrlSafe(url: string, savePath: string, options?: LoadURLOptions): Promise<void>;
|
|
||||||
|
|
||||||
loadUrlSafe(url: string, options?: LoadURLOptions): Promise<void>;
|
|
||||||
}
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { inject } from '../../core/inject';
|
import { inject } from '../../core/inject';
|
||||||
import { ISessionHelper } from '../session/i-session-helper';
|
import type { SessionHelperInterface } from '../session/session-helper-interface';
|
||||||
import { FileAppWindow } from './file-app-window';
|
import { FileAppWindow } from './file-app-window';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class MainAppWindow extends FileAppWindow {
|
export class MainAppWindow extends FileAppWindow {
|
||||||
public constructor(@inject('session-helper') sessionHelper: ISessionHelper) {
|
public constructor(@inject('session-helper') sessionHelper: SessionHelperInterface) {
|
||||||
super(sessionHelper, 'frontend/index.html', {
|
super(sessionHelper, 'frontend/index.html', {
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import type { UrlAppWindowInterface } from './url-app-window-interface';
|
||||||
|
|
||||||
|
interface SiteAppWindowInterface extends UrlAppWindowInterface {
|
||||||
|
/**
|
||||||
|
* @see IMutex.acquire
|
||||||
|
*/
|
||||||
|
acquireLock(): Promise<Mutex.ReleaseFunction>;
|
||||||
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
import { BrowserWindowConstructorOptions, LoadURLOptions } from 'electron';
|
import type { BrowserWindowConstructorOptions, LoadURLOptions } from 'electron';
|
||||||
import { SimpleMutex } from '../mutex/simple-mutex';
|
import { SimpleMutex } from '../mutex/simple-mutex';
|
||||||
import { ISessionHelper } from '../session/i-session-helper';
|
import type { SessionHelperInterface } from '../session/session-helper-interface';
|
||||||
import { ISiteAppWindow } from './i-site-app-window';
|
import type { SiteAppWindowInterface } from './site-app-window-interface';
|
||||||
import { UrlAppWindow } from './url-app-window';
|
import { UrlAppWindow } from './url-app-window';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents an app window of a site which need to be crawled via the built-in chromium of Electron.
|
* This class represents an app window of a site which need to be crawled via the built-in chromium of Electron.
|
||||||
* It offers a lock so that multiple calls do executed simultaneously on the same chromium window.
|
* It offers a lock so that multiple calls do executed simultaneously on the same chromium window.
|
||||||
*/
|
*/
|
||||||
export abstract class SiteAppWindow extends UrlAppWindow implements ISiteAppWindow {
|
export abstract class SiteAppWindow extends UrlAppWindow implements SiteAppWindowInterface {
|
||||||
private windowLock: IMutex;
|
private windowLock: MutexInterface;
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
sessionHelper: ISessionHelper,
|
sessionHelper: SessionHelperInterface,
|
||||||
uri: string,
|
uri: string,
|
||||||
options: BrowserWindowConstructorOptions = {},
|
options: BrowserWindowConstructorOptions = {},
|
||||||
loadOptions: LoadURLOptions = {}
|
loadOptions: LoadURLOptions = {}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import type { LoadURLOptions } from 'electron';
|
||||||
|
import type { AppWindowInterface } from './app-window-interface';
|
||||||
|
|
||||||
|
interface UrlAppWindowInterface extends AppWindowInterface {
|
||||||
|
downloadUrlSafe(url: string, savePath: string, options?: LoadURLOptions): Promise<void>;
|
||||||
|
|
||||||
|
loadUrlSafe(url: string, options?: LoadURLOptions): Promise<void>;
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
import { BrowserWindow, BrowserWindowConstructorOptions, LoadURLOptions } from 'electron';
|
import type { BrowserWindow, BrowserWindowConstructorOptions, LoadURLOptions } from 'electron';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { ISessionHelper } from '../session/i-session-helper';
|
import type { SessionHelperInterface } from '../session/session-helper-interface';
|
||||||
import { AppWindow } from './app-window';
|
import { AppWindow } from './app-window';
|
||||||
import { IUrlAppWindow } from './i-url-app-window';
|
import type { UrlAppWindowInterface } from './url-app-window-interface';
|
||||||
import { WindowClosedError } from './window-closed-error';
|
import { WindowClosedError } from './window-closed-error';
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
|
||||||
export abstract class UrlAppWindow extends AppWindow implements IUrlAppWindow {
|
export abstract class UrlAppWindow extends AppWindow implements UrlAppWindowInterface {
|
||||||
protected loadOptions: LoadURLOptions;
|
protected loadOptions: LoadURLOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,7 +32,7 @@ export abstract class UrlAppWindow extends AppWindow implements IUrlAppWindow {
|
||||||
private loadWait: Promise<void> = Promise.resolve();
|
private loadWait: Promise<void> = Promise.resolve();
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
sessionHelper: ISessionHelper,
|
sessionHelper: SessionHelperInterface,
|
||||||
uri: string,
|
uri: string,
|
||||||
options: BrowserWindowConstructorOptions = {},
|
options: BrowserWindowConstructorOptions = {},
|
||||||
loadOptions: LoadURLOptions = {}
|
loadOptions: LoadURLOptions = {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { dialog, OpenDialogOptions } from 'electron';
|
import { dialog, OpenDialogOptions } from 'electron';
|
||||||
|
|
||||||
interface IDialog {
|
interface DialogInterface {
|
||||||
selectFolder(options: OpenDialogOptions): ReturnType<typeof dialog.showOpenDialog>;
|
selectFolder(options: OpenDialogOptions): ReturnType<typeof dialog.showOpenDialog>;
|
||||||
}
|
}
|
|
@ -1,14 +1,13 @@
|
||||||
import { dialog, OpenDialogOptions } from 'electron';
|
import { dialog, OpenDialogOptions } from 'electron';
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { inject } from '../../core/inject';
|
import { inject } from '../../core/inject';
|
||||||
import { II18nTranslator } from '../i18n/i-i18n-translator';
|
import type { DialogInterface } from './dialog-interface';
|
||||||
import { IDialog } from './i-dialog';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class Dialog implements IDialog {
|
export class Dialog implements DialogInterface {
|
||||||
private readonly translator: II18nTranslator;
|
private readonly translator: I18nTranslatorInterface;
|
||||||
|
|
||||||
public constructor(@inject('i18n-translator') translator: II18nTranslator) {
|
public constructor(@inject('i18n-translator') translator: I18nTranslatorInterface) {
|
||||||
this.translator = translator;
|
this.translator = translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export interface II18nTranslator {
|
|
||||||
t(text: string): string;
|
|
||||||
}
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
interface I18nTranslatorInterface {
|
||||||
|
t(text: string): string;
|
||||||
|
}
|
|
@ -1,8 +1,7 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { II18nTranslator } from './i-i18n-translator';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class I18nTranslator implements II18nTranslator {
|
export class I18nTranslator implements I18nTranslatorInterface {
|
||||||
public t(text: string): string {
|
public t(text: string): string {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { registerHandler } from '../ipc-server';
|
import { registerHandler } from '../ipc-server';
|
||||||
|
|
||||||
export function answer(channel: IpcChannel): IpcControllerMethodDecorator {
|
export function answer(channel: IpcChannel): IpcControllerMethodDecorator {
|
||||||
return function (target: IIpcController, propertyKey): void {
|
return function (target: IpcController, propertyKey): void {
|
||||||
registerHandler(channel, target, propertyKey);
|
registerHandler(channel, target, propertyKey);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import IpcMainEvent = Electron.IpcMainEvent;
|
import IpcMainEvent = Electron.IpcMainEvent;
|
||||||
|
|
||||||
export function registerHandler(channel: IpcChannel, controller: IIpcController, handler: string): void {
|
export function registerHandler(channel: IpcChannel, controller: IpcController, handler: string): void {
|
||||||
ipcMain.on(channel, (event: IpcMainEvent, payload: IIpcPayload) => {
|
ipcMain.on(channel, (event: IpcMainEvent, payload: IpcPayload) => {
|
||||||
((controller.get() as unknown) as { [x: string]: IpcHandler })
|
((controller.get() as unknown) as { [x: string]: IpcHandler })
|
||||||
[handler](payload.data)
|
[handler](payload.data)
|
||||||
.then((result: unknown) => {
|
.then((result: unknown) => {
|
||||||
const response: IIpcResponse = {
|
const response: IpcResponse = {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
success: true,
|
success: true,
|
||||||
data: result,
|
data: result,
|
||||||
|
@ -14,7 +14,7 @@ export function registerHandler(channel: IpcChannel, controller: IIpcController,
|
||||||
event.reply(channel, response);
|
event.reply(channel, response);
|
||||||
})
|
})
|
||||||
.catch((reason: Error) => {
|
.catch((reason: Error) => {
|
||||||
const response: IIpcResponse = {
|
const response: IpcResponse = {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
success: false,
|
success: false,
|
||||||
error: reason.message,
|
error: reason.message,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* A Logger provides methods to save a developer message somewhere it can be retrieved
|
* A Logger provides methods to save a developer message somewhere it can be retrieved
|
||||||
* @see ILogger.getLogFile
|
* @see LoggerInterface.getLogFile
|
||||||
*/
|
*/
|
||||||
export interface ILogger {
|
interface LoggerInterface {
|
||||||
/**
|
/**
|
||||||
* default logging method, the logging level needs to be specified
|
* default logging method, the logging level needs to be specified
|
||||||
* @see LogLevel
|
* @see LogLevel
|
||||||
|
@ -47,7 +47,7 @@ export interface ILogger {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* logs the error, preferably with stack trace
|
* logs the error, preferably with stack trace
|
||||||
* @see ILogger.getExceptionsLogFile
|
* @see LoggerInterface.getExceptionsLogFile
|
||||||
*/
|
*/
|
||||||
exception(error: Error): Promise<void>;
|
exception(error: Error): Promise<void>;
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { ILogger } from './i-logger';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mock of a logger, does not log anywhere
|
* Mock of a logger, does not log anywhere
|
||||||
*/
|
*/
|
||||||
@injectable()
|
@injectable()
|
||||||
export class LoggerMock implements ILogger {
|
export class LoggerMock implements LoggerInterface {
|
||||||
public getLogFile(): string {
|
public getLogFile(): string {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import fc from 'fast-check';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { container } from '../../core/container';
|
import { container } from '../../core/container';
|
||||||
import { setDev } from '../../core/env.spec';
|
import { setDev } from '../../core/env.spec';
|
||||||
import { ILogger } from './i-logger';
|
|
||||||
import { LogLevel } from './log-level';
|
import { LogLevel } from './log-level';
|
||||||
|
|
||||||
chai.use(chaiFs);
|
chai.use(chaiFs);
|
||||||
|
@ -25,7 +24,7 @@ describe('Logger Service', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultLogReadLineInterface(logger: ILogger) {
|
function getDefaultLogReadLineInterface(logger: LoggerInterface) {
|
||||||
return createInterface(fs.createReadStream(logger.getLogFile()));
|
return createInterface(fs.createReadStream(logger.getLogFile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,20 +41,20 @@ describe('Logger Service', () => {
|
||||||
const logLevelNumberArbitrary = fc.constantFrom(...logLevelsNumber);
|
const logLevelNumberArbitrary = fc.constantFrom(...logLevelsNumber);
|
||||||
|
|
||||||
it('creates log files', () => {
|
it('creates log files', () => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
|
|
||||||
expect(logger.getLogFile()).path('log file is not created');
|
expect(logger.getLogFile()).path('log file is not created');
|
||||||
expect(logger.getExceptionsLogFile()).path('exception log file is not created');
|
expect(logger.getExceptionsLogFile()).path('exception log file is not created');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('logs exceptions', async () => {
|
it('logs exceptions', async () => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
|
|
||||||
await logger.exception(new Error('this is an error'));
|
await logger.exception(new Error('this is an error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("default log file doesn't get bigger than 50KB @slow", async () => {
|
it("default log file doesn't get bigger than 50KB @slow", async () => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
|
|
||||||
let prevLogFileSize = (await fs.stat(logger.getLogFile())).size;
|
let prevLogFileSize = (await fs.stat(logger.getLogFile())).size;
|
||||||
let minNumberOfLines = maxLogSize;
|
let minNumberOfLines = maxLogSize;
|
||||||
|
@ -76,7 +75,7 @@ describe('Logger Service', () => {
|
||||||
}).timeout(15000);
|
}).timeout(15000);
|
||||||
|
|
||||||
it("exception log file doesn't get bigger than 50KB @slow", async () => {
|
it("exception log file doesn't get bigger than 50KB @slow", async () => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
|
|
||||||
let prevLogFileSize = (await fs.stat(logger.getExceptionsLogFile())).size;
|
let prevLogFileSize = (await fs.stat(logger.getExceptionsLogFile())).size;
|
||||||
let minNumberOfLines = maxLogSize;
|
let minNumberOfLines = maxLogSize;
|
||||||
|
@ -97,7 +96,7 @@ describe('Logger Service', () => {
|
||||||
}).timeout(15000);
|
}).timeout(15000);
|
||||||
|
|
||||||
it('logs different levels directly', () => {
|
it('logs different levels directly', () => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
|
|
||||||
return fc.assert(
|
return fc.assert(
|
||||||
fc.asyncProperty(logLevelArbitrary as LogLevelArbitrary, fc.string(), async (logLevel, message) => {
|
fc.asyncProperty(logLevelArbitrary as LogLevelArbitrary, fc.string(), async (logLevel, message) => {
|
||||||
|
@ -113,7 +112,7 @@ describe('Logger Service', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('logs different levels indirectly via the generic log function', () => {
|
it('logs different levels indirectly via the generic log function', () => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
|
|
||||||
return fc.assert(
|
return fc.assert(
|
||||||
fc.asyncProperty(logLevelNumberArbitrary, fc.string(), async (logLevelNumber, message) => {
|
fc.asyncProperty(logLevelNumberArbitrary, fc.string(), async (logLevelNumber, message) => {
|
||||||
|
@ -132,7 +131,7 @@ describe('Logger Service', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('logs debug only in dev mode', async () => {
|
it('logs debug only in dev mode', async () => {
|
||||||
const logger: ILogger = container.get('logger');
|
const logger: LoggerInterface = container.get('logger');
|
||||||
|
|
||||||
setDev();
|
setDev();
|
||||||
await logger.debug('this is a development message');
|
await logger.debug('this is a development message');
|
||||||
|
|
|
@ -4,7 +4,6 @@ import * as fs from 'fs-extra';
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { appPath } from '../../core/app-path';
|
import { appPath } from '../../core/app-path';
|
||||||
import { isDev } from '../../core/env';
|
import { isDev } from '../../core/env';
|
||||||
import { ILogger } from './i-logger';
|
|
||||||
import { LogLevel } from './log-level';
|
import { LogLevel } from './log-level';
|
||||||
|
|
||||||
const loggingDir = path.resolve(appPath, 'logs');
|
const loggingDir = path.resolve(appPath, 'logs');
|
||||||
|
@ -16,7 +15,7 @@ const maxLogSize = 50000;
|
||||||
* A logger not using winston to log to files in the appPath.
|
* A logger not using winston to log to files in the appPath.
|
||||||
*/
|
*/
|
||||||
@injectable()
|
@injectable()
|
||||||
export class Logger implements ILogger {
|
export class Logger implements LoggerInterface {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
fs.createFileSync(logFile);
|
fs.createFileSync(logFile);
|
||||||
fs.createFileSync(exceptionLogFile);
|
fs.createFileSync(exceptionLogFile);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* Acquiring this lock returns a release function function (via promise) which needs to be called to release the lock again.
|
* Acquiring this lock returns a release function function (via promise) which needs to be called to release the lock again.
|
||||||
*/
|
*/
|
||||||
interface IMutex {
|
interface MutexInterface {
|
||||||
/**
|
/**
|
||||||
* acquires the lock and returns a Promise with the release function to be called when the lock shall be released.
|
* acquires the lock and returns a Promise with the release function to be called when the lock shall be released.
|
||||||
* This release function needs to be called or the lock will never release and execution of subsequent consumers will not take place.
|
* This release function needs to be called or the lock will never release and execution of subsequent consumers will not take place.
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* This class implements a simple mutex using a semaphore.
|
* This class implements a simple mutex using a semaphore.
|
||||||
*/
|
*/
|
||||||
export class SimpleMutex implements IMutex {
|
export class SimpleMutex implements MutexInterface {
|
||||||
/**
|
/**
|
||||||
* This queue is an array of promise resolve functions.
|
* This queue is an array of promise resolve functions.
|
||||||
* Calling them signals the corresponding consumer that the lock is now free.
|
* Calling them signals the corresponding consumer that the lock is now free.
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { ISiteAppWindow } from '../app-window/i-site-app-window';
|
|
||||||
|
|
||||||
export interface INhentaiAppWindow extends ISiteAppWindow {
|
|
||||||
getFavorites(): Promise<NodeJS.ReadableStream>;
|
|
||||||
}
|
|
|
@ -1,3 +1,3 @@
|
||||||
export interface INhentaiApi {
|
interface NhentaiApiInterface {
|
||||||
getFavorites(): Promise<NodeJS.ReadableStream>;
|
getFavorites(): Promise<NodeJS.ReadableStream>;
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { inject } from '../../core/inject';
|
import { inject } from '../../core/inject';
|
||||||
import { INhentaiApi } from './i-nhentai-api';
|
import type { NhentaiAppWindowInterface } from './nhentai-app-window-interface';
|
||||||
import { INhentaiAppWindow } from './i-nhentai-app-window';
|
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class NhentaiApi implements INhentaiApi {
|
export class NhentaiApi implements NhentaiApiInterface {
|
||||||
private readonly appWindow: INhentaiAppWindow;
|
private readonly appWindow: NhentaiAppWindowInterface;
|
||||||
|
|
||||||
public constructor(@inject('nhentai-app-window') appWindow: INhentaiAppWindow) {
|
public constructor(@inject('nhentai-app-window') appWindow: NhentaiAppWindowInterface) {
|
||||||
this.appWindow = appWindow;
|
this.appWindow = appWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import type { SiteAppWindowInterface } from '../app-window/site-app-window-interface';
|
||||||
|
|
||||||
|
interface NhentaiAppWindowInterface extends SiteAppWindowInterface {
|
||||||
|
getFavorites(): Promise<NodeJS.ReadableStream>;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { WebContents } from 'electron';
|
import type { WebContents } from 'electron';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
|
@ -8,9 +8,8 @@ import { injectable } from 'inversify';
|
||||||
import { inject } from '../../core/inject';
|
import { inject } from '../../core/inject';
|
||||||
import { SiteAppWindow } from '../app-window/site-app-window';
|
import { SiteAppWindow } from '../app-window/site-app-window';
|
||||||
import { WindowClosedError } from '../app-window/window-closed-error';
|
import { WindowClosedError } from '../app-window/window-closed-error';
|
||||||
import { ISessionHelper } from '../session/i-session-helper';
|
import type { SessionHelperInterface } from '../session/session-helper-interface';
|
||||||
import { INhentaiAppWindow } from './i-nhentai-app-window';
|
import type { NhentaiAppWindowInterface } from './nhentai-app-window-interface';
|
||||||
import { IFavorite } from './nhentai';
|
|
||||||
import {
|
import {
|
||||||
url as nhentaiUrl,
|
url as nhentaiUrl,
|
||||||
hostname as nhentaiHostname,
|
hostname as nhentaiHostname,
|
||||||
|
@ -25,8 +24,8 @@ import {
|
||||||
const waitInterval = 2000;
|
const waitInterval = 2000;
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class NhentaiAppWindow extends SiteAppWindow implements INhentaiAppWindow {
|
export class NhentaiAppWindow extends SiteAppWindow implements NhentaiAppWindowInterface {
|
||||||
public constructor(@inject('session-helper') sessionHelper: ISessionHelper) {
|
public constructor(@inject('session-helper') sessionHelper: SessionHelperInterface) {
|
||||||
super(sessionHelper, nhentaiUrl);
|
super(sessionHelper, nhentaiUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ export class NhentaiAppWindow extends SiteAppWindow implements INhentaiAppWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
const readable = Readable.from(
|
const readable = Readable.from(
|
||||||
(async function* (thisArg): AsyncGenerator<IFavorite, undefined> {
|
(async function* (thisArg): AsyncGenerator<Nhentai.Favorite, undefined> {
|
||||||
for (let i = 0; i < bookUrls.length; i++) {
|
for (let i = 0; i < bookUrls.length; i++) {
|
||||||
const bookUrl = bookUrls[i];
|
const bookUrl = bookUrls[i];
|
||||||
yield await thisArg.getBookTorrent(bookUrl);
|
yield await thisArg.getBookTorrent(bookUrl);
|
||||||
|
@ -76,7 +75,7 @@ export class NhentaiAppWindow extends SiteAppWindow implements INhentaiAppWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getCsp(): IContentSecurityPolicy {
|
protected getCsp(): Session.ContentSecurityPolicy {
|
||||||
return {
|
return {
|
||||||
'default-src': ['nhentai.net'],
|
'default-src': ['nhentai.net'],
|
||||||
'script-src': ['nhentai.net', "'unsafe-eval'"],
|
'script-src': ['nhentai.net', "'unsafe-eval'"],
|
||||||
|
@ -163,7 +162,7 @@ export class NhentaiAppWindow extends SiteAppWindow implements INhentaiAppWindow
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getBookTorrent(bookUrl: string): Promise<IFavorite> {
|
private async getBookTorrent(bookUrl: string): Promise<Nhentai.Favorite> {
|
||||||
if (!this._window) {
|
if (!this._window) {
|
||||||
throw new WindowClosedError();
|
throw new WindowClosedError();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { createWriteStream } from 'fs-extra';
|
import { createWriteStream } from 'fs-extra';
|
||||||
import { container } from '../../core/container';
|
import { container } from '../../core/container';
|
||||||
import { IDialog } from '../dialog/i-dialog';
|
import type { DialogInterface } from '../dialog/dialog-interface';
|
||||||
import { II18nTranslator } from '../i18n/i-i18n-translator';
|
|
||||||
import { answer } from '../ipc/annotations/answer';
|
import { answer } from '../ipc/annotations/answer';
|
||||||
import { INhentaiApi } from './i-nhentai-api';
|
|
||||||
import { IFavorite } from './nhentai';
|
|
||||||
|
|
||||||
export class NhentaiIpcController implements IIpcController {
|
export class NhentaiIpcController implements IpcController {
|
||||||
private readonly nhentaiApi: INhentaiApi;
|
private readonly nhentaiApi: NhentaiApiInterface;
|
||||||
|
|
||||||
private readonly translator: II18nTranslator;
|
private readonly translator: I18nTranslatorInterface;
|
||||||
|
|
||||||
private readonly dialog: IDialog;
|
private readonly dialog: DialogInterface;
|
||||||
|
|
||||||
private constructor(nhentaiApi: INhentaiApi, translator: II18nTranslator, dialog: IDialog) {
|
private constructor(nhentaiApi: NhentaiApiInterface, translator: I18nTranslatorInterface, dialog: DialogInterface) {
|
||||||
this.nhentaiApi = nhentaiApi;
|
this.nhentaiApi = nhentaiApi;
|
||||||
this.translator = translator;
|
this.translator = translator;
|
||||||
this.dialog = dialog;
|
this.dialog = dialog;
|
||||||
|
@ -33,7 +30,7 @@ export class NhentaiIpcController implements IIpcController {
|
||||||
const favoritesStream = await this.nhentaiApi.getFavorites();
|
const favoritesStream = await this.nhentaiApi.getFavorites();
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
favoritesStream.on('data', (favorite: IFavorite) => {
|
favoritesStream.on('data', (favorite: Nhentai.Favorite) => {
|
||||||
const writable = createWriteStream(path.resolve(result.filePaths[0], favorite.name));
|
const writable = createWriteStream(path.resolve(result.filePaths[0], favorite.name));
|
||||||
favorite.torrentFile.pipe(writable);
|
favorite.torrentFile.pipe(writable);
|
||||||
});
|
});
|
||||||
|
@ -43,9 +40,9 @@ export class NhentaiIpcController implements IIpcController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(): NhentaiIpcController {
|
public get(): NhentaiIpcController {
|
||||||
const nhentaiApi: INhentaiApi = container.get('nhentai-api');
|
const nhentaiApi: NhentaiApiInterface = container.get('nhentai-api');
|
||||||
const translator: II18nTranslator = container.get('i18n-translator');
|
const translator: I18nTranslatorInterface = container.get('i18n-translator');
|
||||||
const dialog: IDialog = container.get('dialog');
|
const dialog: DialogInterface = container.get('dialog');
|
||||||
return new NhentaiIpcController(nhentaiApi, translator, dialog);
|
return new NhentaiIpcController(nhentaiApi, translator, dialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
export interface IFavorite {
|
declare namespace Nhentai {
|
||||||
name: string;
|
type Favorite = {
|
||||||
torrentFile: NodeJS.ReadableStream;
|
name: string;
|
||||||
|
torrentFile: NodeJS.ReadableStream;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { BrowserWindow } from 'electron';
|
|
||||||
|
|
||||||
export interface ISessionHelper {
|
|
||||||
setCsp(window: BrowserWindow, csp: IContentSecurityPolicy): void;
|
|
||||||
}
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
interface SessionHelperInterface {
|
||||||
|
setCsp(window: BrowserWindow, csp: Session.ContentSecurityPolicy): void;
|
||||||
|
}
|
|
@ -1,23 +1,26 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { isDev } from '../../core/env';
|
import { isDev } from '../../core/env';
|
||||||
import { ISessionHelper } from './i-session-helper';
|
import type { SessionHelperInterface } from './session-helper-interface';
|
||||||
|
|
||||||
const defaultCsp: IContentSecurityPolicy = {
|
const defaultCsp: Session.ContentSecurityPolicy = {
|
||||||
'default-src': ["'self'"],
|
'default-src': ["'self'"],
|
||||||
'style-src': ["'unsafe-inline'"],
|
'style-src': ["'unsafe-inline'"],
|
||||||
'object-src': ["'none'"],
|
'object-src': ["'none'"],
|
||||||
};
|
};
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class SessionHelper implements ISessionHelper {
|
export class SessionHelper implements SessionHelperInterface {
|
||||||
private static stringifyCspHeader(csp: IContentSecurityPolicy): string {
|
private static stringifyCspHeader(csp: Session.ContentSecurityPolicy): string {
|
||||||
return Object.entries(csp)
|
return Object.entries(csp)
|
||||||
.map((directive: [string, CspValue[]]) => `${directive[0]} ${directive[1]?.join(' ')}`)
|
.map(
|
||||||
|
(directive: [string, Session.CspValue[] | undefined]) =>
|
||||||
|
`${directive[0]} ${directive[1] ? directive[1]?.join(' ') : ''}`
|
||||||
|
)
|
||||||
.join('; ');
|
.join('; ');
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCsp(window: Electron.BrowserWindow, csp: IContentSecurityPolicy): void {
|
public setCsp(window: Electron.BrowserWindow, csp: Session.ContentSecurityPolicy): void {
|
||||||
const mergedCsp: IContentSecurityPolicy = { ...defaultCsp, ...csp };
|
const mergedCsp: Session.ContentSecurityPolicy = { ...defaultCsp, ...csp };
|
||||||
|
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
mergedCsp['default-src'] = ['devtools:'].concat(mergedCsp['default-src'] ?? []);
|
mergedCsp['default-src'] = ['devtools:'].concat(mergedCsp['default-src'] ?? []);
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
type CspValue = '*' | "'none'" | "'self'" | "'unsafe-inline'" | "'unsafe-eval'" | string;
|
declare namespace Session {
|
||||||
|
type CspValue = '*' | "'none'" | "'self'" | "'unsafe-inline'" | "'unsafe-eval'" | string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface represents a content security policy.
|
* This interface represents a content security policy.
|
||||||
*
|
*
|
||||||
* @see https://www.w3.org/TR/CSP/
|
* @see https://www.w3.org/TR/CSP/
|
||||||
* @see https://content-security-policy.com/
|
* @see https://content-security-policy.com/
|
||||||
*/
|
*/
|
||||||
interface IContentSecurityPolicy {
|
type ContentSecurityPolicy = {
|
||||||
'child-src'?: CspValue[];
|
'child-src'?: CspValue[];
|
||||||
'connect-src'?: CspValue[];
|
'connect-src'?: CspValue[];
|
||||||
'default-src'?: CspValue[];
|
'default-src'?: CspValue[];
|
||||||
'font-src'?: CspValue[];
|
'font-src'?: CspValue[];
|
||||||
'frame-src'?: CspValue[];
|
'frame-src'?: CspValue[];
|
||||||
'img-src'?: CspValue[];
|
'img-src'?: CspValue[];
|
||||||
'media-src'?: CspValue[];
|
'media-src'?: CspValue[];
|
||||||
'object-src'?: ["'none'"];
|
'object-src'?: ["'none'"];
|
||||||
'script-src'?: CspValue[];
|
'script-src'?: CspValue[];
|
||||||
'script-src-elem'?: CspValue[];
|
'script-src-elem'?: CspValue[];
|
||||||
'script-src-attr'?: CspValue[];
|
'script-src-attr'?: CspValue[];
|
||||||
'style-src'?: CspValue[];
|
'style-src'?: CspValue[];
|
||||||
'worker-src'?: CspValue[];
|
'worker-src'?: CspValue[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export interface IStore {
|
interface StoreInterface {
|
||||||
load: (key: StoreKey) => Promise<unknown>;
|
load: (key: StoreKey) => Promise<unknown>;
|
||||||
save: (key: StoreKey, data: unknown) => Promise<void>;
|
save: (key: StoreKey, data: unknown) => Promise<void>;
|
||||||
}
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { IStore } from './i-store';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mock store saves the data in memory.
|
* This mock store saves the data in memory.
|
||||||
*/
|
*/
|
||||||
@injectable()
|
@injectable()
|
||||||
export class StoreMock implements IStore {
|
export class StoreMock implements StoreInterface {
|
||||||
private store: { [x in StoreKey]?: unknown } = {};
|
private store: { [x in StoreKey]?: unknown } = {};
|
||||||
|
|
||||||
public load(key: StoreKey): Promise<unknown> {
|
public load(key: StoreKey): Promise<unknown> {
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import { container } from '../../core/container';
|
import { container } from '../../core/container';
|
||||||
import { IStore } from './i-store';
|
|
||||||
|
|
||||||
describe('Store Service', function () {
|
describe('Store Service', function () {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
|
|
||||||
it('loads saved data', () => {
|
it('loads saved data', () => {
|
||||||
const store: IStore = container.get('store');
|
const store: StoreInterface = container.get('store');
|
||||||
const testData = {
|
const testData = {
|
||||||
something: 'gaga',
|
something: 'gaga',
|
||||||
somethingElse: 0,
|
somethingElse: 0,
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { Database, getConnection } from '../../core/database';
|
import { Database, getConnection } from '../../core/database';
|
||||||
import { StoreValue } from '../../entities/store/store-value';
|
import { StoreValue } from '../../entities/store/store-value';
|
||||||
import { IStore } from './i-store';
|
|
||||||
|
|
||||||
const CACHE_ID = 'store';
|
const CACHE_ID = 'store';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class Store implements IStore {
|
export class Store implements StoreInterface {
|
||||||
public async load(key: StoreKey): Promise<unknown> {
|
public async load(key: StoreKey): Promise<unknown> {
|
||||||
const c = await getConnection(Database.STORE);
|
const c = await getConnection(Database.STORE);
|
||||||
const repository = c.getRepository(StoreValue);
|
const repository = c.getRepository(StoreValue);
|
||||||
|
|
|
@ -2,16 +2,16 @@ import { ipcRenderer } from 'electron';
|
||||||
import { uuid } from '../../services/uuid';
|
import { uuid } from '../../services/uuid';
|
||||||
import IpcRendererEvent = Electron.IpcRendererEvent;
|
import IpcRendererEvent = Electron.IpcRendererEvent;
|
||||||
|
|
||||||
const ipcClient: IIpcClient = {
|
const ipcClient: IpcClient = {
|
||||||
ask: (channel: IpcChannel, data?: unknown): Promise<unknown> => {
|
ask: (channel: IpcChannel, data?: unknown): Promise<unknown> => {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
const payload: IIpcPayload = {
|
const payload: IpcPayload = {
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve: (value?: unknown) => void, reject: (reason?: Error) => void): void => {
|
return new Promise((resolve: (value?: unknown) => void, reject: (reason?: Error) => void): void => {
|
||||||
const listener = (event: IpcRendererEvent, response: IIpcResponse): void => {
|
const listener = (event: IpcRendererEvent, response: IpcResponse): void => {
|
||||||
if (response.id === id) {
|
if (response.id === id) {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
resolve(response.data);
|
resolve(response.data);
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"preserveConstEnums": false,
|
"preserveConstEnums": false,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true
|
"emitDecoratorMetadata": true,
|
||||||
|
"importsNotUsedAsValues": "error"
|
||||||
},
|
},
|
||||||
"include": ["declarations/**/*.ts", "types/**/*.ts", "src/main.ts", "src/main/**/*.ts", "src/services/**/*.ts"]
|
"include": ["declarations/**/*.ts", "types/**/*.ts", "src/main.ts", "src/main/**/*.ts", "src/services/**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"preserveConstEnums": false
|
"preserveConstEnums": false,
|
||||||
|
"importsNotUsedAsValues": "error"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"declarations/**/*.ts",
|
"declarations/**/*.ts",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* @see MethodDecorator
|
* @see MethodDecorator
|
||||||
*/
|
*/
|
||||||
type IpcControllerMethodDecorator = <T>(
|
type IpcControllerMethodDecorator = <T>(
|
||||||
target: IIpcController,
|
target: IpcController,
|
||||||
propertyKey: string,
|
propertyKey: string,
|
||||||
descriptor: TypedPropertyDescriptor<T>
|
descriptor: TypedPropertyDescriptor<T>
|
||||||
) => void;
|
) => void;
|
||||||
|
|
|
@ -2,26 +2,25 @@ declare const enum IpcChannel {
|
||||||
NHENTAI_SAVE_FAVORITES = 'NHENTAI_SAVE_FAVORITES',
|
NHENTAI_SAVE_FAVORITES = 'NHENTAI_SAVE_FAVORITES',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IIpcPayload {
|
type IpcPayload = {
|
||||||
id: string;
|
id: string;
|
||||||
data: unknown;
|
data: unknown;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface IIpcResponse {
|
type IpcResponse = {
|
||||||
id: string;
|
id: string;
|
||||||
success: boolean;
|
success: boolean;
|
||||||
data?: unknown;
|
data?: unknown;
|
||||||
// just the error message
|
// just the error message
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface IIpcClient {
|
type IpcClient = {
|
||||||
ask: (channel: IpcChannel, data?: unknown) => Promise<unknown>;
|
ask: (channel: IpcChannel, data?: unknown) => Promise<unknown>;
|
||||||
}
|
};
|
||||||
|
|
||||||
type IpcHandler = (data?: unknown) => Promise<unknown>;
|
type IpcHandler = (data?: unknown) => Promise<unknown>;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- https://github.com/typescript-eslint/typescript-eslint/issues/2714
|
type IpcController = {
|
||||||
interface IIpcController {
|
get(): IpcController;
|
||||||
get(): IIpcController;
|
};
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue