feat: implement entity API and reactive store-like modules in frontend

Also does a bunch of other stuff.
This commit is contained in:
Xymorot 2021-01-30 22:34:36 +01:00
parent 55a5047328
commit 4bb6e5c166
221 changed files with 20203 additions and 6051 deletions

View File

@ -11,3 +11,6 @@ charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.handlebars]
insert_final_newline = false

View File

@ -4,6 +4,7 @@
# except these file types
!*.js
!*.ts
!*.svelte
# but ignore stuff from .gitignore
node_modules

View File

@ -1,9 +1,9 @@
{
"root": true,
"plugins": ["@typescript-eslint", "import"],
"extends": ["eslint:recommended", "prettier", "plugin:import/recommended"],
"plugins": ["@typescript-eslint", "import", "promise", "svelte3"],
"extends": ["eslint:recommended", "prettier", "plugin:import/recommended", "plugin:promise/recommended"],
"parserOptions": {
"ecmaVersion": 2019,
"ecmaVersion": 2020,
"sourceType": "module"
},
"env": {
@ -91,16 +91,24 @@
"order": "asc"
}
}
]
],
"promise/valid-params": "off",
"promise/always-return": "off"
},
"overrides": [
{
"files": ["src/**/*.*"],
"rules": {
"no-console": "warn"
}
},
{
"files": ["**/*.ts"],
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier/@typescript-eslint",
"plugin:import/typescript",
"plugin:import/electron"
],
@ -115,7 +123,6 @@
}
},
"rules": {
"no-console": "warn",
"no-magic-numbers": "off",
"no-shadow": "off",
@ -183,7 +190,7 @@
{
"selector": "interface",
"format": ["PascalCase"],
"suffix": ["Interface"]
"suffix": ["Interface", "Function"]
}
],
"@typescript-eslint/explicit-member-accessibility": "warn",
@ -198,7 +205,10 @@
"allowNullish": false
}
],
"@typescript-eslint/no-shadow": ["warn"]
"@typescript-eslint/no-shadow": ["warn"],
"@typescript-eslint/no-extra-semi": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/consistent-type-assertions": ["warn"]
}
},
{
@ -207,6 +217,10 @@
"project": "./tsconfig.renderer.json"
}
},
{
"files": ["**/*.svelte"],
"processor": "svelte3/svelte3"
},
{
"files": ["src/shared/types/**/*"],
"rules": {

1
.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

4
.husky/pre-commit Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run precommit

4
.husky/pre-push Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run prepush

View File

@ -2,7 +2,6 @@
*.*
# except these file types
!*.html
!*.handlebars
!*.json
!*.yml

View File

@ -1,4 +1,4 @@
trailingComma: 'es5'
trailingComma: all
tabWidth: 2
semi: true
singleQuote: true
@ -6,13 +6,10 @@ quoteProps: consistent
arrowParens: always
printWidth: 120
endOfLine: lf
svelteStrictMode: true
overrides:
- files: '*.svelte'
options:
parser: html
- files: '*.handlebars'
options:
parser: html
- files: '*.drawio'
options:
parser: html
parser: xml

View File

@ -11,6 +11,8 @@ Nothing in this project is set in stone (even this rule). I might not know what
1. [Formatting and Linters](CONTRIBUTING.md#formatting-and-linters)
1. [Testing](CONTRIBUTING.md#testing)
1. [Updating](CONTRIBUTING.md#updating-dependencies)
1. [Frontend](CONTRIBUTING.md#frontend)
1. [Translation](CONTRIBUTING.md#translation)
1. [Design](CONTRIBUTING.md#design)
## Currently Most Wanted
@ -45,6 +47,10 @@ The [ideas folder](workspace/ideas) is a collection of possible features for the
I could use another developer (experienced or not) to take a look or two at this code base and voice their opinion.
#### LF: TypeScript Magician
Do a search over the whole code-base for `@ts-ignore` or ` as [A-Z]` (regex for type assertions) and try to fix my typings. Good luck!
## Communication
Every contribution is valuable. Open a ticket [here](https://git.fuwafuwa.moe/Xymorot/RenaiApp/issues) or write me an [email](mailto:xymorot@mailbox.org).
@ -141,7 +147,7 @@ The point of these libraries is to make uniform code possible over various edito
The testing framework of choice is [Mocha](https://mochajs.org/). Call `npm run test` to run all tests. Tests are written in typescript and need to be transpiled before testing.
- assertion is (mainly) done by [Chai](https://www.chaijs.com/)should not be compromised
- assertion is (mainly) done by [Chai](https://www.chaijs.com/)
- spies, stubs and mocks are provided by [Sinon.JS](https://sinonjs.org/)
- HTTP server mocking is done by [nock](https://github.com/nock/nock)
- property based testing is made possible by [fast-check](https://github.com/dubzzz/fast-check)
@ -182,6 +188,15 @@ This also means potentially updating:
- the `@types/node` package
- the electron version in the `target` field of the [webpack config](scripts/webpack.config.js)
- the `compilerOptions.target` fields of the [main](tsconfig.json) and [renderer](tsconfig.renderer.json) typescript configuration files
- the `parserOptions.ecmaVersion` field in the [ESLint configuration](.eslintrc.json)
Also might be worth checking out https://github.com/electron/electron/issues/21457 if the project can be converted to transpile to ES Modules.
### Frontend
#### Translation
With the help of [intl-messageformat](https://www.npmjs.com/package/intl-messageformat) frontend message can be defined in the [ICU Message Format](https://unicode-org.github.io/icu/userguide/format_parse/messages/). The corresponding code can be found [here](src/shared/services/translation/t.ts). I don't think a language other than English will be necessary, but the formatting features are nice anyway.
## Design

View File

@ -1,5 +0,0 @@
declare module '*.svelte' {
import { SvelteComponentTyped } from 'svelte';
// noinspection JSUnusedGlobalSymbols -- because it is used
export class Component extends SvelteComponentTyped {}
}

19913
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,9 +11,11 @@
},
"main": "src/main.js",
"engines": {
"node": "12"
"node": "14",
"npm": "7"
},
"scripts": {
"prepare": "husky install",
"postinstall": "npm run rebuild",
"postupdate": "npm run rebuild",
"start": "electron . --enable-logging --env=dev",
@ -45,69 +47,68 @@
"prepush": "npm run build && npm run coverage"
},
"dependencies": {
"better-sqlite3": "^7.1.2",
"better-sqlite3": "^7.4.0",
"electron-squirrel-startup": "^1.0.0",
"fs-extra": "^9.1.0",
"inversify": "^5.0.1",
"fs-extra": "^10.0.0",
"intl-messageformat": "^9.6.14",
"inversify": "^5.1.1",
"minimist": "^1.2.5",
"reflect-metadata": "^0.1.13",
"typeorm": "^0.2.30",
"typeorm": "^0.2.32",
"uuid": "^8.3.2"
},
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.54",
"@electron-forge/maker-squirrel": "^6.0.0-beta.54",
"@electron-forge/cli": "^6.0.0-beta.55",
"@electron-forge/maker-squirrel": "^6.0.0-beta.55",
"@prettier/plugin-xml": "^0.13.1",
"@types/better-sqlite3": "^5.4.1",
"@types/chai": "^4.2.14",
"@types/chai": "^4.2.18",
"@types/chai-fs": "^2.0.2",
"@types/deep-equal-in-any-order": "^1.0.1",
"@types/fs-extra": "^9.0.6",
"@types/fs-extra": "^9.0.11",
"@types/glob": "^7.1.3",
"@types/minimist": "^1.2.1",
"@types/mocha": "^8.2.0",
"@types/node": "^12.19.15",
"@types/sinon": "^9.0.10",
"@types/mocha": "^8.2.2",
"@types/node": "^14.17.0",
"@types/sinon": "^10.0.0",
"@types/uuid": "^8.3.0",
"@types/webpack": "^4.41.26",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
"chai": "^4.2.0",
"@types/webpack": "^5.28.0",
"@typescript-eslint/eslint-plugin": "^4.24.0",
"@typescript-eslint/parser": "^4.24.0",
"chai": "^4.3.4",
"chai-fs": "^2.0.0",
"chokidar": "^3.5.1",
"concurrently": "^5.3.0",
"deep-equal-in-any-order": "^1.0.28",
"electron": "^10.3.0",
"concurrently": "^6.1.0",
"deep-equal-in-any-order": "^1.1.4",
"electron": "^13.0.1",
"electron-mocha": "^10.0.0",
"electron-rebuild": "^2.3.4",
"eslint": "^7.18.0",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-import": "^2.22.1",
"fast-check": "^2.12.0",
"glob": "^7.1.6",
"handlebars": "^4.7.6",
"husky": "^4.3.8",
"lodash": "^4.17.20",
"mocha": "^8.2.1",
"nock": "^13.0.6",
"electron-rebuild": "^2.3.5",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.2",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-svelte3": "^3.2.0",
"fast-check": "^2.14.0",
"glob": "^7.1.7",
"handlebars": "^4.7.7",
"husky": "^6.0.0",
"lodash": "^4.17.21",
"mocha": "^8.4.0",
"nock": "^13.0.11",
"nyc": "^15.1.0",
"prettier": "^2.2.1",
"sinon": "^9.2.4",
"svelte": "^3.31.2",
"svelte-loader": "^3.0.0",
"ts-loader": "^8.0.14",
"typescript": "^4.1.3",
"webpack": "^5.17.0",
"webpack-cli": "^4.4.0"
"prettier": "^2.3.0",
"prettier-plugin-svelte": "^2.3.0",
"sinon": "^11.1.1",
"svelte": "^3.38.2",
"svelte-loader": "^3.1.1",
"ts-loader": "^9.2.0",
"typescript": "^4.2.4",
"webpack": "^5.37.1",
"webpack-cli": "^4.7.0"
},
"repository": "https://git.fuwafuwa.moe/Xymorot/RenaiApp",
"bugs": "https://git.fuwafuwa.moe/Xymorot/RenaiApp/issues",
"config": {
"forge": "scripts/forge.config.js"
},
"husky": {
"hooks": {
"pre-commit": "npm run precommit",
"pre-push": "npm run prepush"
}
}
}

View File

@ -39,7 +39,7 @@ const name = packageJson.productName;
if (name !== 'Renai') {
throw new TypeError(
`The product name "${name}" in package.json is not "Renai"! Change it before building but do not commit it.`
`The product name "${name}" in package.json is not "Renai"! Change it before building but do not commit it.`,
);
}

View File

@ -3,7 +3,7 @@ const path = require('path');
module.exports = {
mode: 'production',
entry: path.resolve(__dirname, '../src/renderer.ts'),
target: 'electron10-renderer',
target: 'electron13-renderer',
output: {
path: path.resolve(__dirname, '../frontend'),
filename: 'bundle.js',
@ -21,14 +21,9 @@ module.exports = {
configFile: path.resolve('tsconfig.renderer.json'),
},
},
{
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto',
},
],
},
resolve: {
extensions: ['.ts', '.js', '.mjs'],
extensions: ['.ts', '.js'],
},
};

View File

@ -1,23 +1,22 @@
/* eslint-disable import/order -- this is the entry point for the application and some things have to happen before others */
import { container } from './main/core/container';
import './main/core/install';
import { app } from 'electron';
import { container, Service } from './main/core/container';
import './main/core/controller';
import { isDev } from './main/core/env';
import type { AppWindowInterface } from './main/modules/app-window/app-window-interface';
import './main/core/install';
/**
* 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
*/
process.on('unhandledRejection', (reason) => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
void logger.fatal(`Unhandled Rejection, see ${logger.getExceptionsLogFile()}`);
throw reason;
});
process.on('uncaughtException', (error) => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
void logger.exception(error);
if (isDev()) {
// eslint-disable-next-line no-console -- only for development purposes
@ -26,7 +25,7 @@ process.on('uncaughtException', (error) => {
});
async function createWindow(): Promise<void> {
const appWindowMain: AppWindowInterface = container.get('app-window-main');
const appWindowMain = container.get(Service.APP_WINDOW_MAIN);
await appWindowMain.open();
appWindowMain.window?.on('closed', () => {
@ -49,7 +48,7 @@ app.on('window-all-closed', () => {
app.on('activate', () => {
// 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.
const appWindowMain: AppWindowInterface = container.get('app-window-main');
const appWindowMain = container.get(Service.APP_WINDOW_MAIN);
if (appWindowMain.isClosed()) {
void createWindow();
}

View File

@ -1,40 +1,185 @@
import 'reflect-metadata';
import { Container, interfaces } from 'inversify';
import type { AppWindowInterface } from '../modules/app-window/app-window-interface';
import { MainAppWindow } from '../modules/app-window/main-app-window';
import { Dialog } from '../modules/dialog/dialog';
import { I18nTranslator } from '../modules/i18n/i18n-translator';
import type { DialogInterface } from '../modules/dialog/dialog-interface';
import { Logger } from '../modules/logger/logger';
import { NhentaiApi } from '../modules/nhentai/nhentai-api';
import '../modules/nhentai/nhentai-ipc-controller';
import { NhentaiAppWindow } from '../modules/nhentai/nhentai-app-window';
import type { NhentaiAppWindowInterface } from '../modules/nhentai/nhentai-app-window-interface';
import { NhentaiSourceGetter } from '../modules/nhentai/nhentai-source-getter';
import { AuthorNameSerializer } from '../modules/serialization/serializers/author-name-serializer';
import { AuthorRoleNameSerializer } from '../modules/serialization/serializers/author-role-name-serializer';
import { AuthorRoleSerializer } from '../modules/serialization/serializers/author-role-serializer';
import { AuthorSerializer } from '../modules/serialization/serializers/author-serializer';
import { CharacterTagSerializer } from '../modules/serialization/serializers/character-tag-serializer';
import { CollectionNameSerializer } from '../modules/serialization/serializers/collection-name-serializer';
import { CollectionPartSerializer } from '../modules/serialization/serializers/collection-part-serializer';
import { CollectionSerializer } from '../modules/serialization/serializers/collection-serializer';
import { CopySerializer } from '../modules/serialization/serializers/copy-serializer';
import { InteractionTagSerializer } from '../modules/serialization/serializers/interaction-tag-serializer';
import { LanguageSerializer } from '../modules/serialization/serializers/language-serializer';
import { SiteNameSerializer } from '../modules/serialization/serializers/site-name-serializer';
import { SiteSerializer } from '../modules/serialization/serializers/site-serializer';
import { SourceSerializer } from '../modules/serialization/serializers/source-serializer';
import { TagNameSerializer } from '../modules/serialization/serializers/tag-name-serializer';
import { TagSerializer } from '../modules/serialization/serializers/tag-serializer';
import { TransformationSerializer } from '../modules/serialization/serializers/transformation-serializer';
import { TransformationTypeNameSerializer } from '../modules/serialization/serializers/transformation-type-name-serializer';
import { TransformationTypeSerializer } from '../modules/serialization/serializers/transformation-type-serializer';
import { WorkAuthorSerializer } from '../modules/serialization/serializers/work-author-serializer';
import { WorkCharacterNameSerializer } from '../modules/serialization/serializers/work-character-name-serializer';
import { WorkCharacterSerializer } from '../modules/serialization/serializers/work-character-serializer';
import { WorkNameSerializer } from '../modules/serialization/serializers/work-name-serializer';
import { WorkSerializer } from '../modules/serialization/serializers/work-serializer';
import { WorkTagSerializer } from '../modules/serialization/serializers/work-tag-serializer';
import { WorldCharacterNameSerializer } from '../modules/serialization/serializers/world-character-name-serializer';
import { WorldCharacterSerializer } from '../modules/serialization/serializers/world-character-serializer';
import { WorldNameSerializer } from '../modules/serialization/serializers/world-name-serializer';
import { WorldSerializer } from '../modules/serialization/serializers/world-serializer';
import type { SourceGetterInterface } from '../modules/source/source-getter-interface';
import { Store } from '../modules/store/store';
import '../modules/entity-api/entity-api-ipc-controller';
import BindingToSyntax = interfaces.BindingToSyntax;
export const enum Service {
LOGGER = 'LOGGER',
AUTHOR_SERIALIZER = 'AUTHOR_SERIALIZER',
AUTHOR_NAME_SERIALIZER = 'AUTHOR_NAME_SERIALIZER',
AUTHOR_ROLE_SERIALIZER = 'AUTHOR_ROLE_SERIALIZER',
AUTHOR_ROLE_NAME_SERIALIZER = 'AUTHOR_ROLE_NAME_SERIALIZER',
CHARACTER_TAG_SERIALIZER = 'CHARACTER_TAG_SERIALIZER',
COLLECTION_SERIALIZER = 'COLLECTION_SERIALIZER',
COLLECTION_NAME_SERIALIZER = 'COLLECTION_NAME_SERIALIZER',
COLLECTION_PART_SERIALIZER = 'COLLECTION_PART_SERIALIZER',
COPY_SERIALIZER = 'COPY_SERIALIZER',
INTERACTION_TAG_SERIALIZER = 'INTERACTION_TAG_SERIALIZER',
LANGUAGE_SERIALIZER = 'LANGUAGE_SERIALIZER',
SITE_SERIALIZER = 'SITE_SERIALIZER',
SITE_NAME_SERIALIZER = 'SITE_NAME_SERIALIZER',
SOURCE_SERIALIZER = 'SOURCE_SERIALIZER',
TAG_SERIALIZER = 'TAG_SERIALIZER',
TAG_NAME_SERIALIZER = 'TAG_NAME_SERIALIZER',
TRANSFORMATION_SERIALIZER = 'TRANSFORMATION_SERIALIZER',
TRANSFORMATION_TYPE_SERIALIZER = 'TRANSFORMATION_TYPE_SERIALIZER',
TRANSFORMATION_TYPE_NAME_SERIALIZER = 'TRANSFORMATION_TYPE_NAME_SERIALIZER',
WORK_AUTHOR_SERIALIZER = 'WORK_AUTHOR_SERIALIZER',
WORK_CHARACTER_SERIALIZER = 'WORK_CHARACTER_SERIALIZER',
WORK_CHARACTER_NAME_SERIALIZER = 'WORK_CHARACTER_NAME_SERIALIZER',
WORK_SERIALIZER = 'WORK_SERIALIZER',
WORK_NAME_SERIALIZER = 'WORK_NAME_SERIALIZER',
WORK_TAG_SERIALIZER = 'WORK_TAG_SERIALIZER',
WORLD_SERIALIZER = 'WORLD_SERIALIZER',
WORLD_NAME_SERIALIZER = 'WORLD_NAME_SERIALIZER',
WORLD_CHARACTER_SERIALIZER = 'WORLD_CHARACTER_SERIALIZER',
WORLD_CHARACTER_NAME_SERIALIZER = 'WORLD_CHARACTER_NAME_SERIALIZER',
DIALOG = 'DIALOG',
STORE = 'STORE',
NHENTAI_APP_WINDOW = 'NHENTAI_APP_WINDOW',
NHENTAI_API = 'NHENTAI_API',
NHENTAI_SOURCE_GETTER = 'NHENTAI_SOURCE_GETTER',
APP_WINDOW_MAIN = 'APP_WINDOW_MAIN',
}
type ServiceType = {
[Service.LOGGER]: LoggerInterface;
[Service.AUTHOR_SERIALIZER]: AuthorSerializer;
[Service.AUTHOR_NAME_SERIALIZER]: AuthorNameSerializer;
[Service.AUTHOR_ROLE_SERIALIZER]: AuthorRoleSerializer;
[Service.AUTHOR_ROLE_NAME_SERIALIZER]: AuthorRoleNameSerializer;
[Service.CHARACTER_TAG_SERIALIZER]: CharacterTagSerializer;
[Service.COLLECTION_SERIALIZER]: CollectionSerializer;
[Service.COLLECTION_NAME_SERIALIZER]: CollectionNameSerializer;
[Service.COLLECTION_PART_SERIALIZER]: CollectionPartSerializer;
[Service.COPY_SERIALIZER]: CopySerializer;
[Service.INTERACTION_TAG_SERIALIZER]: InteractionTagSerializer;
[Service.LANGUAGE_SERIALIZER]: LanguageSerializer;
[Service.SITE_SERIALIZER]: SiteSerializer;
[Service.SITE_NAME_SERIALIZER]: SiteNameSerializer;
[Service.SOURCE_SERIALIZER]: SourceSerializer;
[Service.TAG_SERIALIZER]: TagSerializer;
[Service.TAG_NAME_SERIALIZER]: TagNameSerializer;
[Service.TRANSFORMATION_SERIALIZER]: TransformationSerializer;
[Service.TRANSFORMATION_TYPE_SERIALIZER]: TransformationTypeSerializer;
[Service.TRANSFORMATION_TYPE_NAME_SERIALIZER]: TransformationTypeNameSerializer;
[Service.WORK_AUTHOR_SERIALIZER]: WorkAuthorSerializer;
[Service.WORK_CHARACTER_SERIALIZER]: WorkCharacterSerializer;
[Service.WORK_CHARACTER_NAME_SERIALIZER]: WorkCharacterNameSerializer;
[Service.WORK_SERIALIZER]: WorkSerializer;
[Service.WORK_NAME_SERIALIZER]: WorkNameSerializer;
[Service.WORK_TAG_SERIALIZER]: WorkTagSerializer;
[Service.WORLD_SERIALIZER]: WorldSerializer;
[Service.WORLD_NAME_SERIALIZER]: WorldNameSerializer;
[Service.WORLD_CHARACTER_SERIALIZER]: WorldCharacterSerializer;
[Service.WORLD_CHARACTER_NAME_SERIALIZER]: WorldCharacterNameSerializer;
[Service.DIALOG]: DialogInterface;
[Service.STORE]: StoreInterface;
[Service.NHENTAI_APP_WINDOW]: NhentaiAppWindowInterface;
[Service.NHENTAI_API]: NhentaiApiInterface;
[Service.NHENTAI_SOURCE_GETTER]: SourceGetterInterface;
[Service.APP_WINDOW_MAIN]: AppWindowInterface;
};
export const container = {
original: new Container({ defaultScope: 'Singleton', skipBaseClassChecks: true }),
bind<T>(key: string): BindingToSyntax<T> {
return this.original.bind<T>(Symbol.for(key));
bind<S extends Service>(key: S): BindingToSyntax<ServiceType[S]> {
return this.original.bind<ServiceType[S]>(Symbol.for(key));
},
unbind(key: string): void {
unbind(key: Service): void {
return this.original.unbind(Symbol.for(key));
},
get<T>(key: string): T {
return this.original.get<T>(Symbol.for(key));
get<S extends Service>(key: S): ServiceType[S] {
return this.original.get<ServiceType[S]>(Symbol.for(key));
},
};
container.bind('logger').to(Logger);
container.bind(Service.LOGGER).to(Logger);
container.bind('i18n-translator').to(I18nTranslator);
container.bind(Service.AUTHOR_SERIALIZER).to(AuthorSerializer);
container.bind(Service.AUTHOR_NAME_SERIALIZER).to(AuthorNameSerializer);
container.bind(Service.AUTHOR_ROLE_SERIALIZER).to(AuthorRoleSerializer);
container.bind(Service.AUTHOR_ROLE_NAME_SERIALIZER).to(AuthorRoleNameSerializer);
container.bind(Service.CHARACTER_TAG_SERIALIZER).to(CharacterTagSerializer);
container.bind(Service.COLLECTION_SERIALIZER).to(CollectionSerializer);
container.bind(Service.COLLECTION_NAME_SERIALIZER).to(CollectionNameSerializer);
container.bind(Service.COLLECTION_PART_SERIALIZER).to(CollectionPartSerializer);
container.bind(Service.COPY_SERIALIZER).to(CopySerializer);
container.bind(Service.INTERACTION_TAG_SERIALIZER).to(InteractionTagSerializer);
container.bind(Service.LANGUAGE_SERIALIZER).to(LanguageSerializer);
container.bind(Service.SITE_SERIALIZER).to(SiteSerializer);
container.bind(Service.SITE_NAME_SERIALIZER).to(SiteNameSerializer);
container.bind(Service.SOURCE_SERIALIZER).to(SourceSerializer);
container.bind(Service.TAG_SERIALIZER).to(TagSerializer);
container.bind(Service.TAG_NAME_SERIALIZER).to(TagNameSerializer);
container.bind(Service.TRANSFORMATION_SERIALIZER).to(TransformationSerializer);
container.bind(Service.TRANSFORMATION_TYPE_SERIALIZER).to(TransformationTypeSerializer);
container.bind(Service.TRANSFORMATION_TYPE_NAME_SERIALIZER).to(TransformationTypeNameSerializer);
container.bind(Service.WORK_AUTHOR_SERIALIZER).to(WorkAuthorSerializer);
container.bind(Service.WORK_CHARACTER_SERIALIZER).to(WorkCharacterSerializer);
container.bind(Service.WORK_CHARACTER_NAME_SERIALIZER).to(WorkCharacterNameSerializer);
container.bind(Service.WORK_SERIALIZER).to(WorkSerializer);
container.bind(Service.WORK_NAME_SERIALIZER).to(WorkNameSerializer);
container.bind(Service.WORK_TAG_SERIALIZER).to(WorkTagSerializer);
container.bind(Service.WORLD_SERIALIZER).to(WorldSerializer);
container.bind(Service.WORLD_NAME_SERIALIZER).to(WorldNameSerializer);
container.bind(Service.WORLD_CHARACTER_SERIALIZER).to(WorldCharacterSerializer);
container.bind(Service.WORLD_CHARACTER_NAME_SERIALIZER).to(WorldCharacterNameSerializer);
container.bind('dialog').to(Dialog);
container.bind(Service.DIALOG).to(Dialog);
container.bind('store').to(Store);
container.bind(Service.STORE).to(Store);
container.bind('nhentai-app-window').to(NhentaiAppWindow);
container.bind('nhentai-api').to(NhentaiApi);
container.bind('nhentai-source-getter').to(NhentaiSourceGetter);
container.bind(Service.NHENTAI_APP_WINDOW).to(NhentaiAppWindow);
container.bind(Service.NHENTAI_API).to(NhentaiApi);
container.bind(Service.NHENTAI_SOURCE_GETTER).to(NhentaiSourceGetter);
container.bind('app-window-main').to(MainAppWindow);
container.bind(Service.APP_WINDOW_MAIN).to(MainAppWindow);

View File

@ -0,0 +1,2 @@
import '../modules/nhentai/nhentai-ipc-controller';
import '../modules/entity-api/entity-api-ipc-controller';

View File

@ -1,5 +1,5 @@
import path from 'path';
import { Connection, createConnection as ormCreateConnection } from 'typeorm';
import { Connection, createConnection as ormCreateConnection, EntityManager, EntityTarget, Repository } from 'typeorm';
import type { BetterSqlite3ConnectionOptions } from 'typeorm/driver/better-sqlite3/BetterSqlite3ConnectionOptions';
import { appPath } from './app-path';
@ -50,5 +50,16 @@ const connections = {
};
export function getConnection(database: Database): Promise<Connection> {
return Promise.resolve(connections[database]);
return connections[database];
}
export function getManager(database: Database): Promise<EntityManager> {
return getConnection(database).then((c) => c.manager);
}
export function getRepository<T>(
target: EntityTarget<T>,
database: Database = Database.LIBRARY,
): Promise<Repository<T>> {
return getManager(database).then((m) => m.getRepository(target));
}

View File

@ -1,5 +1,6 @@
import { inject as inversifyInject } from 'inversify';
import type { Service } from './container';
export function inject(key: string): ReturnType<typeof inversifyInject> {
export function inject(key: Service): ReturnType<typeof inversifyInject> {
return inversifyInject(Symbol.for(key));
}

View File

@ -9,6 +9,6 @@ export const maxValue = Number.MAX_SAFE_INTEGER;
export function PercentCheck(column: string): ClassDecorator & PropertyDecorator {
return Check(
`${column} needs to be between ${minValue} and ${maxValue}`,
`${column} >= ${minValue} AND ${column} <= ${maxValue}`
`${column} >= ${minValue} AND ${column} <= ${maxValue}`,
);
}

View File

@ -16,6 +16,6 @@ export class Author implements AuthorEntityInterface {
@OneToMany(() => AuthorName, (authorName: AuthorNameEntityInterface) => authorName.entity)
public names!: Promise<AuthorNameEntityInterface[]>;
@OneToMany(() => WorkAuthor, (workAuthor: WorkAuthorEntityInterface) => workAuthor.author, {})
@OneToMany(() => WorkAuthor, (workAuthor: WorkAuthorEntityInterface) => workAuthor.author)
public workAuthors!: Promise<WorkAuthorEntityInterface[]>;
}

View File

@ -4,8 +4,12 @@ import { Work } from './work';
@Entity()
export class Language implements LanguageEntityInterface {
@PrimaryColumn()
public code!: string;
public readonly code!: LangCode;
@ManyToMany(() => Work, (work: WorkEntityInterface) => work.languages)
public works!: Promise<WorkEntityInterface[]>;
public get id(): LangCode {
return this.code;
}
}

View File

@ -13,7 +13,7 @@ export class TransformationTypeName implements TransformationTypeNameEntityInter
nullable: false,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
}
},
)
public entity!: Promise<TransformationTypeEntityInterface>;

View File

@ -16,7 +16,6 @@ export class TransformationType implements TransformationTypeEntityInterface {
@OneToMany(
() => TransformationTypeName,
(transformationTypeName: TransformationTypeNameEntityInterface) => transformationTypeName.entity,
{}
)
public names!: Promise<TransformationTypeNameEntityInterface[]>;

View File

@ -21,7 +21,7 @@ export class Transformation implements TransformationEntityInterface {
nullable: false,
onDelete: 'RESTRICT',
onUpdate: 'CASCADE',
}
},
)
public type!: Promise<TransformationTypeEntityInterface>;

View File

@ -25,7 +25,7 @@ export class Work implements WorkEntityInterface {
@OneToMany(() => WorkName, (workName: WorkNameEntityInterface) => workName.entity)
public names!: Promise<WorkNameEntityInterface[]>;
@OneToMany(() => Copy, (copy: CopyEntityInterface) => copy.original, {})
@OneToMany(() => Copy, (copy: CopyEntityInterface) => copy.original)
public copies!: Promise<CopyEntityInterface[]>;
@OneToMany(() => Transformation, (transformation: TransformationEntityInterface) => transformation.byWork)

View File

@ -16,7 +16,7 @@ export class WorldCharacter implements WorldCharacterEntityInterface {
@OneToMany(
() => WorldCharacterName,
(worldCharacterName: WorldCharacterNameEntityInterface) => worldCharacterName.entity
(worldCharacterName: WorldCharacterNameEntityInterface) => worldCharacterName.entity,
)
public names!: Promise<WorldCharacterNameEntityInterface[]>;

File diff suppressed because it is too large Load Diff

View File

@ -65,7 +65,7 @@ export class addLanguages1611508644004 implements MigrationInterface {
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.HIRI_MOTU}')`);
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.HUNGARIAN}')`);
await queryRunner.query(
`INSERT INTO language VALUES('${LangCode.INTERLINGUA_INTERNATIONAL_AUXILIARY_LANGUAGE_ASSOCIATION}')`
`INSERT INTO language VALUES('${LangCode.INTERLINGUA_INTERNATIONAL_AUXILIARY_LANGUAGE_ASSOCIATION}')`,
);
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.INDONESIAN}')`);
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.INTERLINGUE_OCCIDENTAL}')`);
@ -124,7 +124,7 @@ export class addLanguages1611508644004 implements MigrationInterface {
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.OCCITAN}')`);
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.OJIBWA}')`);
await queryRunner.query(
`INSERT INTO language VALUES('${LangCode.CHURCH_SLAVIC_OLD_SLAVONIC_CHURCH_SLAVONIC_OLD_BULGARIAN_OLD_CHURCH_SLAVONIC}')`
`INSERT INTO language VALUES('${LangCode.CHURCH_SLAVIC_OLD_SLAVONIC_CHURCH_SLAVONIC_OLD_BULGARIAN_OLD_CHURCH_SLAVONIC}')`,
);
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.OROMO}')`);
await queryRunner.query(`INSERT INTO language VALUES('${LangCode.ORIYA}')`);

View File

@ -6,11 +6,11 @@ export class initialMigration1587511036078 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "store_value" ("key" varchar PRIMARY KEY NOT NULL, "value" text NOT NULL)`,
undefined
undefined,
);
await queryRunner.query(
`CREATE TABLE "query-result-cache" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "identifier" varchar, "time" bigint NOT NULL, "duration" integer NOT NULL, "query" text NOT NULL, "result" text NOT NULL)`,
undefined
undefined,
);
}

View File

@ -142,7 +142,7 @@ export abstract class AppWindow implements AppWindowInterface {
})
.catch(() => {
void this.logger.warning(
`Could not get the of the presumed HTMLTimeElement with the selector '${selector}'.`
`Could not get the of the presumed HTMLTimeElement with the selector '${selector}'.`,
);
resolve(undefined);
});

View File

@ -8,7 +8,7 @@ export abstract class FileAppWindow extends AppWindow {
logger: LoggerInterface,
uri: string,
options: BrowserWindowConstructorOptions = {},
loadOptions: LoadFileOptions = {}
loadOptions: LoadFileOptions = {},
) {
super(logger, uri, options);
this.loadOptions = loadOptions;

View File

@ -1,13 +1,15 @@
import { injectable } from 'inversify';
import { Service } from '../../core/container';
import { inject } from '../../core/inject';
import { FileAppWindow } from './file-app-window';
@injectable()
export class MainAppWindow extends FileAppWindow {
public constructor(@inject('logger') logger: LoggerInterface) {
public constructor(@inject(Service.LOGGER) logger: LoggerInterface) {
super(logger, 'frontend/index.html', {
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
}

View File

@ -14,7 +14,7 @@ export abstract class SiteAppWindow extends UrlAppWindow implements SiteAppWindo
logger: LoggerInterface,
uri: string,
options: BrowserWindowConstructorOptions = {},
loadOptions: LoadURLOptions = {}
loadOptions: LoadURLOptions = {},
) {
super(logger, uri, options, loadOptions);
this.windowLock = new SimpleMutex();

View File

@ -16,6 +16,6 @@ interface UrlAppWindowInterface extends AppWindowInterface {
loadUrlSafe(
url: string,
readyCheck?: (webContents: WebContents) => Promise<boolean>,
options?: LoadURLOptions
options?: LoadURLOptions,
): Promise<void>;
}

View File

@ -35,7 +35,7 @@ export abstract class UrlAppWindow extends AppWindow implements UrlAppWindowInte
logger: LoggerInterface,
uri: string,
options: BrowserWindowConstructorOptions = {},
loadOptions: LoadURLOptions = {}
loadOptions: LoadURLOptions = {},
) {
super(logger, uri, {
...options,
@ -78,36 +78,38 @@ export abstract class UrlAppWindow extends AppWindow implements UrlAppWindowInte
public async loadUrlSafe(
url: string,
readyCheck?: (webContents: WebContents) => Promise<boolean>,
options?: LoadURLOptions
options?: LoadURLOptions,
): Promise<void> {
return this.loadWait.then(async () => {
let failedLoad = true;
while (failedLoad) {
await this.loadUrl(url, options).then((httpResponseCode) => {
failedLoad = HttpCode.BAD_REQUEST <= httpResponseCode;
if (HttpCode.TOO_MANY_REQUESTS === httpResponseCode) {
// go slower
this.loadWaitTime += this.loadWaitTimeStep;
// but go faster again after a time
clearTimeout(this.loadWaitTimeStepResetTimeout);
this.loadWaitTimeStepResetTimeout = setTimeout(() => {
this.loadWaitTime = 0;
}, this.loadWaitTimeResetTimeoutTime);
}
});
if (failedLoad) {
await promisify(setTimeout)(this.waitInterval);
}
await this.loadWait;
let failedLoad = true;
while (failedLoad) {
const httpResponseCode = await this.loadUrl(url, options);
failedLoad = HttpCode.BAD_REQUEST <= httpResponseCode;
switch (httpResponseCode) {
case HttpCode.NOT_FOUND:
throw new Error(`Loading ${url} gave the unrecoverable HTTP status code ${httpResponseCode}.`);
case HttpCode.TOO_MANY_REQUESTS:
// go slower
this.loadWaitTime += this.loadWaitTimeStep;
// but go faster again after a time
clearTimeout(this.loadWaitTimeStepResetTimeout);
this.loadWaitTimeStepResetTimeout = setTimeout(() => {
this.loadWaitTime = 0;
}, this.loadWaitTimeResetTimeoutTime);
break;
}
this.loadWait = promisify(setTimeout)(this.loadWaitTime);
if (readyCheck) {
let isReady = await readyCheck(this.getWindow().webContents);
do {
await promisify(setTimeout)(Milliseconds.TEN);
isReady = await readyCheck(this.getWindow().webContents);
} while (!isReady);
if (failedLoad) {
await promisify(setTimeout)(this.waitInterval);
}
});
}
this.loadWait = promisify(setTimeout)(this.loadWaitTime);
if (readyCheck) {
let isReady = await readyCheck(this.getWindow().webContents);
do {
await promisify(setTimeout)(Milliseconds.TEN);
isReady = await readyCheck(this.getWindow().webContents);
} while (!isReady);
}
}
/**

View File

@ -9,7 +9,7 @@ export abstract class CloudflareSiteAppWindow extends SiteAppWindow {
const onDidNavigate: (event: Event, url: string, httpResponseCode: number) => void = async (
event,
navigationUrl,
httpResponseCode
httpResponseCode,
) => {
if (!(await isCloudFlareSite(this.getWindow().webContents))) {
this.getWindow().webContents.removeListener('did-navigate', onDidNavigate);

View File

@ -15,12 +15,12 @@ export const cloudflareSiteCsp: ContentSecurityPolicy = {
export function humanInteractionRequired(webContents: WebContents): Promise<boolean> {
return webContents.executeJavaScript(
"[...document.querySelectorAll('iframe')].map(iframe => (new URL(iframe.src)).hostname.match(/hcaptcha/)).some(e => e)"
"[...document.querySelectorAll('iframe')].map(iframe => (new URL(iframe.src)).hostname.match(/hcaptcha/)).some(e => e)",
) as Promise<boolean>;
}
export function isCloudFlareSite(webContents: WebContents): Promise<boolean> {
return webContents.executeJavaScript(
"!!document.querySelector('.cf-browser-verification, #cf-content, #cf-wrapper') || !!window._cf_translation"
"!!document.querySelector('.cf-browser-verification, #cf-content, #cf-wrapper') || !!window._cf_translation",
) as Promise<boolean>;
}

View File

@ -1,6 +1,6 @@
export function dateObjectToString(date: Date): string {
return `${date.getUTCFullYear()}-${`${date.getUTCMonth()}`.padStart(2, '0')}-${`${date.getUTCDate()}`.padStart(
2,
'0'
'0',
)}`;
}

View File

@ -1,20 +1,14 @@
import { dialog, OpenDialogOptions } from 'electron';
import { injectable } from 'inversify';
import { inject } from '../../core/inject';
import { t } from '../../../shared/services/translation/t';
import type { DialogInterface } from './dialog-interface';
@injectable()
export class Dialog implements DialogInterface {
private readonly translator: I18nTranslatorInterface;
public constructor(@inject('i18n-translator') translator: I18nTranslatorInterface) {
this.translator = translator;
}
public selectFolder(options: OpenDialogOptions): ReturnType<typeof dialog.showOpenDialog> {
return dialog.showOpenDialog({
...{
title: this.translator.t('select a folder'),
title: t('imperatives.dialog.select_folder'),
},
...options,
...{

View File

@ -0,0 +1,265 @@
import type { EntityTarget } from 'typeorm';
import { container, Service } from '../../core/container';
import { Database, getManager } from '../../core/database';
import { Author } from '../../entities/library/author';
import { AuthorName } from '../../entities/library/author-name';
import { AuthorRole } from '../../entities/library/author-role';
import { AuthorRoleName } from '../../entities/library/author-role-name';
import { CharacterTag } from '../../entities/library/character-tag';
import { Collection } from '../../entities/library/collection';
import { CollectionName } from '../../entities/library/collection-name';
import { CollectionPart } from '../../entities/library/collection-part';
import { Copy } from '../../entities/library/copy';
import { InteractionTag } from '../../entities/library/interaction-tag';
import { Site } from '../../entities/library/site';
import { SiteName } from '../../entities/library/site-name';
import { Source } from '../../entities/library/source';
import { Tag } from '../../entities/library/tag';
import { TagName } from '../../entities/library/tag-name';
import { Transformation } from '../../entities/library/transformation';
import { TransformationType } from '../../entities/library/transformation-type';
import { TransformationTypeName } from '../../entities/library/transformation-type-name';
import { Work } from '../../entities/library/work';
import { WorkAuthor } from '../../entities/library/work-author';
import { WorkCharacter } from '../../entities/library/work-character';
import { WorkCharacterName } from '../../entities/library/work-character-name';
import { WorkName } from '../../entities/library/work-name';
import { WorkTag } from '../../entities/library/work-tag';
import { World } from '../../entities/library/world';
import { WorldCharacter } from '../../entities/library/world-character';
import { WorldCharacterName } from '../../entities/library/world-character-name';
import { WorldName } from '../../entities/library/world-name';
import { ipcServer } from '../ipc/ipc-server';
import type { Serializer } from '../serialization/serializer';
type CreateSerializedType = {
[IpcChannel.ENTITY_CREATE_AUTHOR]: AuthorSerializedInterface;
[IpcChannel.ENTITY_CREATE_AUTHOR_NAME]: AuthorNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_AUTHOR_ROLE]: AuthorRoleSerializedInterface;
[IpcChannel.ENTITY_CREATE_AUTHOR_ROLE_NAME]: AuthorRoleNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_CHARACTER_TAG]: CharacterTagSerializedInterface;
[IpcChannel.ENTITY_CREATE_COLLECTION]: CollectionSerializedInterface;
[IpcChannel.ENTITY_CREATE_COLLECTION_NAME]: CollectionNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_COLLECTION_PART]: CollectionPartSerializedInterface;
[IpcChannel.ENTITY_CREATE_COPY]: CopySerializedInterface;
[IpcChannel.ENTITY_CREATE_INTERACTION_TAG]: InteractionTagSerializedInterface;
[IpcChannel.ENTITY_CREATE_SITE]: SiteSerializedInterface;
[IpcChannel.ENTITY_CREATE_SITE_NAME]: SiteNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_SOURCE]: SourceSerializedInterface;
[IpcChannel.ENTITY_CREATE_TAG]: TagSerializedInterface;
[IpcChannel.ENTITY_CREATE_TAG_NAME]: TagNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_TRANSFORMATION]: TransformationSerializedInterface;
[IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE]: TransformationTypeSerializedInterface;
[IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE_NAME]: TransformationTypeNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORK_AUTHOR]: WorkAuthorSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORK_CHARACTER]: WorkCharacterSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORK_CHARACTER_NAME]: WorkCharacterNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORK]: WorkSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORK_NAME]: WorkNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORK_TAG]: WorkTagSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORLD]: WorldSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORLD_NAME]: WorldNameSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORLD_CHARACTER]: WorldCharacterSerializedInterface;
[IpcChannel.ENTITY_CREATE_WORLD_CHARACTER_NAME]: WorldCharacterNameSerializedInterface;
};
type CreateEntityType = {
[IpcChannel.ENTITY_CREATE_AUTHOR]: Author;
[IpcChannel.ENTITY_CREATE_AUTHOR_NAME]: AuthorName;
[IpcChannel.ENTITY_CREATE_AUTHOR_ROLE]: AuthorRole;
[IpcChannel.ENTITY_CREATE_AUTHOR_ROLE_NAME]: AuthorRoleName;
[IpcChannel.ENTITY_CREATE_CHARACTER_TAG]: CharacterTag;
[IpcChannel.ENTITY_CREATE_COLLECTION]: Collection;
[IpcChannel.ENTITY_CREATE_COLLECTION_NAME]: CollectionName;
[IpcChannel.ENTITY_CREATE_COLLECTION_PART]: CollectionPart;
[IpcChannel.ENTITY_CREATE_COPY]: Copy;
[IpcChannel.ENTITY_CREATE_INTERACTION_TAG]: InteractionTag;
[IpcChannel.ENTITY_CREATE_SITE]: Site;
[IpcChannel.ENTITY_CREATE_SITE_NAME]: SiteName;
[IpcChannel.ENTITY_CREATE_SOURCE]: Source;
[IpcChannel.ENTITY_CREATE_TAG]: Tag;
[IpcChannel.ENTITY_CREATE_TAG_NAME]: TagName;
[IpcChannel.ENTITY_CREATE_TRANSFORMATION]: Transformation;
[IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE]: TransformationType;
[IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE_NAME]: TransformationTypeName;
[IpcChannel.ENTITY_CREATE_WORK_AUTHOR]: WorkAuthor;
[IpcChannel.ENTITY_CREATE_WORK_CHARACTER]: WorkCharacter;
[IpcChannel.ENTITY_CREATE_WORK_CHARACTER_NAME]: WorkCharacterName;
[IpcChannel.ENTITY_CREATE_WORK]: Work;
[IpcChannel.ENTITY_CREATE_WORK_NAME]: WorkName;
[IpcChannel.ENTITY_CREATE_WORK_TAG]: WorkTag;
[IpcChannel.ENTITY_CREATE_WORLD]: World;
[IpcChannel.ENTITY_CREATE_WORLD_NAME]: WorldName;
[IpcChannel.ENTITY_CREATE_WORLD_CHARACTER]: WorldCharacter;
[IpcChannel.ENTITY_CREATE_WORLD_CHARACTER_NAME]: WorldCharacterName;
};
async function create<T extends keyof CreateSerializedType>(
partial: Partial<CreateSerializedType[T]>,
entityTarget: EntityTarget<CreateEntityType[T]>,
serializer: Serializer<CreateEntityType[T], CreateSerializedType[T]>,
): Promise<CreateSerializedType[T]> {
const manager = await getManager(Database.LIBRARY);
const entity = manager.create(entityTarget, serializer.deserialize(partial) as DeepPartial<CreateEntityType[T]>);
return serializer.serialize(await manager.save(entity));
}
ipcServer.answer(IpcChannel.ENTITY_CREATE_AUTHOR, (partial) =>
create<IpcChannel.ENTITY_CREATE_AUTHOR>(partial, Author, container.get(Service.AUTHOR_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_AUTHOR_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_AUTHOR_NAME>(partial, AuthorName, container.get(Service.AUTHOR_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_AUTHOR_ROLE, (partial) =>
create<IpcChannel.ENTITY_CREATE_AUTHOR_ROLE>(partial, AuthorRole, container.get(Service.AUTHOR_ROLE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_AUTHOR_ROLE_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_AUTHOR_ROLE_NAME>(
partial,
AuthorRoleName,
container.get(Service.AUTHOR_ROLE_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_CHARACTER_TAG, (partial) =>
create<IpcChannel.ENTITY_CREATE_CHARACTER_TAG>(
partial,
CharacterTag,
container.get(Service.CHARACTER_TAG_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_COLLECTION, (partial) =>
create<IpcChannel.ENTITY_CREATE_COLLECTION>(partial, Collection, container.get(Service.COLLECTION_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_COLLECTION_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_COLLECTION_NAME>(
partial,
CollectionName,
container.get(Service.COLLECTION_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_COLLECTION_PART, (partial) =>
create<IpcChannel.ENTITY_CREATE_COLLECTION_PART>(
partial,
CollectionPart,
container.get(Service.COLLECTION_PART_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_COPY, (partial) =>
create<IpcChannel.ENTITY_CREATE_COPY>(partial, Copy, container.get(Service.COPY_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_INTERACTION_TAG, (partial) =>
create<IpcChannel.ENTITY_CREATE_INTERACTION_TAG>(
partial,
InteractionTag,
container.get(Service.INTERACTION_TAG_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_SITE, (partial) =>
create<IpcChannel.ENTITY_CREATE_SITE>(partial, Site, container.get(Service.SITE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_SITE_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_SITE_NAME>(partial, SiteName, container.get(Service.SITE_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_SOURCE, (partial) =>
create<IpcChannel.ENTITY_CREATE_SOURCE>(partial, Source, container.get(Service.SOURCE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_TAG, (partial) =>
create<IpcChannel.ENTITY_CREATE_TAG>(partial, Tag, container.get(Service.TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_TAG_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_TAG_NAME>(partial, TagName, container.get(Service.TAG_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_TRANSFORMATION, (partial) =>
create<IpcChannel.ENTITY_CREATE_TRANSFORMATION>(
partial,
Transformation,
container.get(Service.TRANSFORMATION_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE, (partial) =>
create<IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE>(
partial,
TransformationType,
container.get(Service.TRANSFORMATION_TYPE_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE_NAME>(
partial,
TransformationTypeName,
container.get(Service.TRANSFORMATION_TYPE_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORK_AUTHOR, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORK_AUTHOR>(partial, WorkAuthor, container.get(Service.WORK_AUTHOR_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORK_CHARACTER, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORK_CHARACTER>(
partial,
WorkCharacter,
container.get(Service.WORK_CHARACTER_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORK_CHARACTER_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORK_CHARACTER_NAME>(
partial,
WorkCharacterName,
container.get(Service.WORK_CHARACTER_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORK, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORK>(partial, Work, container.get(Service.WORK_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORK_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORK_NAME>(partial, WorkName, container.get(Service.WORK_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORK_TAG, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORK_TAG>(partial, WorkTag, container.get(Service.WORK_TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORLD, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORLD>(partial, World, container.get(Service.WORLD_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORLD_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORLD_NAME>(partial, WorldName, container.get(Service.WORLD_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORLD_CHARACTER, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORLD_CHARACTER>(
partial,
WorldCharacter,
container.get(Service.WORLD_CHARACTER_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_CREATE_WORLD_CHARACTER_NAME, (partial) =>
create<IpcChannel.ENTITY_CREATE_WORLD_CHARACTER_NAME>(
partial,
WorldCharacterName,
container.get(Service.WORLD_CHARACTER_NAME_SERIALIZER),
),
);

View File

@ -0,0 +1,92 @@
import type { EntityTarget } from 'typeorm';
import { getRepository } from '../../core/database';
import { Author } from '../../entities/library/author';
import { AuthorName } from '../../entities/library/author-name';
import { AuthorRole } from '../../entities/library/author-role';
import { AuthorRoleName } from '../../entities/library/author-role-name';
import { CharacterTag } from '../../entities/library/character-tag';
import { Collection } from '../../entities/library/collection';
import { CollectionName } from '../../entities/library/collection-name';
import { CollectionPart } from '../../entities/library/collection-part';
import { Copy } from '../../entities/library/copy';
import { InteractionTag } from '../../entities/library/interaction-tag';
import { Site } from '../../entities/library/site';
import { SiteName } from '../../entities/library/site-name';
import { Source } from '../../entities/library/source';
import { Tag } from '../../entities/library/tag';
import { TagName } from '../../entities/library/tag-name';
import { Transformation } from '../../entities/library/transformation';
import { TransformationType } from '../../entities/library/transformation-type';
import { TransformationTypeName } from '../../entities/library/transformation-type-name';
import { Work } from '../../entities/library/work';
import { WorkAuthor } from '../../entities/library/work-author';
import { WorkCharacter } from '../../entities/library/work-character';
import { WorkCharacterName } from '../../entities/library/work-character-name';
import { WorkName } from '../../entities/library/work-name';
import { WorkTag } from '../../entities/library/work-tag';
import { World } from '../../entities/library/world';
import { WorldCharacter } from '../../entities/library/world-character';
import { WorldCharacterName } from '../../entities/library/world-character-name';
import { WorldName } from '../../entities/library/world-name';
import { ipcServer } from '../ipc/ipc-server';
async function del(id: number, entityTarget: EntityTarget<unknown>): Promise<void> {
const repository = await getRepository(entityTarget);
await repository.delete(id);
}
ipcServer.answer(IpcChannel.ENTITY_DELETE_AUTHOR, async (id) => del(id, Author));
ipcServer.answer(IpcChannel.ENTITY_DELETE_AUTHOR_NAME, async (id) => del(id, AuthorName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_AUTHOR_ROLE, async (id) => del(id, AuthorRole));
ipcServer.answer(IpcChannel.ENTITY_DELETE_AUTHOR_ROLE_NAME, async (id) => del(id, AuthorRoleName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_CHARACTER_TAG, async (id) => del(id, CharacterTag));
ipcServer.answer(IpcChannel.ENTITY_DELETE_COLLECTION, async (id) => del(id, Collection));
ipcServer.answer(IpcChannel.ENTITY_DELETE_COLLECTION_NAME, async (id) => del(id, CollectionName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_COLLECTION_PART, async (id) => del(id, CollectionPart));
ipcServer.answer(IpcChannel.ENTITY_DELETE_COPY, async (id) => del(id, Copy));
ipcServer.answer(IpcChannel.ENTITY_DELETE_INTERACTION_TAG, async (id) => del(id, InteractionTag));
ipcServer.answer(IpcChannel.ENTITY_DELETE_SITE, async (id) => del(id, Site));
ipcServer.answer(IpcChannel.ENTITY_DELETE_SITE_NAME, async (id) => del(id, SiteName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_SOURCE, async (id) => del(id, Source));
ipcServer.answer(IpcChannel.ENTITY_DELETE_TAG, async (id) => del(id, Tag));
ipcServer.answer(IpcChannel.ENTITY_DELETE_TAG_NAME, async (id) => del(id, TagName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_TRANSFORMATION, async (id) => del(id, Transformation));
ipcServer.answer(IpcChannel.ENTITY_DELETE_TRANSFORMATION_TYPE, async (id) => del(id, TransformationType));
ipcServer.answer(IpcChannel.ENTITY_DELETE_TRANSFORMATION_TYPE_NAME, async (id) => del(id, TransformationTypeName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORK_AUTHOR, async (id) => del(id, WorkAuthor));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORK_CHARACTER, async (id) => del(id, WorkCharacter));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORK_CHARACTER_NAME, async (id) => del(id, WorkCharacterName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORK, async (id) => del(id, Work));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORK_NAME, async (id) => del(id, WorkName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORK_TAG, async (id) => del(id, WorkTag));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORLD, async (id) => del(id, World));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORLD_NAME, async (id) => del(id, WorldName));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORLD_CHARACTER, async (id) => del(id, WorldCharacter));
ipcServer.answer(IpcChannel.ENTITY_DELETE_WORLD_CHARACTER_NAME, async (id) => del(id, WorldCharacterName));

View File

@ -1,20 +1,4 @@
import { workSerializer } from '../../../shared/services/serialization/serializers/work';
import { Database, getConnection } from '../../core/database';
import { Work } from '../../entities/library/work';
import { answer } from '../ipc/annotations/answer';
export class EntityApiIpcController implements IpcController {
private constructor() {}
@answer(IpcChannel.ENTITY_GET_WORK)
public async getWork({ id }: { id: number }): Promise<WorkSerializedInterface> {
const connection = await getConnection(Database.LIBRARY);
const work = await connection.manager.getRepository(Work).findOneOrFail(id);
return workSerializer.serialize(work);
}
public get(): EntityApiIpcController {
return new EntityApiIpcController();
}
}
import './create-controller';
import './read-controller';
import './update-controller';
import './delete-controller';

View File

@ -0,0 +1,274 @@
import type { EntityTarget } from 'typeorm';
import { container, Service } from '../../core/container';
import { getRepository } from '../../core/database';
import { Author } from '../../entities/library/author';
import { AuthorName } from '../../entities/library/author-name';
import { AuthorRole } from '../../entities/library/author-role';
import { AuthorRoleName } from '../../entities/library/author-role-name';
import { CharacterTag } from '../../entities/library/character-tag';
import { Collection } from '../../entities/library/collection';
import { CollectionName } from '../../entities/library/collection-name';
import { CollectionPart } from '../../entities/library/collection-part';
import { Copy } from '../../entities/library/copy';
import { InteractionTag } from '../../entities/library/interaction-tag';
import { Language } from '../../entities/library/language';
import { Site } from '../../entities/library/site';
import { SiteName } from '../../entities/library/site-name';
import { Source } from '../../entities/library/source';
import { Tag } from '../../entities/library/tag';
import { TagName } from '../../entities/library/tag-name';
import { Transformation } from '../../entities/library/transformation';
import { TransformationType } from '../../entities/library/transformation-type';
import { TransformationTypeName } from '../../entities/library/transformation-type-name';
import { Work } from '../../entities/library/work';
import { WorkAuthor } from '../../entities/library/work-author';
import { WorkCharacter } from '../../entities/library/work-character';
import { WorkCharacterName } from '../../entities/library/work-character-name';
import { WorkName } from '../../entities/library/work-name';
import { WorkTag } from '../../entities/library/work-tag';
import { World } from '../../entities/library/world';
import { WorldCharacter } from '../../entities/library/world-character';
import { WorldCharacterName } from '../../entities/library/world-character-name';
import { WorldName } from '../../entities/library/world-name';
import { ipcServer } from '../ipc/ipc-server';
import type { Serializer } from '../serialization/serializer';
type ReadSerializedType = {
[IpcChannel.ENTITY_READ_AUTHOR]: AuthorSerializedInterface;
[IpcChannel.ENTITY_READ_AUTHOR_NAME]: AuthorNameSerializedInterface;
[IpcChannel.ENTITY_READ_AUTHOR_ROLE]: AuthorRoleSerializedInterface;
[IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME]: AuthorRoleNameSerializedInterface;
[IpcChannel.ENTITY_READ_CHARACTER_TAG]: CharacterTagSerializedInterface;
[IpcChannel.ENTITY_READ_COLLECTION]: CollectionSerializedInterface;
[IpcChannel.ENTITY_READ_COLLECTION_NAME]: CollectionNameSerializedInterface;
[IpcChannel.ENTITY_READ_COLLECTION_PART]: CollectionPartSerializedInterface;
[IpcChannel.ENTITY_READ_COPY]: CopySerializedInterface;
[IpcChannel.ENTITY_READ_INTERACTION_TAG]: InteractionTagSerializedInterface;
[IpcChannel.ENTITY_READ_LANGUAGE]: LanguageSerializedInterface;
[IpcChannel.ENTITY_READ_SITE]: SiteSerializedInterface;
[IpcChannel.ENTITY_READ_SITE_NAME]: SiteNameSerializedInterface;
[IpcChannel.ENTITY_READ_SOURCE]: SourceSerializedInterface;
[IpcChannel.ENTITY_READ_TAG]: TagSerializedInterface;
[IpcChannel.ENTITY_READ_TAG_NAME]: TagNameSerializedInterface;
[IpcChannel.ENTITY_READ_TRANSFORMATION]: TransformationSerializedInterface;
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE]: TransformationTypeSerializedInterface;
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME]: TransformationTypeNameSerializedInterface;
[IpcChannel.ENTITY_READ_WORK_AUTHOR]: WorkAuthorSerializedInterface;
[IpcChannel.ENTITY_READ_WORK_CHARACTER]: WorkCharacterSerializedInterface;
[IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME]: WorkCharacterNameSerializedInterface;
[IpcChannel.ENTITY_READ_WORK]: WorkSerializedInterface;
[IpcChannel.ENTITY_READ_WORK_NAME]: WorkNameSerializedInterface;
[IpcChannel.ENTITY_READ_WORK_TAG]: WorkTagSerializedInterface;
[IpcChannel.ENTITY_READ_WORLD]: WorldSerializedInterface;
[IpcChannel.ENTITY_READ_WORLD_NAME]: WorldNameSerializedInterface;
[IpcChannel.ENTITY_READ_WORLD_CHARACTER]: WorldCharacterSerializedInterface;
[IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME]: WorldCharacterNameSerializedInterface;
};
type ReadIdType = {
[IpcChannel.ENTITY_READ_AUTHOR]: number;
[IpcChannel.ENTITY_READ_AUTHOR_NAME]: number;
[IpcChannel.ENTITY_READ_AUTHOR_ROLE]: number;
[IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME]: number;
[IpcChannel.ENTITY_READ_CHARACTER_TAG]: number;
[IpcChannel.ENTITY_READ_COLLECTION]: number;
[IpcChannel.ENTITY_READ_COLLECTION_NAME]: number;
[IpcChannel.ENTITY_READ_COLLECTION_PART]: number;
[IpcChannel.ENTITY_READ_COPY]: number;
[IpcChannel.ENTITY_READ_INTERACTION_TAG]: number;
[IpcChannel.ENTITY_READ_LANGUAGE]: string;
[IpcChannel.ENTITY_READ_SITE]: number;
[IpcChannel.ENTITY_READ_SITE_NAME]: number;
[IpcChannel.ENTITY_READ_SOURCE]: number;
[IpcChannel.ENTITY_READ_TAG]: number;
[IpcChannel.ENTITY_READ_TAG_NAME]: number;
[IpcChannel.ENTITY_READ_TRANSFORMATION]: number;
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE]: number;
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME]: number;
[IpcChannel.ENTITY_READ_WORK_AUTHOR]: number;
[IpcChannel.ENTITY_READ_WORK_CHARACTER]: number;
[IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME]: number;
[IpcChannel.ENTITY_READ_WORK]: number;
[IpcChannel.ENTITY_READ_WORK_NAME]: number;
[IpcChannel.ENTITY_READ_WORK_TAG]: number;
[IpcChannel.ENTITY_READ_WORLD]: number;
[IpcChannel.ENTITY_READ_WORLD_NAME]: number;
[IpcChannel.ENTITY_READ_WORLD_CHARACTER]: number;
[IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME]: number;
};
type ReadEntityType = {
[IpcChannel.ENTITY_READ_AUTHOR]: Author;
[IpcChannel.ENTITY_READ_AUTHOR_NAME]: AuthorName;
[IpcChannel.ENTITY_READ_AUTHOR_ROLE]: AuthorRole;
[IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME]: AuthorRoleName;
[IpcChannel.ENTITY_READ_CHARACTER_TAG]: CharacterTag;
[IpcChannel.ENTITY_READ_COLLECTION]: Collection;
[IpcChannel.ENTITY_READ_COLLECTION_NAME]: CollectionName;
[IpcChannel.ENTITY_READ_COLLECTION_PART]: CollectionPart;
[IpcChannel.ENTITY_READ_COPY]: Copy;
[IpcChannel.ENTITY_READ_INTERACTION_TAG]: InteractionTag;
[IpcChannel.ENTITY_READ_LANGUAGE]: Language;
[IpcChannel.ENTITY_READ_SITE]: Site;
[IpcChannel.ENTITY_READ_SITE_NAME]: SiteName;
[IpcChannel.ENTITY_READ_SOURCE]: Source;
[IpcChannel.ENTITY_READ_TAG]: Tag;
[IpcChannel.ENTITY_READ_TAG_NAME]: TagName;
[IpcChannel.ENTITY_READ_TRANSFORMATION]: Transformation;
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE]: TransformationType;
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME]: TransformationTypeName;
[IpcChannel.ENTITY_READ_WORK_AUTHOR]: WorkAuthor;
[IpcChannel.ENTITY_READ_WORK_CHARACTER]: WorkCharacter;
[IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME]: WorkCharacterName;
[IpcChannel.ENTITY_READ_WORK]: Work;
[IpcChannel.ENTITY_READ_WORK_NAME]: WorkName;
[IpcChannel.ENTITY_READ_WORK_TAG]: WorkTag;
[IpcChannel.ENTITY_READ_WORLD]: World;
[IpcChannel.ENTITY_READ_WORLD_NAME]: WorldName;
[IpcChannel.ENTITY_READ_WORLD_CHARACTER]: WorldCharacter;
[IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME]: WorldCharacterName;
};
async function read<T extends keyof ReadSerializedType>(
id: ReadIdType[T],
entityTarget: EntityTarget<ReadEntityType[T]>,
// @ts-ignore -- yeah, i don't get it
serializer: Serializer<ReadEntityType[T], ReadSerializedType[T], ReadIdType[T]>,
): Promise<ReadSerializedType[T]> {
const repository = await getRepository(entityTarget);
const entity = await repository.findOneOrFail(id);
return serializer.serialize(entity);
}
ipcServer.answer(IpcChannel.ENTITY_READ_AUTHOR, (id) =>
read<IpcChannel.ENTITY_READ_AUTHOR>(id, Author, container.get(Service.AUTHOR_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_AUTHOR_NAME, (id) =>
read<IpcChannel.ENTITY_READ_AUTHOR_NAME>(id, AuthorName, container.get(Service.AUTHOR_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_AUTHOR_ROLE, (id) =>
read<IpcChannel.ENTITY_READ_AUTHOR_ROLE>(id, AuthorRole, container.get(Service.AUTHOR_ROLE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME, (id) =>
read<IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME>(id, AuthorRoleName, container.get(Service.AUTHOR_ROLE_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_CHARACTER_TAG, (id) =>
read<IpcChannel.ENTITY_READ_CHARACTER_TAG>(id, CharacterTag, container.get(Service.CHARACTER_TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_COLLECTION, (id) =>
read<IpcChannel.ENTITY_READ_COLLECTION>(id, Collection, container.get(Service.COLLECTION_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_COLLECTION_NAME, (id) =>
read<IpcChannel.ENTITY_READ_COLLECTION_NAME>(id, CollectionName, container.get(Service.COLLECTION_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_COLLECTION_PART, (id) =>
read<IpcChannel.ENTITY_READ_COLLECTION_PART>(id, CollectionPart, container.get(Service.COLLECTION_PART_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_COPY, (id) =>
read<IpcChannel.ENTITY_READ_COPY>(id, Copy, container.get(Service.COPY_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_INTERACTION_TAG, (id) =>
read<IpcChannel.ENTITY_READ_INTERACTION_TAG>(id, InteractionTag, container.get(Service.INTERACTION_TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_LANGUAGE, (code) =>
read<IpcChannel.ENTITY_READ_LANGUAGE>(code, Language, container.get(Service.LANGUAGE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_SITE, (id) =>
read<IpcChannel.ENTITY_READ_SITE>(id, Site, container.get(Service.SITE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_SITE_NAME, (id) =>
read<IpcChannel.ENTITY_READ_SITE_NAME>(id, SiteName, container.get(Service.SITE_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_SOURCE, (id) =>
read<IpcChannel.ENTITY_READ_SOURCE>(id, Source, container.get(Service.SOURCE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_TAG, (id) =>
read<IpcChannel.ENTITY_READ_TAG>(id, Tag, container.get(Service.TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_TAG_NAME, (id) =>
read<IpcChannel.ENTITY_READ_TAG_NAME>(id, TagName, container.get(Service.TAG_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_TRANSFORMATION, (id) =>
read<IpcChannel.ENTITY_READ_TRANSFORMATION>(id, Transformation, container.get(Service.TRANSFORMATION_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE, (id) =>
read<IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE>(
id,
TransformationType,
container.get(Service.TRANSFORMATION_TYPE_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME, (id) =>
read<IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME>(
id,
TransformationTypeName,
container.get(Service.TRANSFORMATION_TYPE_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORK_AUTHOR, (id) =>
read<IpcChannel.ENTITY_READ_WORK_AUTHOR>(id, WorkAuthor, container.get(Service.WORK_AUTHOR_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORK_CHARACTER, (id) =>
read<IpcChannel.ENTITY_READ_WORK_CHARACTER>(id, WorkCharacter, container.get(Service.WORK_CHARACTER_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME, (id) =>
read<IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME>(
id,
WorkCharacterName,
container.get(Service.WORK_CHARACTER_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORK, (id) =>
read<IpcChannel.ENTITY_READ_WORK>(id, Work, container.get(Service.WORK_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORK_NAME, (id) =>
read<IpcChannel.ENTITY_READ_WORK_NAME>(id, WorkName, container.get(Service.WORK_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORK_TAG, (id) =>
read<IpcChannel.ENTITY_READ_WORK_TAG>(id, WorkTag, container.get(Service.WORK_TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORLD, (id) =>
read<IpcChannel.ENTITY_READ_WORLD>(id, World, container.get(Service.WORLD_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORLD_NAME, (id) =>
read<IpcChannel.ENTITY_READ_WORLD_NAME>(id, WorldName, container.get(Service.WORLD_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORLD_CHARACTER, (id) =>
read<IpcChannel.ENTITY_READ_WORLD_CHARACTER>(id, WorldCharacter, container.get(Service.WORLD_CHARACTER_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME, (id) =>
read<IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME>(
id,
WorldCharacterName,
container.get(Service.WORLD_CHARACTER_NAME_SERIALIZER),
),
);

View File

@ -0,0 +1,281 @@
import type { EntityTarget } from 'typeorm';
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
import { container, Service } from '../../core/container';
import { getRepository } from '../../core/database';
import { Author } from '../../entities/library/author';
import { AuthorName } from '../../entities/library/author-name';
import { AuthorRole } from '../../entities/library/author-role';
import { AuthorRoleName } from '../../entities/library/author-role-name';
import { CharacterTag } from '../../entities/library/character-tag';
import { Collection } from '../../entities/library/collection';
import { CollectionName } from '../../entities/library/collection-name';
import { CollectionPart } from '../../entities/library/collection-part';
import { Copy } from '../../entities/library/copy';
import { InteractionTag } from '../../entities/library/interaction-tag';
import { Site } from '../../entities/library/site';
import { SiteName } from '../../entities/library/site-name';
import { Source } from '../../entities/library/source';
import { Tag } from '../../entities/library/tag';
import { TagName } from '../../entities/library/tag-name';
import { Transformation } from '../../entities/library/transformation';
import { TransformationType } from '../../entities/library/transformation-type';
import { TransformationTypeName } from '../../entities/library/transformation-type-name';
import { Work } from '../../entities/library/work';
import { WorkAuthor } from '../../entities/library/work-author';
import { WorkCharacter } from '../../entities/library/work-character';
import { WorkCharacterName } from '../../entities/library/work-character-name';
import { WorkName } from '../../entities/library/work-name';
import { WorkTag } from '../../entities/library/work-tag';
import { World } from '../../entities/library/world';
import { WorldCharacter } from '../../entities/library/world-character';
import { WorldCharacterName } from '../../entities/library/world-character-name';
import { WorldName } from '../../entities/library/world-name';
import { ipcServer } from '../ipc/ipc-server';
import type { Serializer } from '../serialization/serializer';
type UpdateSerializedType = {
[IpcChannel.ENTITY_UPDATE_AUTHOR]: AuthorSerializedInterface;
[IpcChannel.ENTITY_UPDATE_AUTHOR_NAME]: AuthorNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE]: AuthorRoleSerializedInterface;
[IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE_NAME]: AuthorRoleNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_CHARACTER_TAG]: CharacterTagSerializedInterface;
[IpcChannel.ENTITY_UPDATE_COLLECTION]: CollectionSerializedInterface;
[IpcChannel.ENTITY_UPDATE_COLLECTION_NAME]: CollectionNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_COLLECTION_PART]: CollectionPartSerializedInterface;
[IpcChannel.ENTITY_UPDATE_COPY]: CopySerializedInterface;
[IpcChannel.ENTITY_UPDATE_INTERACTION_TAG]: InteractionTagSerializedInterface;
[IpcChannel.ENTITY_UPDATE_SITE]: SiteSerializedInterface;
[IpcChannel.ENTITY_UPDATE_SITE_NAME]: SiteNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_SOURCE]: SourceSerializedInterface;
[IpcChannel.ENTITY_UPDATE_TAG]: TagSerializedInterface;
[IpcChannel.ENTITY_UPDATE_TAG_NAME]: TagNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_TRANSFORMATION]: TransformationSerializedInterface;
[IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE]: TransformationTypeSerializedInterface;
[IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE_NAME]: TransformationTypeNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORK_AUTHOR]: WorkAuthorSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORK_CHARACTER]: WorkCharacterSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORK_CHARACTER_NAME]: WorkCharacterNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORK]: WorkSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORK_NAME]: WorkNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORK_TAG]: WorkTagSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORLD]: WorldSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORLD_NAME]: WorldNameSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER]: WorldCharacterSerializedInterface;
[IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER_NAME]: WorldCharacterNameSerializedInterface;
};
type UpdateEntityType = {
[IpcChannel.ENTITY_UPDATE_AUTHOR]: Author;
[IpcChannel.ENTITY_UPDATE_AUTHOR_NAME]: AuthorName;
[IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE]: AuthorRole;
[IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE_NAME]: AuthorRoleName;
[IpcChannel.ENTITY_UPDATE_CHARACTER_TAG]: CharacterTag;
[IpcChannel.ENTITY_UPDATE_COLLECTION]: Collection;
[IpcChannel.ENTITY_UPDATE_COLLECTION_NAME]: CollectionName;
[IpcChannel.ENTITY_UPDATE_COLLECTION_PART]: CollectionPart;
[IpcChannel.ENTITY_UPDATE_COPY]: Copy;
[IpcChannel.ENTITY_UPDATE_INTERACTION_TAG]: InteractionTag;
[IpcChannel.ENTITY_UPDATE_SITE]: Site;
[IpcChannel.ENTITY_UPDATE_SITE_NAME]: SiteName;
[IpcChannel.ENTITY_UPDATE_SOURCE]: Source;
[IpcChannel.ENTITY_UPDATE_TAG]: Tag;
[IpcChannel.ENTITY_UPDATE_TAG_NAME]: TagName;
[IpcChannel.ENTITY_UPDATE_TRANSFORMATION]: Transformation;
[IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE]: TransformationType;
[IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE_NAME]: TransformationTypeName;
[IpcChannel.ENTITY_UPDATE_WORK_AUTHOR]: WorkAuthor;
[IpcChannel.ENTITY_UPDATE_WORK_CHARACTER]: WorkCharacter;
[IpcChannel.ENTITY_UPDATE_WORK_CHARACTER_NAME]: WorkCharacterName;
[IpcChannel.ENTITY_UPDATE_WORK]: Work;
[IpcChannel.ENTITY_UPDATE_WORK_NAME]: WorkName;
[IpcChannel.ENTITY_UPDATE_WORK_TAG]: WorkTag;
[IpcChannel.ENTITY_UPDATE_WORLD]: World;
[IpcChannel.ENTITY_UPDATE_WORLD_NAME]: WorldName;
[IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER]: WorldCharacter;
[IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER_NAME]: WorldCharacterName;
};
async function update<T extends keyof UpdateSerializedType>(
id: number,
partial: Partial<UpdateSerializedType[T]>,
entityTarget: EntityTarget<UpdateEntityType[T]>,
serializer: Serializer<UpdateEntityType[T], UpdateSerializedType[T]>,
): Promise<UpdateSerializedType[T]> {
const repository = await getRepository(entityTarget);
await repository.update(id, serializer.deserialize(partial) as QueryDeepPartialEntity<UpdateEntityType[T]>);
const entity = await repository.findOneOrFail(id);
return serializer.serialize(entity);
}
ipcServer.answer(IpcChannel.ENTITY_UPDATE_AUTHOR, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_AUTHOR>(id, partial, Author, container.get(Service.AUTHOR_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_AUTHOR_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_AUTHOR_NAME>(id, partial, AuthorName, container.get(Service.AUTHOR_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE>(id, partial, AuthorRole, container.get(Service.AUTHOR_ROLE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE_NAME>(
id,
partial,
AuthorRoleName,
container.get(Service.AUTHOR_ROLE_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_CHARACTER_TAG, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_CHARACTER_TAG>(
id,
partial,
CharacterTag,
container.get(Service.CHARACTER_TAG_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_COLLECTION, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_COLLECTION>(id, partial, Collection, container.get(Service.COLLECTION_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_COLLECTION_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_COLLECTION_NAME>(
id,
partial,
CollectionName,
container.get(Service.COLLECTION_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_COLLECTION_PART, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_COLLECTION_PART>(
id,
partial,
CollectionPart,
container.get(Service.COLLECTION_PART_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_COPY, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_COPY>(id, partial, Copy, container.get(Service.COPY_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_INTERACTION_TAG, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_INTERACTION_TAG>(
id,
partial,
InteractionTag,
container.get(Service.INTERACTION_TAG_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_SITE, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_SITE>(id, partial, Site, container.get(Service.SITE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_SITE_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_SITE_NAME>(id, partial, SiteName, container.get(Service.SITE_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_SOURCE, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_SOURCE>(id, partial, Source, container.get(Service.SOURCE_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_TAG, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_TAG>(id, partial, Tag, container.get(Service.TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_TAG_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_TAG_NAME>(id, partial, TagName, container.get(Service.TAG_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_TRANSFORMATION, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_TRANSFORMATION>(
id,
partial,
Transformation,
container.get(Service.TRANSFORMATION_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE>(
id,
partial,
TransformationType,
container.get(Service.TRANSFORMATION_TYPE_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE_NAME>(
id,
partial,
TransformationTypeName,
container.get(Service.TRANSFORMATION_TYPE_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORK_AUTHOR, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORK_AUTHOR>(id, partial, WorkAuthor, container.get(Service.WORK_AUTHOR_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORK_CHARACTER, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORK_CHARACTER>(
id,
partial,
WorkCharacter,
container.get(Service.WORK_CHARACTER_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORK_CHARACTER_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORK_CHARACTER_NAME>(
id,
partial,
WorkCharacterName,
container.get(Service.WORK_CHARACTER_NAME_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORK, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORK>(id, partial, Work, container.get(Service.WORK_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORK_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORK_NAME>(id, partial, WorkName, container.get(Service.WORK_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORK_TAG, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORK_TAG>(id, partial, WorkTag, container.get(Service.WORK_TAG_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORLD, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORLD>(id, partial, World, container.get(Service.WORLD_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORLD_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORLD_NAME>(id, partial, WorldName, container.get(Service.WORLD_NAME_SERIALIZER)),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER>(
id,
partial,
WorldCharacter,
container.get(Service.WORLD_CHARACTER_SERIALIZER),
),
);
ipcServer.answer(IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER_NAME, async ({ id, partial }) =>
update<IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER_NAME>(
id,
partial,
WorldCharacterName,
container.get(Service.WORLD_CHARACTER_NAME_SERIALIZER),
),
);

View File

@ -1,3 +0,0 @@
interface I18nTranslatorInterface {
t(text: string): string;
}

View File

@ -1,8 +0,0 @@
import { injectable } from 'inversify';
@injectable()
export class I18nTranslator implements I18nTranslatorInterface {
public t(text: string): string {
return text;
}
}

View File

@ -1,7 +0,0 @@
import { registerHandler } from '../ipc-server';
export function answer(channel: IpcChannel): IpcControllerMethodDecorator {
return function (target: IpcController, propertyKey): void {
registerHandler(channel, target, propertyKey);
};
}

View File

@ -1,25 +1,16 @@
import { ipcMain } from 'electron';
import IpcMainEvent = Electron.IpcMainEvent;
import { ipcMain, IpcMainInvokeEvent } from 'electron';
import { container, Service } from '../../core/container';
export function registerHandler(channel: IpcChannel, controller: IpcController, handler: string): void {
ipcMain.on(channel, (event: IpcMainEvent, payload: IpcPayload) => {
((controller.get() as unknown) as { [x: string]: IpcHandler })
[handler](payload.data)
.then((result: unknown) => {
const response: IpcResponse = {
id: payload.id,
success: true,
data: result,
};
event.reply(channel, response);
})
.catch((reason: Error) => {
const response: IpcResponse = {
id: payload.id,
success: false,
error: reason.message,
};
event.reply(channel, response);
const logger = container.get(Service.LOGGER);
export const ipcServer: IpcServer = {
answer<T extends IpcChannel>(channel: T, answerer: IpcHandler<IpcParameter<T>, IpcAnswer<T>>) {
const listener = (data: IpcParameter<T>): Promise<IpcAnswer<T>> =>
answerer(data).catch((reason: Error) => {
void logger.exception(reason);
throw reason;
});
});
}
ipcMain.handle(channel, (event: IpcMainInvokeEvent, data: IpcParameter<T>) => listener(data));
},
};

View File

@ -1,10 +1,10 @@
import { createInterface, Interface } from 'readline';
import chai, { expect } from 'chai';
import 'mocha';
import chaiFs from 'chai-fs';
import fc from 'fast-check';
import fs from 'fs-extra';
import { container } from '../../core/container';
import 'mocha';
import { container, Service } from '../../core/container';
import { setDev } from '../../core/env.spec';
import { LogLevel } from './log-level';
@ -41,20 +41,20 @@ describe('Logger Service', () => {
const logLevelNumberArbitrary = fc.constantFrom(...logLevelsNumber);
it('creates log files', () => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
expect(logger.getLogFile()).path('log file is not created');
expect(logger.getExceptionsLogFile()).path('exception log file is not created');
});
it('logs exceptions', async () => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
await logger.exception(new Error('this is an error'));
});
it("default log file doesn't get bigger than 50KB @slow", async () => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
let prevLogFileSize = (await fs.stat(logger.getLogFile())).size;
let minNumberOfLines = maxLogSize;
@ -75,7 +75,7 @@ describe('Logger Service', () => {
}).timeout(15000);
it("exception log file doesn't get bigger than 50KB @slow", async () => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
let prevLogFileSize = (await fs.stat(logger.getExceptionsLogFile())).size;
let minNumberOfLines = maxLogSize;
@ -96,7 +96,7 @@ describe('Logger Service', () => {
}).timeout(15000);
it('logs different levels directly', () => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
return fc.assert(
fc.asyncProperty(logLevelArbitrary as LogLevelArbitrary, fc.string(), async (logLevel, message) => {
@ -107,12 +107,12 @@ describe('Logger Service', () => {
}),
{
numRuns: 50,
}
},
);
});
it('logs different levels indirectly via the generic log function', () => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
return fc.assert(
fc.asyncProperty(logLevelNumberArbitrary, fc.string(), async (logLevelNumber, message) => {
@ -121,17 +121,17 @@ describe('Logger Service', () => {
expect(lastLine).contains(message, 'the log line does not contain the message');
expect(lastLine).contains(
logLevels[logLevelNumber],
`the log line does not contain the '${logLevels[logLevelNumber]}' keyword`
`the log line does not contain the '${logLevels[logLevelNumber]}' keyword`,
);
}),
{
numRuns: 50,
}
},
);
});
it('logs debug only in dev mode', async () => {
const logger: LoggerInterface = container.get('logger');
const logger = container.get(Service.LOGGER);
setDev();
await logger.debug('this is a development message');
@ -143,7 +143,7 @@ describe('Logger Service', () => {
await logger.debug('this is a second development message, should not be here');
expect(lastLine).not.contain(
'this is a second development message, should not be here',
'debug is logged even in non-dev mode'
'debug is logged even in non-dev mode',
);
});
});

View File

@ -64,9 +64,9 @@ export class Logger implements LoggerInterface {
return Logger.writeStream(
Readable.from(
`[${new Date().toISOString()}] ${(error as NodeJS.ErrnoException).code ?? 'Error'}: ${error.message}
${error.stack ?? 'no stack trace'}\n`
${error.stack ?? 'no stack trace'}\n`,
),
exceptionLogFile
exceptionLogFile,
);
}

View File

@ -29,21 +29,18 @@ describe('Simple Mutex', () => {
const acquireOne = mutex.acquire();
const acquireTwo = mutex.acquire();
const acquireThree = mutex.acquire();
void acquireOne.then((release) =>
useResource().then(() => {
release();
})
);
void acquireTwo.then((release) =>
useResource().then(() => {
release();
})
);
return acquireThree.then((release) =>
useResource().then(() => {
release();
})
);
void acquireOne.then(async (release) => {
await useResource();
release();
});
void acquireTwo.then(async (release) => {
await useResource();
release();
});
return acquireThree.then(async (release) => {
await useResource();
release();
});
});
it('executes consumers in the right order', async () => {
@ -60,21 +57,18 @@ describe('Simple Mutex', () => {
const acquireOne = mutex.acquire();
const acquireTwo = mutex.acquire();
const acquireThree = mutex.acquire();
void acquireOne.then((release) =>
useResource(1).then(() => {
release();
})
);
void acquireTwo.then((release) =>
useResource(2).then(() => {
release();
})
);
return acquireThree.then((release) =>
useResource(3).then(() => {
release();
})
);
void acquireOne.then(async (release) => {
await useResource(1);
release();
});
void acquireTwo.then(async (release) => {
await useResource(2);
release();
});
return acquireThree.then(async (release) => {
await useResource(3);
release();
});
});
it('correctly informs if it is currently locked', async () => {

View File

@ -1,4 +1,5 @@
import { injectable } from 'inversify';
import { Service } from '../../core/container';
import { inject } from '../../core/inject';
import type { NhentaiAppWindowInterface } from './nhentai-app-window-interface';
@ -6,7 +7,7 @@ import type { NhentaiAppWindowInterface } from './nhentai-app-window-interface';
export class NhentaiApi implements NhentaiApiInterface {
private readonly appWindow: NhentaiAppWindowInterface;
public constructor(@inject('nhentai-app-window') appWindow: NhentaiAppWindowInterface) {
public constructor(@inject(Service.NHENTAI_APP_WINDOW) appWindow: NhentaiAppWindowInterface) {
this.appWindow = appWindow;
}

View File

@ -1,27 +1,26 @@
import chai, { expect } from 'chai';
import deepEqualInAnyOrder from 'deep-equal-in-any-order';
import { describe, it, before } from 'mocha';
import { container } from '../../core/container';
import { before, describe, it } from 'mocha';
import { container, Service } from '../../core/container';
import { LoggerMock } from '../logger/logger.mock';
import type { NhentaiAppWindowInterface } from './nhentai-app-window-interface';
chai.use(deepEqualInAnyOrder);
describe('Nhentai App Window', () => {
before(() => {
container.unbind('logger');
container.bind('logger').to(LoggerMock);
container.unbind(Service.LOGGER);
container.bind(Service.LOGGER).to(LoggerMock);
});
it('gets the gallery information from an identifier @slow', async () => {
const nhentaiAppWindow: NhentaiAppWindowInterface = container.get('nhentai-app-window');
const nhentaiAppWindow = container.get(Service.NHENTAI_APP_WINDOW);
let expectedGallery: Nhentai.Gallery = {
url: 'https://nhentai.net/g/117300/',
url: 'https://nhentai.net/g/107386/',
title: {
pre: '[Homunculus]',
main: 'Renai Sample',
post: '[English] [Decensored]',
main: 'Renai Sample + Bonus Booklets',
post: '[English] [Tankoubon version]',
},
artists: ['homunculus'],
groups: [],
@ -32,22 +31,27 @@ describe('Nhentai App Window', () => {
'stockings',
'schoolgirl uniform',
'glasses',
'nakadashi',
'incest',
'tankoubon',
'defloration',
'milf',
'swimsuit',
'ffm threesome',
'impregnation',
'sister',
'schoolboy uniform',
'bikini',
'uncensored',
'small breasts',
'teacher',
'apron',
'inseki',
'leg lock',
'cousin',
'niece',
],
languages: ['english', 'translated'],
uploadTime: 1411853968970,
uploadTime: 1404001890940,
};
let gallery = await nhentaiAppWindow.getGallery('117300');
let gallery = await nhentaiAppWindow.getGallery('107386');
expect(gallery).deep.equalInAnyOrder(expectedGallery, 'Renai Sample is not got correctly');
expectedGallery = {

View File

@ -5,37 +5,38 @@ import { Readable } from 'stream';
import { URL } from 'url';
import { createReadStream, remove } from 'fs-extra';
import { injectable } from 'inversify';
import { Service } from '../../core/container';
import { inject } from '../../core/inject';
import { CloudflareSiteAppWindow } from '../cloudflare/cloudflare-site-app-window';
import { mergeContentSecurityPolicy } from '../session/session-util';
import type { NhentaiAppWindowInterface } from './nhentai-app-window-interface';
import {
url as nhentaiUrl,
hostname as nhentaiHostname,
paths as nhentaiPaths,
getFavoritePageUrl,
nextFavoritePageSelector,
coverLinkSelector,
downloadLinkId,
getGalleryId,
favoritePageIsReady,
galleryPageIsReady,
getBookUrl,
getFavoritePageUrl,
getGalleryId,
hostname as nhentaiHostname,
labeledTagContainerSelector,
loginPageIsReady,
mainTitleSelector,
nextFavoritePageSelector,
pageIsReady,
paths as nhentaiPaths,
postTitleSelector,
preTitleSelector,
tagLabelArtists,
labeledTagContainerSelector,
tagLabelCharacters,
tagLabelGroups,
tagLabelLanguages,
tagLabelParodies,
tagLabelTags,
tagNameSelector,
tagSelector,
tagLabelGroups,
tagLabelParodies,
tagLabelCharacters,
tagLabelTags,
mainTitleSelector,
postTitleSelector,
galleryPageIsReady,
loginPageIsReady,
favoritePageIsReady,
pageIsReady,
timeSelector,
tagLabelLanguages,
url as nhentaiUrl,
} from './nhentai-util';
const waitInterval = 2000;
@ -44,7 +45,7 @@ const waitInterval = 2000;
export class NhentaiAppWindow extends CloudflareSiteAppWindow implements NhentaiAppWindowInterface {
protected readyCheck = pageIsReady;
public constructor(@inject('logger') logger: LoggerInterface) {
public constructor(@inject(Service.LOGGER) logger: LoggerInterface) {
super(logger, nhentaiUrl);
}
@ -64,8 +65,8 @@ export class NhentaiAppWindow extends CloudflareSiteAppWindow implements Nhentai
for await (const wc of this.getFavoritePageWebContentsGenerator()) {
bookUrls.push(
...((await wc.executeJavaScript(
`Array.from(document.querySelectorAll('${coverLinkSelector}')).map((el) => el.href)`
)) as string[])
`Array.from(document.querySelectorAll('${coverLinkSelector}')).map((el) => el.href)`,
)) as string[]),
);
}
@ -80,7 +81,7 @@ export class NhentaiAppWindow extends CloudflareSiteAppWindow implements Nhentai
})(this),
{
objectMode: true,
}
},
);
readable.once('end', () => {
this.close();
@ -198,7 +199,7 @@ export class NhentaiAppWindow extends CloudflareSiteAppWindow implements Nhentai
.webContents.executeJavaScript(
`fetch('${
nhentaiUrl + nhentaiPaths.favorites
}', {credentials: 'include', redirect: 'manual'}).then((res) => res.status)`
}', {credentials: 'include', redirect: 'manual'}).then((res) => res.status)`,
)
.then((status: number) => status === HttpCode.OK);
}
@ -225,11 +226,11 @@ export class NhentaiAppWindow extends CloudflareSiteAppWindow implements Nhentai
while (true) {
yield this.getWindow().webContents;
const hasNextPage = (await this.getWindow().webContents.executeJavaScript(
`!!document.querySelector('${nextFavoritePageSelector}')`
`!!document.querySelector('${nextFavoritePageSelector}')`,
)) as boolean;
if (hasNextPage) {
const nextPageHref = (await this.getWindow().webContents.executeJavaScript(
`document.querySelector('${nextFavoritePageSelector}').href`
`document.querySelector('${nextFavoritePageSelector}').href`,
)) as string;
await this.loadFavoritesPageSafe(nextPageHref);
} else {
@ -245,7 +246,7 @@ export class NhentaiAppWindow extends CloudflareSiteAppWindow implements Nhentai
const filePath = path.resolve(os.tmpdir(), fileName);
await this.loadGalleryPageSafe(bookUrl);
const downloadLink: string = (await this.getWindow().webContents.executeJavaScript(
`document.getElementById('${downloadLinkId}').href`
`document.getElementById('${downloadLinkId}').href`,
)) as string;
await this.downloadUrlSafe(downloadLink, filePath);
@ -283,7 +284,7 @@ export class NhentaiAppWindow extends CloudflareSiteAppWindow implements Nhentai
(tagContainer) => Array.from(tagContainer.querySelectorAll('${tagSelector}'))
).flat().map(
(tagElement) => tagElement.querySelector('${tagNameSelector}').innerHTML
)`
)`,
) as Promise<string[]>;
}
}

View File

@ -1,65 +1,40 @@
import path from 'path';
import { createWriteStream } from 'fs-extra';
import { container } from '../../core/container';
import type { DialogInterface } from '../dialog/dialog-interface';
import { answer } from '../ipc/annotations/answer';
import type { SourceGetterInterface } from '../source/source-getter-interface';
import { t } from '../../../shared/services/translation/t';
import { container, Service } from '../../core/container';
import { ipcServer } from '../ipc/ipc-server';
export class NhentaiIpcController implements IpcController {
private readonly nhentaiApi: NhentaiApiInterface;
ipcServer.answer(IpcChannel.NHENTAI_SAVE_FAVORITES, async (): Promise<void> => {
const nhentaiApi = container.get(Service.NHENTAI_API);
const dialog = container.get(Service.DIALOG);
private readonly nhentaiSourceGetter: SourceGetterInterface;
const result = await dialog.selectFolder({
title: t('imperatives.dialog.select_torrent_save_location'),
});
private readonly translator: I18nTranslatorInterface;
private readonly dialog: DialogInterface;
private constructor(
nhentaiApi: NhentaiApiInterface,
nhentaiSourceGetter: SourceGetterInterface,
translator: I18nTranslatorInterface,
dialog: DialogInterface
) {
this.nhentaiApi = nhentaiApi;
this.nhentaiSourceGetter = nhentaiSourceGetter;
this.translator = translator;
this.dialog = dialog;
if (result.canceled) {
return;
}
@answer(IpcChannel.NHENTAI_SAVE_FAVORITES)
public async nhentaiSaveFavorites(): Promise<void> {
const result = await this.dialog.selectFolder({
title: this.translator.t('Select torrent file save location'),
const favoritesStream = await nhentaiApi.getFavorites();
return new Promise((resolve) => {
favoritesStream.on('data', (favorite: Nhentai.Favorite) => {
const writable = createWriteStream(path.resolve(result.filePaths[0], favorite.name));
favorite.torrentFile.pipe(writable);
});
if (result.canceled) {
return;
}
favoritesStream.once('end', resolve);
});
});
const favoritesStream = await this.nhentaiApi.getFavorites();
ipcServer.answer(
IpcChannel.NHENTAI_GET_WORK,
async ({ galleryId }: { galleryId: string }): Promise<WorkEntityInterface> => {
const nhentaiSourceGetter = container.get(Service.NHENTAI_SOURCE_GETTER);
return new Promise((resolve) => {
favoritesStream.on('data', (favorite: Nhentai.Favorite) => {
const writable = createWriteStream(path.resolve(result.filePaths[0], favorite.name));
favorite.torrentFile.pipe(writable);
});
favoritesStream.once('end', resolve);
});
}
@answer(IpcChannel.NHENTAI_GET_WORK)
public async nhentaiGetWork({ galleryId }: { galleryId: string }): Promise<WorkEntityInterface> {
const work = await this.nhentaiSourceGetter.find(galleryId);
const work = await nhentaiSourceGetter.find(galleryId);
return work;
}
public get(): NhentaiIpcController {
const nhentaiApi: NhentaiApiInterface = container.get('nhentai-api');
const nhentaiSourceGetter: SourceGetterInterface = container.get('nhentai-source-getter');
const translator: I18nTranslatorInterface = container.get('i18n-translator');
const dialog: DialogInterface = container.get('dialog');
return new NhentaiIpcController(nhentaiApi, nhentaiSourceGetter, translator, dialog);
}
}
},
);

View File

@ -1,4 +1,5 @@
import { injectable } from 'inversify';
import { Service } from '../../core/container';
import { Database, getConnection } from '../../core/database';
import { inject } from '../../core/inject';
import { Copy } from '../../entities/library/copy';
@ -18,7 +19,7 @@ async function getLanguage(nhentaiLanguageIdentifier: NhentaiRealLanguage): Prom
export class NhentaiSourceGetter implements SourceGetterInterface {
private nhentaiApi: NhentaiApiInterface;
public constructor(@inject('nhentai-api') nhentaiApi: NhentaiApiInterface) {
public constructor(@inject(Service.NHENTAI_API) nhentaiApi: NhentaiApiInterface) {
this.nhentaiApi = nhentaiApi;
}
@ -49,7 +50,7 @@ export class NhentaiSourceGetter implements SourceGetterInterface {
return true;
}
return false;
}
},
);
work.languages = Promise.all(filteredLanguages.map((language) => getLanguage(language)));
}

View File

@ -0,0 +1,48 @@
import type { EntityTarget } from 'typeorm';
import { getRepository } from '../../core/database';
export function getDeserializedEntityPromise<T>(serializedProperty: number, entityTarget: EntityTarget<T>): Promise<T>;
export function getDeserializedEntityPromise<T>(
serializedProperty: undefined,
entityTarget: EntityTarget<T>,
): undefined;
export function getDeserializedEntityPromise<T>(serializedProperty: null, entityTarget: EntityTarget<T>): null;
export function getDeserializedEntityPromise<T>(
serializedProperty: number | undefined,
entityTarget: EntityTarget<T>,
): Promise<T> | undefined;
export function getDeserializedEntityPromise<T>(
serializedProperty: number | undefined | null,
entityTarget: EntityTarget<T>,
): Promise<T> | undefined | null;
export function getDeserializedEntityPromise<T>(
serializedProperty: number | undefined | null,
entityTarget: EntityTarget<T>,
): Promise<T> | undefined | null {
return serializedProperty === null
? null
: serializedProperty
? getRepository(entityTarget).then((repo) => repo.findOneOrFail(serializedProperty))
: undefined;
}
export function getDeserializedEntitiesPromise<T>(
serializedProperty: number[],
entityTarget: EntityTarget<T>,
): Promise<T[]>;
export function getDeserializedEntitiesPromise<T>(
serializedProperty: undefined,
entityTarget: EntityTarget<T>,
): undefined;
export function getDeserializedEntitiesPromise<T>(
serializedProperty: number[] | undefined,
entityTarget: EntityTarget<T>,
): Promise<T[]> | undefined;
export function getDeserializedEntitiesPromise<T>(
serializedProperty: number[] | undefined,
entityTarget: EntityTarget<T>,
): Promise<T[]> | undefined {
return serializedProperty
? Promise.all(serializedProperty.map((id) => getRepository(entityTarget).then((repo) => repo.findOneOrFail(id))))
: undefined;
}

View File

@ -0,0 +1,17 @@
export function serializeEntityPromise<T extends Identifier>(
entityPromise: Promise<IdentifiableInterface<T>>,
): Promise<number>;
export function serializeEntityPromise<T extends Identifier>(
entityPromise: Promise<IdentifiableInterface<T>> | null,
): Promise<T | null>;
export async function serializeEntityPromise<T extends Identifier>(
entityPromise: Promise<IdentifiableInterface<T>> | null,
): Promise<T | null> {
return entityPromise ? (await entityPromise).id : null;
}
export async function serializeEntitiesPromise<T extends Identifier>(
entitiesPromise: Promise<Array<IdentifiableInterface<T>>>,
): Promise<T[]> {
return (await entitiesPromise).map((entity) => entity.id);
}

View File

@ -0,0 +1,9 @@
import { serializeEntityPromise } from './serialize-entity-promise';
export async function serializeName(nameEntity: NameEntityInterface): Promise<NameSerializedInterface> {
return {
id: nameEntity.id,
name: nameEntity.name,
entity: await serializeEntityPromise(nameEntity.entity),
};
}

View File

@ -0,0 +1,9 @@
export abstract class Serializer<
Entity extends IdentifiableInterface<Id>,
Serialized extends IdentifiableInterface<Id>,
Id extends Identifier = number,
> {
public abstract serialize(entity: Entity): Promise<Serialized>;
public abstract deserialize(partial: Partial<Serialized>): Partial<Entity>;
}

View File

@ -0,0 +1,20 @@
import { injectable } from 'inversify';
import { Author } from '../../../entities/library/author';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class AuthorNameSerializer extends Serializer<AuthorNameEntityInterface, AuthorNameSerializedInterface> {
public serialize(entity: AuthorNameEntityInterface): Promise<AuthorNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(partial: Partial<AuthorNameSerializedInterface>): Partial<AuthorNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, Author) } : {}),
};
}
}

View File

@ -0,0 +1,23 @@
import { injectable } from 'inversify';
import { AuthorRole } from '../../../entities/library/author-role';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class AuthorRoleNameSerializer extends Serializer<
AuthorRoleNameEntityInterface,
AuthorRoleNameSerializedInterface
> {
public serialize(entity: AuthorRoleNameEntityInterface): Promise<AuthorRoleNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(partial: Partial<AuthorRoleNameSerializedInterface>): Partial<AuthorRoleNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, AuthorRole) } : {}),
};
}
}

View File

@ -0,0 +1,34 @@
import { injectable } from 'inversify';
import { AuthorRoleName } from '../../../entities/library/author-role-name';
import { WorkAuthor } from '../../../entities/library/work-author';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class AuthorRoleSerializer extends Serializer<AuthorRoleEntityInterface, AuthorRoleSerializedInterface> {
public async serialize(entity: AuthorRoleEntityInterface): Promise<AuthorRoleSerializedInterface> {
const [names, workAuthors] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.workAuthors),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
description: entity.description,
names,
workAuthors,
};
}
public deserialize(partial: Partial<AuthorRoleSerializedInterface>): Partial<AuthorRoleEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.description ? { description: partial.description } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, AuthorRoleName) } : {}),
...(partial.workAuthors ? { workAuthors: getDeserializedEntitiesPromise(partial.workAuthors, WorkAuthor) } : {}),
};
}
}

View File

@ -0,0 +1,32 @@
import { injectable } from 'inversify';
import { AuthorName } from '../../../entities/library/author-name';
import { WorkAuthor } from '../../../entities/library/work-author';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise.js';
import { Serializer } from '../serializer.js';
@injectable()
export class AuthorSerializer extends Serializer<AuthorEntityInterface, AuthorSerializedInterface> {
public async serialize(entity: AuthorEntityInterface): Promise<AuthorSerializedInterface> {
const [names, workAuthors] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.workAuthors),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
names,
workAuthors,
};
}
public deserialize(partial: Partial<AuthorSerializedInterface>): Partial<AuthorEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, AuthorName) } : {}),
...(partial.workAuthors ? { workAuthors: getDeserializedEntitiesPromise(partial.workAuthors, WorkAuthor) } : {}),
};
}
}

View File

@ -0,0 +1,34 @@
import { injectable } from 'inversify';
import { Tag } from '../../../entities/library/tag';
import { WorkCharacter } from '../../../entities/library/work-character';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class CharacterTagSerializer extends Serializer<CharacterTagEntityInterface, CharacterTagSerializedInterface> {
public async serialize(entity: CharacterTagEntityInterface): Promise<CharacterTagSerializedInterface> {
const [tag, workCharacter] = await Promise.all([
serializeEntityPromise(entity.tag),
serializeEntityPromise(entity.workCharacter),
]);
return {
id: entity.id,
weight: entity.weight,
tag,
workCharacter,
};
}
public deserialize(partial: Partial<CharacterTagSerializedInterface>): Partial<CharacterTagEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.weight ? { weight: partial.weight } : {}),
...(partial.tag ? { tag: getDeserializedEntityPromise(partial.tag, Tag) } : {}),
...(partial.workCharacter
? { workCharacter: getDeserializedEntityPromise(partial.workCharacter, WorkCharacter) }
: {}),
};
}
}

View File

@ -0,0 +1,23 @@
import { injectable } from 'inversify';
import { Collection } from '../../../entities/library/collection';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class CollectionNameSerializer extends Serializer<
CollectionNameEntityInterface,
CollectionNameSerializedInterface
> {
public serialize(entity: CollectionNameEntityInterface): Promise<CollectionNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(partial: Partial<CollectionNameSerializedInterface>): Partial<CollectionNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, Collection) } : {}),
};
}
}

View File

@ -0,0 +1,35 @@
import { injectable } from 'inversify';
import { Collection } from '../../../entities/library/collection';
import { Work } from '../../../entities/library/work';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class CollectionPartSerializer extends Serializer<
CollectionPartEntityInterface,
CollectionPartSerializedInterface
> {
public async serialize(entity: CollectionPartEntityInterface): Promise<CollectionPartSerializedInterface> {
const [collection, work] = await Promise.all([
serializeEntityPromise(entity.collection),
serializeEntityPromise(entity.work),
]);
return {
id: entity.id,
order: entity.order,
collection,
work,
};
}
public deserialize(partial: Partial<CollectionPartSerializedInterface>): Partial<CollectionPartEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.order ? { order: partial.order } : {}),
...(partial.collection ? { collection: getDeserializedEntityPromise(partial.collection, Collection) } : {}),
...(partial.work ? { work: getDeserializedEntityPromise(partial.work, Work) } : {}),
};
}
}

View File

@ -0,0 +1,32 @@
import { injectable } from 'inversify';
import { CollectionName } from '../../../entities/library/collection-name';
import { CollectionPart } from '../../../entities/library/collection-part';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class CollectionSerializer extends Serializer<CollectionEntityInterface, CollectionSerializedInterface> {
public async serialize(entity: CollectionEntityInterface): Promise<CollectionSerializedInterface> {
const [names, parts] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.parts),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
names,
parts,
};
}
public deserialize(partial: Partial<CollectionSerializedInterface>): Partial<CollectionEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, CollectionName) } : {}),
...(partial.parts ? { parts: getDeserializedEntitiesPromise(partial.parts, CollectionPart) } : {}),
};
}
}

View File

@ -0,0 +1,36 @@
import { injectable } from 'inversify';
import { Source } from '../../../entities/library/source';
import { Work } from '../../../entities/library/work';
import { getDeserializedEntitiesPromise, getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntitiesPromise, serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class CopySerializer extends Serializer<CopyEntityInterface, CopySerializedInterface> {
public async serialize(entity: CopyEntityInterface): Promise<CopySerializedInterface> {
const [original, sources] = await Promise.all([
serializeEntityPromise(entity.original),
serializeEntitiesPromise(entity.sources),
]);
return {
id: entity.id,
hash: entity.hash,
location: entity.location,
ranking: entity.ranking,
original,
sources,
};
}
public deserialize(partial: Partial<CopySerializedInterface>): Partial<CopyEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.hash ? { hash: partial.hash } : {}),
...(partial.location ? { location: partial.location } : {}),
...(partial.ranking ? { ranking: partial.ranking } : {}),
...(partial.original ? { original: getDeserializedEntityPromise(partial.original, Work) } : {}),
...(partial.sources ? { sources: getDeserializedEntitiesPromise(partial.sources, Source) } : {}),
};
}
}

View File

@ -0,0 +1,42 @@
import { injectable } from 'inversify';
import { Tag } from '../../../entities/library/tag';
import { WorkCharacter } from '../../../entities/library/work-character';
import { getDeserializedEntitiesPromise, getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntitiesPromise, serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
injectable();
export class InteractionTagSerializer extends Serializer<
InteractionTagEntityInterface,
InteractionTagSerializedInterface
> {
public async serialize(entity: InteractionTagEntityInterface): Promise<InteractionTagSerializedInterface> {
const [tag, objectCharacters, subjectCharacters] = await Promise.all([
serializeEntityPromise(entity.tag),
serializeEntitiesPromise(entity.objectCharacters),
serializeEntitiesPromise(entity.subjectCharacters),
]);
return {
id: entity.id,
weight: entity.weight,
tag,
objectCharacters,
subjectCharacters,
};
}
public deserialize(partial: Partial<InteractionTagSerializedInterface>): Partial<InteractionTagEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.weight ? { weight: partial.weight } : {}),
...(partial.tag ? { tag: getDeserializedEntityPromise(partial.tag, Tag) } : {}),
...(partial.objectCharacters
? { objectCharacters: getDeserializedEntitiesPromise(partial.objectCharacters, WorkCharacter) }
: {}),
...(partial.subjectCharacters
? { subjectCharacters: getDeserializedEntitiesPromise(partial.subjectCharacters, WorkCharacter) }
: {}),
};
}
}

View File

@ -0,0 +1,24 @@
import { injectable } from 'inversify';
import { Work } from '../../../entities/library/work';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise.js';
import { Serializer } from '../serializer.js';
@injectable()
export class LanguageSerializer extends Serializer<LanguageEntityInterface, LanguageSerializedInterface, string> {
public async serialize(entity: LanguageEntityInterface): Promise<LanguageSerializedInterface> {
return {
id: entity.code,
code: entity.code,
works: await serializeEntitiesPromise(entity.works),
};
}
public deserialize(partial: Partial<LanguageSerializedInterface>): Partial<LanguageEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.code ? { code: partial.code } : {}),
...(partial.works ? { works: getDeserializedEntitiesPromise(partial.works, Work) } : {}),
};
}
}

View File

@ -0,0 +1,20 @@
import { injectable } from 'inversify';
import { Site } from '../../../entities/library/site';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class SiteNameSerializer extends Serializer<SiteNameEntityInterface, SiteNameSerializedInterface> {
public serialize(entity: SiteNameEntityInterface): Promise<SiteNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(partial: Partial<SiteNameSerializedInterface>): Partial<SiteNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, Site) } : {}),
};
}
}

View File

@ -0,0 +1,32 @@
import { injectable } from 'inversify';
import { SiteName } from '../../../entities/library/site-name';
import { Source } from '../../../entities/library/source';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class SiteSerializer extends Serializer<SiteEntityInterface, SiteSerializedInterface> {
public async serialize(entity: SiteEntityInterface): Promise<SiteSerializedInterface> {
const [names, sources] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.sources),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
names,
sources,
};
}
public deserialize(partial: Partial<SiteSerializedInterface>): Partial<SiteEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, SiteName) } : {}),
...(partial.sources ? { sources: getDeserializedEntitiesPromise(partial.sources, Source) } : {}),
};
}
}

View File

@ -0,0 +1,32 @@
import { injectable } from 'inversify';
import { Copy } from '../../../entities/library/copy';
import { Site } from '../../../entities/library/site';
import { getDeserializedEntitiesPromise, getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntitiesPromise, serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
injectable();
export class SourceSerializer extends Serializer<SourceEntityInterface, SourceSerializedInterface> {
public async serialize(entity: SourceEntityInterface): Promise<SourceSerializedInterface> {
const [copies, site] = await Promise.all([
serializeEntitiesPromise(entity.copies),
serializeEntityPromise(entity.site),
]);
return {
id: entity.id,
uri: entity.uri,
copies,
site,
};
}
public deserialize(partial: Partial<SourceSerializedInterface>): Partial<SourceEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.uri ? { uri: partial.uri } : {}),
...(partial.site ? { site: getDeserializedEntityPromise(partial.site, Site) } : {}),
...(partial.copies ? { copies: getDeserializedEntitiesPromise(partial.copies, Copy) } : {}),
};
}
}

View File

@ -0,0 +1,20 @@
import { injectable } from 'inversify';
import { Tag } from '../../../entities/library/tag';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class TagNameSerializer extends Serializer<TagNameEntityInterface, TagNameSerializedInterface> {
public serialize(entity: TagNameEntityInterface): Promise<TagNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(partial: Partial<TagNameSerializedInterface>): Partial<TagNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, Tag) } : {}),
};
}
}

View File

@ -0,0 +1,53 @@
import { injectable } from 'inversify';
import { CharacterTag } from '../../../entities/library/character-tag';
import { InteractionTag } from '../../../entities/library/interaction-tag';
import { Tag } from '../../../entities/library/tag';
import { TagName } from '../../../entities/library/tag-name';
import { WorkTag } from '../../../entities/library/work-tag';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class TagSerializer extends Serializer<TagEntityInterface, TagSerializedInterface> {
public async serialize(entity: TagEntityInterface): Promise<TagSerializedInterface> {
const [names, characterTags, children, parents, interactionTags, workTags] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.characterTags),
serializeEntitiesPromise(entity.children),
serializeEntitiesPromise(entity.parents),
serializeEntitiesPromise(entity.interactionTags),
serializeEntitiesPromise(entity.workTags),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
description: entity.description,
names,
characterTags,
children,
parents,
interactionTags,
workTags,
};
}
public deserialize(partial: Partial<TagSerializedInterface>): Partial<TagEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.description ? { description: partial.description } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, TagName) } : {}),
...(partial.children ? { children: getDeserializedEntitiesPromise(partial.children, Tag) } : {}),
...(partial.parents ? { parents: getDeserializedEntitiesPromise(partial.parents, Tag) } : {}),
...(partial.workTags ? { workTags: getDeserializedEntitiesPromise(partial.workTags, WorkTag) } : {}),
...(partial.interactionTags
? { interactionTags: getDeserializedEntitiesPromise(partial.interactionTags, InteractionTag) }
: {}),
...(partial.characterTags
? { characterTags: getDeserializedEntitiesPromise(partial.characterTags, CharacterTag) }
: {}),
};
}
}

View File

@ -0,0 +1,38 @@
import { injectable } from 'inversify';
import { TransformationType } from '../../../entities/library/transformation-type';
import { Work } from '../../../entities/library/work';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class TransformationSerializer extends Serializer<
TransformationEntityInterface,
TransformationSerializedInterface
> {
public async serialize(entity: TransformationEntityInterface): Promise<TransformationSerializedInterface> {
const [byWork, ofWork, type] = await Promise.all([
serializeEntityPromise(entity.byWork),
serializeEntityPromise(entity.ofWork),
serializeEntityPromise(entity.type),
]);
return {
id: entity.id,
order: entity.order,
byWork,
ofWork,
type,
};
}
public deserialize(partial: Partial<TransformationSerializedInterface>): Partial<TransformationEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.order ? { order: partial.order } : {}),
...(partial.type ? { type: getDeserializedEntityPromise(partial.type, TransformationType) } : {}),
...(partial.ofWork ? { ofWork: getDeserializedEntityPromise(partial.ofWork, Work) } : {}),
...(partial.byWork ? { byWork: getDeserializedEntityPromise(partial.byWork, Work) } : {}),
};
}
}

View File

@ -0,0 +1,25 @@
import { injectable } from 'inversify';
import { TransformationType } from '../../../entities/library/transformation-type';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class TransformationTypeNameSerializer extends Serializer<
TransformationTypeNameEntityInterface,
TransformationTypeNameSerializedInterface
> {
public serialize(entity: TransformationTypeNameEntityInterface): Promise<TransformationTypeNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(
partial: Partial<TransformationTypeNameSerializedInterface>,
): Partial<TransformationTypeNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, TransformationType) } : {}),
};
}
}

View File

@ -0,0 +1,43 @@
import { injectable } from 'inversify';
import { Transformation } from '../../../entities/library/transformation';
import { TransformationTypeName } from '../../../entities/library/transformation-type-name';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class TransformationTypeSerializer extends Serializer<
TransformationTypeEntityInterface,
TransformationTypeSerializedInterface
> {
public async serialize(entity: TransformationTypeEntityInterface): Promise<TransformationTypeSerializedInterface> {
const [names, transformations] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.transformations),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
description: entity.description,
conservesTags: entity.conservesTags,
names,
transformations,
};
}
public deserialize(
partial: Partial<TransformationTypeSerializedInterface>,
): Partial<TransformationTypeEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.description ? { description: partial.description } : {}),
...(partial.conservesTags ? { conservesTags: partial.conservesTags } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, TransformationTypeName) } : {}),
...(partial.transformations
? { transformations: getDeserializedEntitiesPromise(partial.transformations, Transformation) }
: {}),
};
}
}

View File

@ -0,0 +1,36 @@
import { injectable } from 'inversify';
import { Author } from '../../../entities/library/author';
import { AuthorRole } from '../../../entities/library/author-role';
import { Work } from '../../../entities/library/work';
import { getDeserializedEntitiesPromise, getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntitiesPromise, serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class WorkAuthorSerializer extends Serializer<WorkAuthorEntityInterface, WorkAuthorSerializedInterface> {
public async serialize(entity: WorkAuthorEntityInterface): Promise<WorkAuthorSerializedInterface> {
const [author, work, authorRoles] = await Promise.all([
serializeEntityPromise(entity.author),
serializeEntityPromise(entity.work),
serializeEntitiesPromise(entity.authorRoles),
]);
return {
id: entity.id,
order: entity.order,
author,
work,
authorRoles,
};
}
public deserialize(partial: Partial<WorkAuthorSerializedInterface>): Partial<WorkAuthorEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.work ? { order: partial.work } : {}),
...(partial.work ? { work: getDeserializedEntityPromise(partial.work, Work) } : {}),
...(partial.author ? { author: getDeserializedEntityPromise(partial.author, Author) } : {}),
...(partial.authorRoles ? { authorRoles: getDeserializedEntitiesPromise(partial.authorRoles, AuthorRole) } : {}),
};
}
}

View File

@ -0,0 +1,25 @@
import { injectable } from 'inversify';
import { WorkCharacter } from '../../../entities/library/work-character';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class WorkCharacterNameSerializer extends Serializer<
WorkCharacterNameEntityInterface,
WorkCharacterNameSerializedInterface
> {
public serialize(entity: WorkCharacterNameEntityInterface): Promise<WorkCharacterNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(
partial: Partial<WorkCharacterNameSerializedInterface>,
): Partial<WorkCharacterNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, WorkCharacter) } : {}),
};
}
}

View File

@ -0,0 +1,58 @@
import { injectable } from 'inversify';
import { CharacterTag } from '../../../entities/library/character-tag';
import { InteractionTag } from '../../../entities/library/interaction-tag';
import { Work } from '../../../entities/library/work';
import { WorkCharacterName } from '../../../entities/library/work-character-name';
import { WorldCharacter } from '../../../entities/library/world-character';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class WorkCharacterSerializer extends Serializer<
WorkCharacterEntityInterface,
WorkCharacterSerializedInterface
> {
public async serialize(entity: WorkCharacterEntityInterface): Promise<WorkCharacterSerializedInterface> {
const [names, characterTags, interactedBy, works, interactWith, worldCharacters] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.characterTags),
serializeEntitiesPromise(entity.interactedBy),
serializeEntitiesPromise(entity.works),
serializeEntitiesPromise(entity.interactWith),
serializeEntitiesPromise(entity.worldCharacters),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
names,
characterTags,
interactedBy,
works,
interactWith,
worldCharacters,
};
}
public deserialize(partial: Partial<WorkCharacterSerializedInterface>): Partial<WorkCharacterEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, WorkCharacterName) } : {}),
...(partial.works ? { works: getDeserializedEntitiesPromise(partial.works, Work) } : {}),
...(partial.characterTags
? { characterTags: getDeserializedEntitiesPromise(partial.characterTags, CharacterTag) }
: {}),
...(partial.worldCharacters
? { worldCharacters: getDeserializedEntitiesPromise(partial.worldCharacters, WorldCharacter) }
: {}),
...(partial.interactedBy
? { interactedBy: getDeserializedEntitiesPromise(partial.interactedBy, InteractionTag) }
: {}),
...(partial.interactWith
? { interactWith: getDeserializedEntitiesPromise(partial.interactWith, InteractionTag) }
: {}),
};
}
}

View File

@ -0,0 +1,20 @@
import { injectable } from 'inversify';
import { Work } from '../../../entities/library/work';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class WorkNameSerializer extends Serializer<WorkNameEntityInterface, WorkNameSerializedInterface> {
public serialize(entity: WorkNameEntityInterface): Promise<WorkNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(partial: Partial<WorkNameSerializedInterface>): Partial<WorkNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, Work) } : {}),
};
}
}

View File

@ -0,0 +1,82 @@
import { injectable } from 'inversify';
import { Copy } from '../../../entities/library/copy';
import { Transformation } from '../../../entities/library/transformation';
import { WorkAuthor } from '../../../entities/library/work-author';
import { WorkCharacter } from '../../../entities/library/work-character';
import { WorkName } from '../../../entities/library/work-name';
import { WorkTag } from '../../../entities/library/work-tag';
import { World } from '../../../entities/library/world';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise.js';
import { Serializer } from '../serializer.js';
@injectable()
export class WorkSerializer extends Serializer<WorkEntityInterface, WorkSerializedInterface> {
public async serialize(entity: WorkEntityInterface): Promise<WorkSerializedInterface> {
const [
languages,
collectionParts,
copies,
names,
transformationOf,
transformedBy,
workAuthors,
workCharacters,
workTags,
worlds,
] = await Promise.all([
serializeEntitiesPromise(entity.languages),
serializeEntitiesPromise(entity.collectionParts),
serializeEntitiesPromise(entity.copies),
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.transformationOf),
serializeEntitiesPromise(entity.transformedBy),
serializeEntitiesPromise(entity.workAuthors),
serializeEntitiesPromise(entity.workCharacters),
serializeEntitiesPromise(entity.workTags),
serializeEntitiesPromise(entity.worlds),
]);
return {
id: entity.id,
isCanonical: entity.isCanonical,
nameCanonical: entity.nameCanonical,
rating: entity.rating,
releaseDate: entity.releaseDate,
languages,
collectionParts,
copies,
names,
transformationOf,
transformedBy,
workAuthors,
workCharacters,
workTags,
worlds,
};
}
public deserialize(partial: Partial<WorkSerializedInterface>): Partial<WorkEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.rating ? { rating: partial.rating } : {}),
...(partial.releaseDate ? { releaseDate: partial.releaseDate } : {}),
...(partial.isCanonical ? { isCanonical: partial.isCanonical } : {}),
...(partial.copies ? { copies: getDeserializedEntitiesPromise(partial.copies, Copy) } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, WorkName) } : {}),
...(partial.transformationOf
? { transformationOf: getDeserializedEntitiesPromise(partial.transformationOf, Transformation) }
: {}),
...(partial.transformedBy
? { transformedBy: getDeserializedEntitiesPromise(partial.transformedBy, Transformation) }
: {}),
...(partial.workAuthors ? { workAuthors: getDeserializedEntitiesPromise(partial.workAuthors, WorkAuthor) } : {}),
...(partial.workCharacters
? { workCharacters: getDeserializedEntitiesPromise(partial.workCharacters, WorkCharacter) }
: {}),
...(partial.workTags ? { workTags: getDeserializedEntitiesPromise(partial.workTags, WorkTag) } : {}),
...(partial.worlds ? { worlds: getDeserializedEntitiesPromise(partial.worlds, World) } : {}),
};
}
}

View File

@ -0,0 +1,29 @@
import { injectable } from 'inversify';
import { Tag } from '../../../entities/library/tag';
import { Work } from '../../../entities/library/work';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeEntityPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class WorkTagSerializer extends Serializer<WorkTagEntityInterface, WorkTagSerializedInterface> {
public async serialize(entity: WorkTagEntityInterface): Promise<WorkTagSerializedInterface> {
const [tag, work] = await Promise.all([serializeEntityPromise(entity.tag), serializeEntityPromise(entity.work)]);
return {
id: entity.id,
weight: entity.weight,
tag,
work,
};
}
public deserialize(partial: Partial<WorkTagSerializedInterface>): Partial<WorkTagEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.weight ? { weight: partial.weight } : {}),
...(partial.tag ? { tag: getDeserializedEntityPromise(partial.tag, Tag) } : {}),
...(partial.work ? { work: getDeserializedEntityPromise(partial.work, Work) } : {}),
};
}
}

View File

@ -0,0 +1,25 @@
import { injectable } from 'inversify';
import { WorldCharacter } from '../../../entities/library/world-character';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class WorldCharacterNameSerializer extends Serializer<
WorldCharacterNameEntityInterface,
WorldCharacterNameSerializedInterface
> {
public serialize(entity: WorldCharacterNameEntityInterface): Promise<WorldCharacterNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(
partial: Partial<WorldCharacterNameSerializedInterface>,
): Partial<WorldCharacterNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, WorldCharacter) } : {}),
};
}
}

View File

@ -0,0 +1,48 @@
import { injectable } from 'inversify';
import { WorkCharacter } from '../../../entities/library/work-character';
import { World } from '../../../entities/library/world';
import { WorldCharacter } from '../../../entities/library/world-character';
import { WorldCharacterName } from '../../../entities/library/world-character-name';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class WorldCharacterSerializer extends Serializer<
WorldCharacterEntityInterface,
WorldCharacterSerializedInterface
> {
public async serialize(entity: WorldCharacterEntityInterface): Promise<WorldCharacterSerializedInterface> {
const [names, children, parents, workCharacters, worlds] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.children),
serializeEntitiesPromise(entity.parents),
serializeEntitiesPromise(entity.workCharacters),
serializeEntitiesPromise(entity.worlds),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
names,
children,
parents,
workCharacters,
worlds,
};
}
public deserialize(partial: Partial<WorldCharacterSerializedInterface>): Partial<WorldCharacterEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, WorldCharacterName) } : {}),
...(partial.children ? { children: getDeserializedEntitiesPromise(partial.children, WorldCharacter) } : {}),
...(partial.parents ? { parents: getDeserializedEntitiesPromise(partial.parents, WorldCharacter) } : {}),
...(partial.workCharacters
? { workCharacters: getDeserializedEntitiesPromise(partial.workCharacters, WorkCharacter) }
: {}),
...(partial.worlds ? { worlds: getDeserializedEntitiesPromise(partial.worlds, World) } : {}),
};
}
}

View File

@ -0,0 +1,20 @@
import { injectable } from 'inversify';
import { World } from '../../../entities/library/world';
import { getDeserializedEntityPromise } from '../get-deserialized';
import { serializeName } from '../serialize-name';
import { Serializer } from '../serializer';
@injectable()
export class WorldNameSerializer extends Serializer<WorldNameEntityInterface, WorldNameSerializedInterface> {
public serialize(entity: WorldNameEntityInterface): Promise<WorldNameSerializedInterface> {
return serializeName(entity);
}
public deserialize(partial: Partial<WorldNameSerializedInterface>): Partial<WorldNameEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.name ? { name: partial.name } : {}),
...(partial.entity ? { entity: getDeserializedEntityPromise(partial.entity, World) } : {}),
};
}
}

View File

@ -0,0 +1,45 @@
import { injectable } from 'inversify';
import { Work } from '../../../entities/library/work';
import { World } from '../../../entities/library/world';
import { WorldCharacter } from '../../../entities/library/world-character';
import { WorldName } from '../../../entities/library/world-name';
import { getDeserializedEntitiesPromise } from '../get-deserialized';
import { serializeEntitiesPromise } from '../serialize-entity-promise';
import { Serializer } from '../serializer';
@injectable()
export class WorldSerializer extends Serializer<WorldEntityInterface, WorldSerializedInterface> {
public async serialize(entity: WorldEntityInterface): Promise<WorldSerializedInterface> {
const [names, children, parents, works, worldCharacters] = await Promise.all([
serializeEntitiesPromise(entity.names),
serializeEntitiesPromise(entity.children),
serializeEntitiesPromise(entity.parents),
serializeEntitiesPromise(entity.works),
serializeEntitiesPromise(entity.worldCharacters),
]);
return {
id: entity.id,
nameCanonical: entity.nameCanonical,
names,
children,
parents,
works,
worldCharacters,
};
}
public deserialize(partial: Partial<WorldSerializedInterface>): Partial<WorldEntityInterface> {
return {
...(partial.id ? { id: partial.id } : {}),
...(partial.nameCanonical ? { nameCanonical: partial.nameCanonical } : {}),
...(partial.names ? { names: getDeserializedEntitiesPromise(partial.names, WorldName) } : {}),
...(partial.children ? { children: getDeserializedEntitiesPromise(partial.children, World) } : {}),
...(partial.parents ? { parents: getDeserializedEntitiesPromise(partial.parents, World) } : {}),
...(partial.works ? { works: getDeserializedEntitiesPromise(partial.works, Work) } : {}),
...(partial.worldCharacters
? { worldCharacters: getDeserializedEntitiesPromise(partial.worldCharacters, WorldCharacter) }
: {}),
};
}
}

View File

@ -11,7 +11,7 @@ function stringifyCspHeader(csp: ContentSecurityPolicy): string {
return Object.entries(csp)
.map(
(directive: [string, Session.CspValue[] | undefined]) =>
`${directive[0]} ${directive[1] ? directive[1]?.join(' ') : ''}`
`${directive[0]} ${directive[1] ? directive[1]?.join(' ') : ''}`,
)
.join('; ');
}

View File

@ -1,12 +1,12 @@
import { expect } from 'chai';
import 'mocha';
import { container } from '../../core/container';
import { container, Service } from '../../core/container';
describe('Store Service', function () {
this.timeout(10000);
it('loads saved data', () => {
const store: StoreInterface = container.get('store');
const store: StoreInterface = container.get(Service.STORE);
const testData = {
something: 'gaga',
somethingElse: 0,

View File

@ -1,5 +1,5 @@
import App from './renderer/App.svelte';
new App({
target: document.querySelector('#app'),
target: document.querySelector('#app') as Element,
});

View File

@ -3,6 +3,11 @@
import NhentaiSaveFavorites from './components/modules/NhentaiSaveFavorites.svelte';
</script>
<main>
<NhentaiSaveFavorites />
<NhentaiGetWork />
</main>
<style>
:global(:root) {
--color-white: #fff;
@ -43,8 +48,3 @@
height: 100%;
}
</style>
<main>
<NhentaiSaveFavorites></NhentaiSaveFavorites>
<NhentaiGetWork />
</main>

View File

@ -1,16 +1,48 @@
<script>
import { onMount } from 'svelte';
import { entityApi } from '../../services/api';
import { onDestroy } from 'svelte';
import { workRepository } from '../../store/repositories/entities/work-repository';
export let id;
export let id = 0;
/** @type {WorkSerializedInterface | undefined} */
let work;
let unsubscribe = () => {};
onMount(() => {
entityApi.fetchWork(id).then((workSerialized) => {
work = workSerialized;
});
$: unsubscribe = workRepository.subscribe(id, (serialized) => {
work = serialized;
});
onDestroy(() => {
unsubscribe();
});
</script>
<div class="work">{#if work}{JSON.stringify(work)}{/if}</div>
<div class="work">
{#if work}
<dl>
<div>
<dt>id</dt>
<dd>{work?.id}</dd>
</div>
<div>
<dt>name</dt>
<dd>{work?.nameCanonical}</dd>
</div>
<div>
<input
type="text"
on:change="{(input) => {
workRepository.update(id, (w) => {
w.nameCanonical = input.target.value;
return w;
});
}}"
/>
</div>
<div>
<dt>languages</dt>
<dd>{work?.languages.join(', ')}</dd>
</div>
</dl>
{/if}
</div>

View File

@ -1,3 +1,7 @@
<button class="button" on:click|preventDefault>
<slot />
</button>
<style>
.button {
border: none;
@ -9,7 +13,3 @@
outline-color: var(--color-accent-light);
}
</style>
<button class="button" on:click|preventDefault>
<slot></slot>
</button>

View File

@ -1,8 +1,8 @@
<script>
import Work from '../content/Work.svelte';
import { t } from '../../../shared/services/translation/t';
import { nhentaiGetWork } from '../../services/api';
import Work from '../content/Work.svelte';
import SvelteButton from '../elements/SvelteButton.svelte';
import { t } from '../../services/utils';
let galleryId;
let work;
@ -13,9 +13,10 @@
</script>
<div class="nhentai-get-work">
<label><input type="text" placeholder="177013" bind:value="{galleryId}" /></label
><SvelteButton on:click="{handleClick}">{t('Get')}</SvelteButton>
<input type="text" placeholder="177013" bind:value="{galleryId}" /><SvelteButton on:click="{handleClick}"
>{t('labels.actions.get')}</SvelteButton
>
{#if work}
<Work id="{work.id}" />
<Work id="{work.id}" />
{/if}
</div>

View File

@ -1,15 +1,13 @@
<script>
import { t } from '../../services/utils';
import SvelteButton from '../elements/SvelteButton.svelte';
import { t } from '../../../shared/services/translation/t';
import { nhentaiSaveFavorites } from '../../services/api';
import SvelteButton from '../elements/SvelteButton.svelte';
function handleClick() {
nhentaiSaveFavorites();
}
</script>
<style></style>
<div class="nhentai-login">
<SvelteButton on:click="{handleClick}">{ t('Save nhentai Favorites') }</SvelteButton>
<SvelteButton on:click="{handleClick}">{t('labels.actions.save_nhentai_favorites')}</SvelteButton>
</div>

View File

@ -1,15 +1,493 @@
import { ipcClient } from './ipc-client';
export function nhentaiSaveFavorites(): Promise<void> {
return ipcClient.ask(IpcChannel.NHENTAI_SAVE_FAVORITES) as Promise<void>;
return ipcClient.ask(IpcChannel.NHENTAI_SAVE_FAVORITES, undefined);
}
export function nhentaiGetWork(galleryId: string): Promise<WorkEntityInterface> {
return ipcClient.ask(IpcChannel.NHENTAI_GET_WORK, { galleryId }) as Promise<WorkEntityInterface>;
return ipcClient.ask(IpcChannel.NHENTAI_GET_WORK, { galleryId });
}
type IpcChannelEntityRead =
| IpcChannel.ENTITY_READ_AUTHOR
| IpcChannel.ENTITY_READ_AUTHOR_NAME
| IpcChannel.ENTITY_READ_AUTHOR_ROLE
| IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME
| IpcChannel.ENTITY_READ_CHARACTER_TAG
| IpcChannel.ENTITY_READ_COLLECTION
| IpcChannel.ENTITY_READ_COLLECTION_NAME
| IpcChannel.ENTITY_READ_COLLECTION_PART
| IpcChannel.ENTITY_READ_COPY
| IpcChannel.ENTITY_READ_INTERACTION_TAG
| IpcChannel.ENTITY_READ_LANGUAGE
| IpcChannel.ENTITY_READ_SITE
| IpcChannel.ENTITY_READ_SITE_NAME
| IpcChannel.ENTITY_READ_SOURCE
| IpcChannel.ENTITY_READ_TAG
| IpcChannel.ENTITY_READ_TAG_NAME
| IpcChannel.ENTITY_READ_TRANSFORMATION
| IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE
| IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME
| IpcChannel.ENTITY_READ_WORK_AUTHOR
| IpcChannel.ENTITY_READ_WORK_CHARACTER
| IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME
| IpcChannel.ENTITY_READ_WORK
| IpcChannel.ENTITY_READ_WORK_NAME
| IpcChannel.ENTITY_READ_WORK_TAG
| IpcChannel.ENTITY_READ_WORLD
| IpcChannel.ENTITY_READ_WORLD_NAME
| IpcChannel.ENTITY_READ_WORLD_CHARACTER
| IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME;
/**
* when there are duplicate requests to the same entity, the existing promise is returned
*
* this promise is deleted once resolved, this is not a cache!
*/
const entityReadPromiseCollector: {
[channel in IpcChannelEntityRead]: {
[identifier in Identifier]?: Promise<IpcAnswer<channel>>;
};
} = {
[IpcChannel.ENTITY_READ_AUTHOR]: {},
[IpcChannel.ENTITY_READ_AUTHOR_NAME]: {},
[IpcChannel.ENTITY_READ_AUTHOR_ROLE]: {},
[IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME]: {},
[IpcChannel.ENTITY_READ_CHARACTER_TAG]: {},
[IpcChannel.ENTITY_READ_COLLECTION]: {},
[IpcChannel.ENTITY_READ_COLLECTION_NAME]: {},
[IpcChannel.ENTITY_READ_COLLECTION_PART]: {},
[IpcChannel.ENTITY_READ_COPY]: {},
[IpcChannel.ENTITY_READ_INTERACTION_TAG]: {},
[IpcChannel.ENTITY_READ_LANGUAGE]: {},
[IpcChannel.ENTITY_READ_SITE]: {},
[IpcChannel.ENTITY_READ_SITE_NAME]: {},
[IpcChannel.ENTITY_READ_SOURCE]: {},
[IpcChannel.ENTITY_READ_TAG]: {},
[IpcChannel.ENTITY_READ_TAG_NAME]: {},
[IpcChannel.ENTITY_READ_TRANSFORMATION]: {},
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE]: {},
[IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME]: {},
[IpcChannel.ENTITY_READ_WORK_AUTHOR]: {},
[IpcChannel.ENTITY_READ_WORK_CHARACTER]: {},
[IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME]: {},
[IpcChannel.ENTITY_READ_WORK]: {},
[IpcChannel.ENTITY_READ_WORK_NAME]: {},
[IpcChannel.ENTITY_READ_WORK_TAG]: {},
[IpcChannel.ENTITY_READ_WORLD]: {},
[IpcChannel.ENTITY_READ_WORLD_NAME]: {},
[IpcChannel.ENTITY_READ_WORLD_CHARACTER]: {},
[IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME]: {},
};
function readCollected<T extends IpcChannelEntityRead>(channel: T, identifier: IpcParameter<T>): Promise<IpcAnswer<T>> {
if (entityReadPromiseCollector[channel][identifier] === undefined) {
// @ts-ignore -- no fucking clue how to type this correctly
entityReadPromiseCollector[channel][identifier] = ipcClient.ask<T>(channel, identifier).then((value) => {
delete entityReadPromiseCollector[channel][identifier];
return value;
});
}
return entityReadPromiseCollector[channel][identifier] as unknown as Promise<IpcAnswer<T>>;
}
export const entityApi = {
fetchWork(id: number): Promise<WorkSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_GET_WORK, { id });
createAuthor(data: Partial<AuthorSerializedInterface>): Promise<AuthorSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_AUTHOR, data);
},
createAuthorName(data: Partial<AuthorNameSerializedInterface>): Promise<AuthorNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_AUTHOR_NAME, data);
},
createAuthorRole(data: Partial<AuthorRoleSerializedInterface>): Promise<AuthorRoleSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_AUTHOR_ROLE, data);
},
createAuthorRoleName(data: Partial<AuthorRoleNameSerializedInterface>): Promise<AuthorRoleNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_AUTHOR_ROLE_NAME, data);
},
createCharacterTag(data: Partial<CharacterTagSerializedInterface>): Promise<CharacterTagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_CHARACTER_TAG, data);
},
createCollection(data: Partial<CollectionSerializedInterface>): Promise<CollectionSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_COLLECTION, data);
},
createCollectionName(data: Partial<CollectionNameSerializedInterface>): Promise<CollectionNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_COLLECTION_NAME, data);
},
createCollectionPart(data: Partial<CollectionPartSerializedInterface>): Promise<CollectionPartSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_COLLECTION_PART, data);
},
createCopy(data: Partial<CopySerializedInterface>): Promise<CopySerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_COPY, data);
},
createInteractionTag(data: Partial<InteractionTagSerializedInterface>): Promise<InteractionTagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_INTERACTION_TAG, data);
},
createSite(data: Partial<SiteSerializedInterface>): Promise<SiteSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_SITE, data);
},
createSiteName(data: Partial<SiteNameSerializedInterface>): Promise<SiteNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_SITE_NAME, data);
},
createSource(data: Partial<SourceSerializedInterface>): Promise<SourceSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_SOURCE, data);
},
createTag(data: Partial<TagSerializedInterface>): Promise<TagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_TAG, data);
},
createTagName(data: Partial<TagNameSerializedInterface>): Promise<TagNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_TAG_NAME, data);
},
createTransformation(data: Partial<TransformationSerializedInterface>): Promise<TransformationSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_TRANSFORMATION, data);
},
createTransformationType(
data: Partial<TransformationTypeSerializedInterface>,
): Promise<TransformationTypeSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE, data);
},
createTransformationTypeName(
data: Partial<TransformationTypeNameSerializedInterface>,
): Promise<TransformationTypeNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_TRANSFORMATION_TYPE_NAME, data);
},
createWorkAuthor(data: Partial<WorkAuthorSerializedInterface>): Promise<WorkAuthorSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORK_AUTHOR, data);
},
createWorkCharacter(data: Partial<WorkCharacterSerializedInterface>): Promise<WorkCharacterSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORK_CHARACTER, data);
},
createWorkCharacterName(
data: Partial<WorkCharacterNameSerializedInterface>,
): Promise<WorkCharacterNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORK_CHARACTER_NAME, data);
},
createWork(data: Partial<WorkSerializedInterface>): Promise<WorkSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORK, data);
},
createWorkName(data: Partial<WorkNameSerializedInterface>): Promise<WorkNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORK_NAME, data);
},
createWorkTag(data: Partial<WorkTagSerializedInterface>): Promise<WorkTagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORK_TAG, data);
},
createWorld(data: Partial<WorldSerializedInterface>): Promise<WorldSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORLD, data);
},
createWorldName(data: Partial<WorldNameSerializedInterface>): Promise<WorldNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORLD_NAME, data);
},
createWorldCharacter(data: Partial<WorldCharacterSerializedInterface>): Promise<WorldCharacterSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORLD_CHARACTER, data);
},
createWorldCharacterName(
data: Partial<WorldCharacterNameSerializedInterface>,
): Promise<WorldCharacterNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_CREATE_WORLD_CHARACTER_NAME, data);
},
readAuthor(id: number): Promise<AuthorSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_AUTHOR, id);
},
readAuthorName(id: number): Promise<AuthorNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_AUTHOR_NAME, id);
},
readAuthorRole(id: number): Promise<AuthorRoleSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_AUTHOR_ROLE, id);
},
readAuthorRoleName(id: number): Promise<AuthorRoleNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_AUTHOR_ROLE_NAME, id);
},
readCharacterTag(id: number): Promise<CharacterTagSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_CHARACTER_TAG, id);
},
readCollection(id: number): Promise<CollectionSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_COLLECTION, id);
},
readCollectionName(id: number): Promise<CollectionNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_COLLECTION_NAME, id);
},
readCollectionPart(id: number): Promise<CollectionPartSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_COLLECTION_PART, id);
},
readCopy(id: number): Promise<CopySerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_COPY, id);
},
readInteractionTag(id: number): Promise<InteractionTagSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_INTERACTION_TAG, id);
},
readLanguage(code: string): Promise<LanguageSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_LANGUAGE, code);
},
readSite(id: number): Promise<SiteSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_SITE, id);
},
readSiteName(id: number): Promise<SiteNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_SITE_NAME, id);
},
readSource(id: number): Promise<SourceSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_SOURCE, id);
},
readTag(id: number): Promise<TagSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_TAG, id);
},
readTagName(id: number): Promise<TagNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_TAG_NAME, id);
},
readTransformation(id: number): Promise<TransformationSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_TRANSFORMATION, id);
},
readTransformationType(id: number): Promise<TransformationTypeSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE, id);
},
readTransformationTypeName(id: number): Promise<TransformationTypeNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_TRANSFORMATION_TYPE_NAME, id);
},
readWorkAuthor(id: number): Promise<WorkAuthorSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORK_AUTHOR, id);
},
readWorkCharacter(id: number): Promise<WorkCharacterSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORK_CHARACTER, id);
},
readWorkCharacterName(id: number): Promise<WorkCharacterNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORK_CHARACTER_NAME, id);
},
readWork(id: number): Promise<WorkSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORK, id);
},
readWorkName(id: number): Promise<WorkNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORK_NAME, id);
},
readWorkTag(id: number): Promise<WorkTagSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORK_TAG, id);
},
readWorld(id: number): Promise<WorldSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORLD, id);
},
readWorldName(id: number): Promise<WorldNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORLD_NAME, id);
},
readWorldCharacter(id: number): Promise<WorldCharacterSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORLD_CHARACTER, id);
},
readWorldCharacterName(id: number): Promise<WorldCharacterNameSerializedInterface> {
return readCollected(IpcChannel.ENTITY_READ_WORLD_CHARACTER_NAME, id);
},
updateAuthor(id: number, partial: Partial<AuthorSerializedInterface>): Promise<AuthorSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_AUTHOR, { id, partial });
},
updateAuthorName(
id: number,
partial: Partial<AuthorNameSerializedInterface>,
): Promise<AuthorNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_AUTHOR_NAME, { id, partial });
},
updateAuthorRole(
id: number,
partial: Partial<AuthorRoleSerializedInterface>,
): Promise<AuthorRoleSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE, { id, partial });
},
updateAuthorRoleName(
id: number,
partial: Partial<AuthorRoleNameSerializedInterface>,
): Promise<AuthorRoleNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_AUTHOR_ROLE_NAME, { id, partial });
},
updateCharacterTag(
id: number,
partial: Partial<CharacterTagSerializedInterface>,
): Promise<CharacterTagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_CHARACTER_TAG, { id, partial });
},
updateCollection(
id: number,
partial: Partial<CollectionSerializedInterface>,
): Promise<CollectionSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_COLLECTION, { id, partial });
},
updateCollectionName(
id: number,
partial: Partial<CollectionNameSerializedInterface>,
): Promise<CollectionNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_COLLECTION_NAME, { id, partial });
},
updateCollectionPart(
id: number,
partial: Partial<CollectionPartSerializedInterface>,
): Promise<CollectionPartSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_COLLECTION_PART, { id, partial });
},
updateCopy(id: number, partial: Partial<CopySerializedInterface>): Promise<CopySerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_COPY, { id, partial });
},
updateInteractionTag(
id: number,
partial: Partial<InteractionTagSerializedInterface>,
): Promise<InteractionTagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_INTERACTION_TAG, { id, partial });
},
updateSite(id: number, partial: Partial<SiteSerializedInterface>): Promise<SiteSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_SITE, { id, partial });
},
updateSiteName(id: number, partial: Partial<SiteNameSerializedInterface>): Promise<SiteNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_SITE_NAME, { id, partial });
},
updateSource(id: number, partial: Partial<SourceSerializedInterface>): Promise<SourceSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_SOURCE, { id, partial });
},
updateTag(id: number, partial: Partial<TagSerializedInterface>): Promise<TagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_TAG, { id, partial });
},
updateTagName(id: number, partial: Partial<TagNameSerializedInterface>): Promise<TagNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_TAG_NAME, { id, partial });
},
updateTransformation(
id: number,
partial: Partial<TransformationSerializedInterface>,
): Promise<TransformationSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_TRANSFORMATION, { id, partial });
},
updateTransformationType(
id: number,
partial: Partial<TransformationTypeSerializedInterface>,
): Promise<TransformationTypeSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE, { id, partial });
},
updateTransformationTypeName(
id: number,
partial: Partial<TransformationTypeNameSerializedInterface>,
): Promise<TransformationTypeNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_TRANSFORMATION_TYPE_NAME, { id, partial });
},
updateWorkAuthor(
id: number,
partial: Partial<WorkAuthorSerializedInterface>,
): Promise<WorkAuthorSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORK_AUTHOR, { id, partial });
},
updateWorkCharacter(
id: number,
partial: Partial<WorkCharacterSerializedInterface>,
): Promise<WorkCharacterSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORK_CHARACTER, { id, partial });
},
updateWorkCharacterName(
id: number,
partial: Partial<WorkCharacterNameSerializedInterface>,
): Promise<WorkCharacterNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORK_CHARACTER_NAME, { id, partial });
},
updateWork(id: number, partial: Partial<WorkSerializedInterface>): Promise<WorkSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORK, { id, partial });
},
updateWorkName(id: number, partial: Partial<WorkNameSerializedInterface>): Promise<WorkNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORK_NAME, { id, partial });
},
updateWorkTag(id: number, partial: Partial<WorkTagSerializedInterface>): Promise<WorkTagSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORK_TAG, { id, partial });
},
updateWorld(id: number, partial: Partial<WorldSerializedInterface>): Promise<WorldSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORLD, { id, partial });
},
updateWorldName(id: number, partial: Partial<WorldNameSerializedInterface>): Promise<WorldNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORLD_NAME, { id, partial });
},
updateWorldCharacter(
id: number,
partial: Partial<WorldCharacterSerializedInterface>,
): Promise<WorldCharacterSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER, { id, partial });
},
updateWorldCharacterName(
id: number,
partial: Partial<WorldCharacterNameSerializedInterface>,
): Promise<WorldCharacterNameSerializedInterface> {
return ipcClient.ask(IpcChannel.ENTITY_UPDATE_WORLD_CHARACTER_NAME, { id, partial });
},
deleteAuthor(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_AUTHOR, id);
},
deleteAuthorName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_AUTHOR_NAME, id);
},
deleteAuthorRole(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_AUTHOR_ROLE, id);
},
deleteAuthorRoleName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_AUTHOR_ROLE_NAME, id);
},
deleteCharacterTag(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_CHARACTER_TAG, id);
},
deleteCollection(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_COLLECTION, id);
},
deleteCollectionName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_COLLECTION_NAME, id);
},
deleteCollectionPart(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_COLLECTION_PART, id);
},
deleteCopy(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_COPY, id);
},
deleteInteractionTag(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_INTERACTION_TAG, id);
},
deleteSite(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_SITE, id);
},
deleteSiteName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_SITE_NAME, id);
},
deleteSource(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_SOURCE, id);
},
deleteTag(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_TAG, id);
},
deleteTagName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_TAG_NAME, id);
},
deleteTransformation(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_TRANSFORMATION, id);
},
deleteTransformationType(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_TRANSFORMATION_TYPE, id);
},
deleteTransformationTypeName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_TRANSFORMATION_TYPE_NAME, id);
},
deleteWorkAuthor(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORK_AUTHOR, id);
},
deleteWorkCharacter(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORK_CHARACTER, id);
},
deleteWorkCharacterName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORK_CHARACTER_NAME, id);
},
deleteWork(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORK, id);
},
deleteWorkName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORK_NAME, id);
},
deleteWorkTag(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORK_TAG, id);
},
deleteWorld(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORLD, id);
},
deleteWorldName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORLD_NAME, id);
},
deleteWorldCharacter(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORLD_CHARACTER, id);
},
deleteWorldCharacterName(id: number): Promise<void> {
return ipcClient.ask(IpcChannel.ENTITY_DELETE_WORLD_CHARACTER_NAME, id);
},
};

View File

@ -0,0 +1,5 @@
export abstract class EventBusEvent extends Event {
protected constructor(type: EventType, eventInit?: EventInit) {
super(type, eventInit);
}
}

Some files were not shown because too many files have changed in this diff Show More