From cfdb9adaa25b41364d5f398004bb9f1ca63b6725 Mon Sep 17 00:00:00 2001
From: Drischdaan <42834596+Drischdaan@users.noreply.github.com>
Date: Thu, 8 Jul 2021 14:46:33 +0200
Subject: [PATCH] Implemented everything to a working state
---
.github/workflows/publish.yml | 19 +++++
.npmignore | 11 +++
README.md | 2 +-
package.json | 14 +++-
src/application/application.interfaces.ts | 12 +++
src/application/application.ts | 97 +++++++++++++++++++++++
src/index.ts | 13 +++
src/ioc/container.ts | 80 +++++++++++++++++++
src/ioc/ioc.interfaces.ts | 47 +++++++++++
src/ioc/scopes.ts | 42 ++++++++++
src/ioc/util.ts | 19 +++++
src/module/module.interfaces.ts | 30 +++++++
src/mooncake.ts | 18 +++++
src/types.ts | 4 +
src/util/basic.logger.ts | 22 +++++
src/util/logger.interfaces.ts | 5 ++
tsconfig.json | 6 +-
yarn.lock | 14 +++-
18 files changed, 448 insertions(+), 7 deletions(-)
create mode 100644 .github/workflows/publish.yml
create mode 100644 .npmignore
create mode 100644 src/application/application.interfaces.ts
create mode 100644 src/application/application.ts
create mode 100644 src/ioc/container.ts
create mode 100644 src/ioc/ioc.interfaces.ts
create mode 100644 src/ioc/scopes.ts
create mode 100644 src/ioc/util.ts
create mode 100644 src/module/module.interfaces.ts
create mode 100644 src/mooncake.ts
create mode 100644 src/types.ts
create mode 100644 src/util/basic.logger.ts
create mode 100644 src/util/logger.interfaces.ts
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..93d8147
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,19 @@
+name: Publish
+on:
+ release:
+ types: [created]
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v1
+ with:
+ node-version: '14.x'
+ registry-url: 'https://registry.npmjs.org'
+ - run: yarn install
+ - run: yarn build
+ - run: npm publish --access public
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..5063d8f
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,11 @@
+src/
+test/
+tsconfig.json
+jest.config.json
+*.js.map
+.eslintrc.js
+typings
+.vscode
+.prettierrc
+.gitignore
+yarn.lock
\ No newline at end of file
diff --git a/README.md b/README.md
index 562a2be..2bd5678 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
# mooncake
-Component based app framework
+NestJs and Angular inspired application framework
## Support
diff --git a/package.json b/package.json
index 6129908..3510131 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,23 @@
{
- "name": "mooncake",
+ "name": "@drischdaan/mooncake",
+ "description": "NestJs and Angular inspired application framework",
"version": "1.0.0",
"main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "typings": "./dist/index.d.ts",
"repository": "https://github.com/Drischdaan/mooncake",
"author": "Drischdaan",
"license": "MIT",
+ "files": [
+ "dist"
+ ],
"scripts": {
"prebuild": "rimraf dist",
"build": "tsc",
"preversion": "yarn run build",
"prestart": "yarn run build",
"start": "node ./dist/index.js",
- "start:dev": "ts-node-dev --pretty --log-error --quiet --respawn ./src/index.ts",
+ "start:dev": "ts-node-dev --pretty --log-error --respawn ./src/index.ts",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "eslint \"src/**/*.ts\" --fix"
},
@@ -25,5 +31,9 @@
"rimraf": "^3.0.2",
"ts-node-dev": "^1.1.6",
"typescript": "^4.2.4"
+ },
+ "dependencies": {
+ "reflect-metadata": "^0.1.13",
+ "tslog": "^3.2.0"
}
}
diff --git a/src/application/application.interfaces.ts b/src/application/application.interfaces.ts
new file mode 100644
index 0000000..c0678d4
--- /dev/null
+++ b/src/application/application.interfaces.ts
@@ -0,0 +1,12 @@
+import { IContainer } from "../ioc/ioc.interfaces";
+import { ILogger } from "../util/logger.interfaces";
+
+export interface IApplicationOptions {
+ name: string;
+}
+
+export interface IApplication {
+ onInitialization(): Promise;
+ onStart(): Promise;
+ onStop(): Promise;
+}
\ No newline at end of file
diff --git a/src/application/application.ts b/src/application/application.ts
new file mode 100644
index 0000000..0f4d378
--- /dev/null
+++ b/src/application/application.ts
@@ -0,0 +1,97 @@
+import { IocContainer } from "../ioc/container";
+import { IClassProvider, IContainer, IFactoryProvider, IProvider, IScope, IValueProvider } from "../ioc/ioc.interfaces";
+import { isClassProvider, isFactoryProvider, isValueProvider } from "../ioc/util";
+import { IModule, IModuleOptions } from "../module/module.interfaces";
+import { Class } from "../types";
+import { ILogger } from "../util/logger.interfaces";
+import { BasicLogger } from "../util/basic.logger";
+import { IApplication, IApplicationOptions } from "./application.interfaces";
+
+export abstract class Application implements IApplication {
+
+ protected readonly container: IContainer;
+ protected readonly logger: ILogger;
+ private readonly internalLogger: ILogger;
+
+ protected readonly controllers: Class[];
+ protected readonly providers: (Class | IProvider)[];
+
+ constructor(
+ protected readonly mainModule: IModule,
+ protected readonly options: IApplicationOptions,
+ ) {
+ this.container = new IocContainer();
+ this.logger = new BasicLogger(options.name);
+ this.internalLogger = new BasicLogger('Bootstrap');
+ this.controllers= [];
+ this.providers = [];
+ }
+
+ public async onInitialization(): Promise {
+ this.internalLogger.info('Initializing', this.options.name, '...');
+ this.internalLogger.info('Preparing container providers...');
+ await this.registerModule(this.mainModule);
+ }
+
+ public async onStart(): Promise {
+
+ }
+
+ public async onStop(): Promise {
+
+ }
+
+ private async registerModule(module: IModule): Promise {
+ await this.registerProviders(module);
+ await this.registerControllers(module);
+ await this.registerImports(module);
+ }
+
+ private async registerProviders(module: IModule): Promise {
+ for(const provider of module.options.providers) {
+ if(isClassProvider(provider)) {
+ const classProvider: IClassProvider = >provider;
+ this.container.register(classProvider.key).asClassProvider(classProvider.class, classProvider.scope);
+ } else if(isFactoryProvider(provider)) {
+ const factoryProvider: IFactoryProvider = >provider;
+ this.container.register(factoryProvider.key).asFactoryProvider(factoryProvider.factory);
+ } else if(isValueProvider(provider)) {
+ const valueProvider: IValueProvider = >provider;
+ this.container.register(valueProvider.key).asValueProvider(valueProvider.value);
+ } else {
+ const providerName: string = Reflect.getMetadata('injectable:name', provider);
+ const providerScope: Class = Reflect.getMetadata('injectable:scope', provider);
+ if(providerName === undefined || providerScope === undefined)
+ throw new Error(`Invalid provider found: ${provider}`);
+ this.providers.push(provider);
+ this.container.register(>provider).asClassProvider(>provider, new providerScope());
+ }
+ }
+ }
+
+ private async registerControllers(module: IModule): Promise {
+ for(const controller of module.options.controllers) {
+ const controllerName: string = Reflect.getMetadata('injectable:name', controller);
+ const controllerScope: Class = Reflect.getMetadata('injectable:scope', controller);
+ if(controllerName === undefined || controllerScope === undefined)
+ throw new Error(`Invalid controller found: ${controller}`);
+ this.controllers.push(controller);
+ this.container.register(controllerName).asClassProvider(controller, new controllerScope());
+ }
+ }
+
+ private async registerImports(module: IModule): Promise {
+ for(const importedModule of module.options.imports) {
+ const moduleName: string = Reflect.getMetadata('module:name', importedModule)
+ const moduleOptions: IModuleOptions = Reflect.getMetadata('module:options', importedModule);
+ if(moduleName === undefined || moduleOptions === undefined)
+ throw new Error(`Invalid imported module found: ${importedModule}`);
+ const instance: any = new importedModule();
+ if('onInitialization' in instance)
+ await instance['onInitialization']();
+ const generatedModule: IModule = { name: moduleName, moduleClass: importedModule, options: moduleOptions };
+ await this.registerModule(generatedModule);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index e69de29..4a9ce1c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -0,0 +1,13 @@
+import 'reflect-metadata';
+
+export * from './mooncake';
+export * from './types';
+export * from './util/logger.interfaces';
+export * from './util/basic.logger';
+export * from './module/module.interfaces';
+export * from './ioc/container';
+export * from './ioc/ioc.interfaces';
+export * from './ioc/scopes';
+export * from './ioc/util';
+export * from './application/application.interfaces';
+export * from './application/application';
\ No newline at end of file
diff --git a/src/ioc/container.ts b/src/ioc/container.ts
new file mode 100644
index 0000000..d9009d9
--- /dev/null
+++ b/src/ioc/container.ts
@@ -0,0 +1,80 @@
+import { TransientScope } from "..";
+import { Class } from "../types";
+import { IClassProvider, IContainer, IContainerEntry, IFactoryProvider, IProvider, IScope, IValueProvider } from "./ioc.interfaces";
+import { isClassProvider, isFactoryProvider, isValueProvider } from "./util";
+
+export class ContainerEntry implements IContainerEntry {
+
+ public provider: IProvider;
+
+ constructor(
+ public key: string,
+ ) {}
+
+ public asClassProvider(value: Class, scope: IScope = new TransientScope()): void {
+ this.provider = >{
+ key: this.key,
+ class: value,
+ scope: scope,
+ };
+ }
+
+ public asFactoryProvider(value: Function): void {
+ this.provider = >{
+ key: this.key,
+ factory: value
+ };
+ }
+
+ public asValueProvider(value: T): void {
+ this.provider = >{
+ key: this.key,
+ value: value
+ };
+ }
+
+}
+
+export class IocContainer implements IContainer {
+
+ private readonly entries: IContainerEntry[];
+
+ constructor() {
+ this.entries = new Array>();
+ }
+
+ public register(key: Class | string): IContainerEntry {
+ const generatedKey: string = this.generateKey(key);
+ const entries: IContainerEntry[] = this.entries.filter((entry: IContainerEntry) => entry.provider.key === generatedKey);
+ if(entries.length !== 0)
+ throw new Error(`There is already an entry with that key!`);
+ let entry: IContainerEntry = new ContainerEntry(generatedKey);
+ this.entries.push(entry);
+ return entry;
+ }
+
+ public resolve(key: string | Class): T {
+ const generatedKey: string = this.generateKey(key);
+ const entry: IContainerEntry = this.entries.find((entry: IContainerEntry) => entry.provider.key === generatedKey);
+ if(entry === undefined)
+ throw new Error(`There is no entry with that key: ${key}`);
+ if(isClassProvider(entry.provider))
+ return (>entry.provider).scope.resolve((>entry.provider), this);
+ else if(isFactoryProvider(entry.provider))
+ return (>entry.provider).factory();
+ else if(isValueProvider(entry.provider))
+ return (>entry.provider).value;
+ else
+ throw new Error(`Invalid provider detected with key "${entry.provider.key}"`);
+ }
+
+ public generateKey(key: any): string {
+ if(typeof(key) === 'string')
+ return key;
+ const name: string = Reflect.getMetadata('injectable:name', key);
+ if(name === undefined)
+ return key.name;
+ return name;
+ }
+
+}
\ No newline at end of file
diff --git a/src/ioc/ioc.interfaces.ts b/src/ioc/ioc.interfaces.ts
new file mode 100644
index 0000000..202e83b
--- /dev/null
+++ b/src/ioc/ioc.interfaces.ts
@@ -0,0 +1,47 @@
+import { Class } from "../types";
+import { TransientScope } from "./scopes";
+import { makeTargetInjectable } from "./util";
+
+export interface IScope {
+ resolve(provider: IClassProvider, container: IContainer): T;
+}
+
+export interface IProvider {
+ key: string,
+}
+
+export interface IFactoryProvider extends IProvider {
+ factory: Function,
+}
+
+export interface IClassProvider extends IProvider {
+ class: Class,
+ scope: IScope,
+}
+
+export interface IValueProvider extends IProvider {
+ value: T,
+}
+
+export interface IContainerEntry {
+ provider: IProvider;
+ asClassProvider(value: Class, scope?: IScope): void;
+ asFactoryProvider(factory: Function): void;
+ asValueProvider(value: T): void;
+}
+
+export interface IContainer {
+ register(key: Class | string): IContainerEntry;
+ resolve(key: Class | string): T;
+ generateKey(key: any): string;
+}
+
+export const Injectable = (name?: string, scope: Class = TransientScope): ClassDecorator => (target: TFunction): void => {
+ makeTargetInjectable(target, name, scope);
+}
+
+export const Inject = (name: string): ParameterDecorator => (target: Object, propertyKey: string | symbol, parameterIndex: number): void => {
+ const keys: any = Reflect.getMetadata('injection:keys', target) ?? [];
+ keys.push({ key: name, index: parameterIndex });
+ Reflect.defineMetadata('injection:keys', keys, target);
+}
\ No newline at end of file
diff --git a/src/ioc/scopes.ts b/src/ioc/scopes.ts
new file mode 100644
index 0000000..c01edc2
--- /dev/null
+++ b/src/ioc/scopes.ts
@@ -0,0 +1,42 @@
+import { IClassProvider, IContainer, IProvider, IScope } from "./ioc.interfaces";
+
+export class SingletonScope implements IScope {
+
+ private instance: any;
+
+ public resolve(provider: IClassProvider, container: IContainer): T {
+ if(this.instance !== undefined)
+ return this.instance;
+ const parameterTypes: any[] = Reflect.getMetadata('design:paramtypes', provider.class) ?? [];
+ const injectionKeys: any[] = Reflect.getMetadata('injection:keys', provider.class) ?? [];
+ const resolvedParameters: any[] = [];
+ parameterTypes.forEach((parameter: any, index: number) => {
+ const key: string = injectionKeys.find((value: any) => value.index === index)?.key;
+ const resolved: any = container.resolve(key ?? parameter);
+ if(resolved === undefined)
+ throw new Error(`Couldn't resolve parameter "${parameter}"!`);
+ resolvedParameters.push(resolved);
+ });
+ this.instance = new provider.class(...resolvedParameters);
+ return this.instance;
+ }
+
+}
+
+export class TransientScope implements IScope {
+
+ public resolve(provider: IClassProvider, container: IContainer): T {
+ const parameterTypes: any[] = Reflect.getMetadata('design:paramtypes', provider.class) ?? [];
+ const injectionKeys: any[] = Reflect.getMetadata('injection:keys', provider.class) ?? [];
+ const resolvedParameters: any[] = [];
+ parameterTypes.forEach((parameter: any, index: number) => {
+ const key: string = injectionKeys.find((value: any) => value.index === index)?.key;
+ const resolved: any = container.resolve(key ?? parameter);
+ if(resolved === undefined)
+ throw new Error(`Couldn't resolve parameter "${parameter}"!`);
+ resolvedParameters.push(resolved);
+ });
+ return new provider.class(...resolvedParameters);
+ }
+
+}
\ No newline at end of file
diff --git a/src/ioc/util.ts b/src/ioc/util.ts
new file mode 100644
index 0000000..501e517
--- /dev/null
+++ b/src/ioc/util.ts
@@ -0,0 +1,19 @@
+import { Class } from "../types";
+import { IProvider, IScope } from "./ioc.interfaces";
+
+export function makeTargetInjectable(target: any, name: string, scope: Class) {
+ Reflect.defineMetadata('injectable:name', name ?? target.name, target);
+ Reflect.defineMetadata('injectable:scope', scope, target);
+}
+
+export function isClassProvider(provider: IProvider): boolean {
+ return (provider).class !== undefined && (provider).scope !== undefined;
+}
+
+export function isFactoryProvider(provider: IProvider): boolean {
+ return (provider).factory !== undefined;
+}
+
+export function isValueProvider(provider: IProvider): boolean {
+ return (provider).value !== undefined;
+}
\ No newline at end of file
diff --git a/src/module/module.interfaces.ts b/src/module/module.interfaces.ts
new file mode 100644
index 0000000..a71e01a
--- /dev/null
+++ b/src/module/module.interfaces.ts
@@ -0,0 +1,30 @@
+import { IClassProvider, IFactoryProvider, IProvider, IValueProvider } from "../ioc/ioc.interfaces";
+import { Class } from "../types";
+
+
+export interface IModuleOptions {
+ imports?: Class[],
+ controllers?: Class[],
+ providers?: (Class | IClassProvider | IFactoryProvider | IValueProvider)[],
+}
+
+export interface IModule {
+ name: string,
+ moduleClass: Class,
+ options: IModuleOptions,
+}
+
+export interface IModuleInitialize {
+ onInitialization(): Promise;
+}
+
+export const Module = (options: IModuleOptions): ClassDecorator => (target: TFunction): void => {
+ const defaultOptions: IModuleOptions = { imports: [], controllers: [], providers: [] };
+ let mergedOptions: IModuleOptions = {};
+
+ Object.assign(mergedOptions, defaultOptions);
+ Object.assign(mergedOptions, options);
+
+ Reflect.defineMetadata('module:name', target.name, target);
+ Reflect.defineMetadata('module:options', mergedOptions, target);
+}
\ No newline at end of file
diff --git a/src/mooncake.ts b/src/mooncake.ts
new file mode 100644
index 0000000..7796e88
--- /dev/null
+++ b/src/mooncake.ts
@@ -0,0 +1,18 @@
+import { IApplication, IApplicationOptions } from "./application/application.interfaces";
+import { IModule, IModuleOptions } from "./module/module.interfaces";
+import { Class } from './types';
+
+export class Mooncake {
+
+ public static async createApp(applicationClass: Class, mainModule: Class, options: IApplicationOptions): Promise {
+ const moduleName: string = Reflect.getMetadata('module:name', mainModule);
+ const moduleOptions: IModuleOptions = Reflect.getMetadata('module:options', mainModule);
+ if(moduleName === undefined || moduleOptions === undefined)
+ throw new Error(`Invalid module provided!`);
+ const module: IModule = { name: moduleName, moduleClass: mainModule, options: moduleOptions };
+ const app: IApplication = new applicationClass(module, options);
+ await app.onInitialization();
+ return app;
+ }
+
+}
\ No newline at end of file
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..2bf067c
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,4 @@
+export type Class = { new(...args: any[]): T; };
+export interface Type extends Function {
+ new (...args: any[]): T;
+}
\ No newline at end of file
diff --git a/src/util/basic.logger.ts b/src/util/basic.logger.ts
new file mode 100644
index 0000000..e2741e2
--- /dev/null
+++ b/src/util/basic.logger.ts
@@ -0,0 +1,22 @@
+import { Logger } from "tslog";
+import { ILogger } from "./logger.interfaces";
+
+export class BasicLogger implements ILogger {
+
+ private logger: Logger;
+
+ constructor(name: string) {
+ this.logger = new Logger({ name: name });
+ }
+
+ public info(message: string, ...args: string[]): void {
+ this.logger.info(message, ...args);
+ }
+ public error(message: string, ...args: string[]): void {
+ this.logger.error(message, ...args);
+ }
+ public warn(message: string, ...args: string[]): void {
+ this.logger.warn(message, ...args);
+ }
+
+}
\ No newline at end of file
diff --git a/src/util/logger.interfaces.ts b/src/util/logger.interfaces.ts
new file mode 100644
index 0000000..fb0cd3f
--- /dev/null
+++ b/src/util/logger.interfaces.ts
@@ -0,0 +1,5 @@
+export interface ILogger {
+ info(message: string, ...args: string[]): void;
+ error(message: string, ...args: string[]): void;
+ warn(message: string, ...args: string[]): void;
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 985f0c6..9b6709e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,7 +4,7 @@
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
- "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
+ "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
@@ -18,14 +18,14 @@
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
- // "removeComments": true, /* Do not emit comments to output. */
+ "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
- "strict": true, /* Enable all strict type-checking options. */
+ // "strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
diff --git a/yarn.lock b/yarn.lock
index 36cc0a0..c9a9e03 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1154,6 +1154,11 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
+reflect-metadata@^0.1.13:
+ version "0.1.13"
+ resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
+ integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
+
regexpp@^3.0.0, regexpp@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
@@ -1253,7 +1258,7 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
-source-map-support@^0.5.12, source-map-support@^0.5.17:
+source-map-support@^0.5.12, source-map-support@^0.5.17, source-map-support@^0.5.19:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
@@ -1435,6 +1440,13 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+tslog@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/tslog/-/tslog-3.2.0.tgz#4982c289a8948670d6a1c49c29977ae9f861adfa"
+ integrity sha512-xOCghepl5w+wcI4qXI7vJy6c53loF8OoC/EuKz1ktAPMtltEDz00yo1poKuyBYIQaq4ZDYKYFPD9PfqVrFXh0A==
+ dependencies:
+ source-map-support "^0.5.19"
+
tsutils@^3.17.1:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"