RED-4632: use caching module from common-ui
This commit is contained in:
parent
d86312b38f
commit
f249f3d71d
12
angular.json
12
angular.json
@ -1,18 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"projects": {
|
||||
"iqser-cache": {
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "library",
|
||||
"root": "libs/iqser-cache",
|
||||
"sourceRoot": "libs/iqser-cache/src",
|
||||
"prefix": "redaction",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
}
|
||||
},
|
||||
"red-domain": {
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "library",
|
||||
|
||||
@ -13,7 +13,6 @@ import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '@environments/environment';
|
||||
import { AuthModule } from './modules/auth/auth.module';
|
||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||
import { HttpCacheInterceptor } from '@iqser/cache';
|
||||
import { NotificationsComponent } from '@components/notifications/notifications.component';
|
||||
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
@ -32,6 +31,7 @@ import { PruningTranslationLoader } from '@utils/pruning-translation-loader';
|
||||
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||
import * as links from '../assets/help-mode/links.json';
|
||||
import {
|
||||
CachingModule,
|
||||
HELP_DOCS,
|
||||
IqserHelpModeModule,
|
||||
MANUAL_BASE_URL,
|
||||
@ -56,6 +56,7 @@ import { SystemPreferencesService } from '@services/system-preferences.service';
|
||||
import { PdfViewerModule } from './modules/pdf-viewer/pdf-viewer.module';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
import { TenantIdInterceptor } from '@utils/tenant-id-interceptor';
|
||||
import { UI_CACHES } from '@utils/constants';
|
||||
|
||||
export function httpLoaderFactory(httpClient: HttpClient, configService: ConfigService): PruningTranslationLoader {
|
||||
return new PruningTranslationLoader(httpClient, '/assets/i18n/', `.json?version=${configService.values.FRONTEND_APP_VERSION}`);
|
||||
@ -86,6 +87,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
||||
AppRoutingModule,
|
||||
MonacoEditorModule,
|
||||
IqserHelpModeModule,
|
||||
CachingModule.forRoot(UI_CACHES),
|
||||
PdfViewerModule,
|
||||
ToastrModule.forRoot({
|
||||
closeButton: true,
|
||||
@ -174,11 +176,6 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
||||
multi: true,
|
||||
useClass: TenantIdInterceptor,
|
||||
},
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
multi: true,
|
||||
useClass: HttpCacheInterceptor,
|
||||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
|
||||
@ -4,16 +4,15 @@ import { Dictionary, Dossier, DOSSIER_ID, DOSSIER_TEMPLATE_ID, File, FILE_ID } f
|
||||
import { Router } from '@angular/router';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { boolFactory, getParam, LoadingService } from '@iqser/common-ui';
|
||||
import { boolFactory, getParam, LoadingService, wipeCache } from '@iqser/common-ui';
|
||||
import { filter, map, startWith, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { FileManagementService } from '@services/files/file-management.service';
|
||||
import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
|
||||
import { wipeFilesCache } from '@iqser/cache';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { HttpEvent, HttpEventType, HttpProgressEvent, HttpResponse } from '@angular/common/http';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DictionariesMapService } from '../../../services/entity-services/dictionaries-map.service';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
|
||||
|
||||
@ -127,7 +126,7 @@ export class FilePreviewStateService {
|
||||
|
||||
#downloadOriginalFile(cacheIdentifier: string, wipeCaches = true): Observable<Blob> {
|
||||
const downloadFile$ = this.#getFileToDownload(cacheIdentifier);
|
||||
const obs = wipeCaches ? from(wipeFilesCache()) : of({});
|
||||
const obs = wipeCaches ? from(wipeCache('files')) : of({});
|
||||
return obs.pipe(switchMap(() => downloadFile$));
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import packageInfo from '../../../../../package.json';
|
||||
import envConfig from '../../assets/config/config.json';
|
||||
import { CacheApiService, wipeAllCaches } from '@iqser/cache';
|
||||
import { CacheApiService, wipeAllCaches } from '@iqser/common-ui';
|
||||
import { Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { AppConfig } from '@red/domain';
|
||||
|
||||
@ -2,10 +2,9 @@ import { inject, Inject, Injectable } from '@angular/core';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import { ICreateUserRequest, IMyProfileUpdateRequest, IProfileUpdateRequest, IResetPasswordRequest, IUser, User } from '@red/domain';
|
||||
import { wipeCaches } from '@iqser/cache';
|
||||
import { BASE_HREF } from '../tokens';
|
||||
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
|
||||
import { EntitiesService, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { CacheApiService, EntitiesService, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
@ -17,7 +16,11 @@ export class UserService extends EntitiesService<IUser, User> {
|
||||
protected readonly _entityClass = User;
|
||||
readonly #currentUser$ = new BehaviorSubject<User>(null);
|
||||
|
||||
constructor(@Inject(BASE_HREF) private readonly _baseHref: string, private readonly _keycloakService: KeycloakService) {
|
||||
constructor(
|
||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
||||
private readonly _keycloakService: KeycloakService,
|
||||
private readonly _cacheApiService: CacheApiService,
|
||||
) {
|
||||
super();
|
||||
this.currentUser$ = this.#currentUser$.asObservable();
|
||||
}
|
||||
@ -46,7 +49,7 @@ export class UserService extends EntitiesService<IUser, User> {
|
||||
}
|
||||
|
||||
logout() {
|
||||
wipeCaches().then();
|
||||
this._cacheApiService.wipeCaches().then();
|
||||
this._keycloakService.logout().then();
|
||||
}
|
||||
|
||||
|
||||
@ -1,2 +1,23 @@
|
||||
import { DynamicCaches } from '@iqser/common-ui';
|
||||
|
||||
export const CHANGED_CHECK_INTERVAL = 5000;
|
||||
export const FALLBACK_COLOR = '#CCCCCC';
|
||||
|
||||
export const UI_CACHES: DynamicCaches = [
|
||||
{
|
||||
urls: ['/assets/icons', '/assets/wv-resources'],
|
||||
name: 'cached-assets',
|
||||
clearOnLogout: false,
|
||||
maxAge: 24 * 3600,
|
||||
maxSize: 1000,
|
||||
methods: ['GET'],
|
||||
},
|
||||
{
|
||||
urls: ['/download/original'],
|
||||
name: 'files',
|
||||
maxAge: 3600 * 24 * 7,
|
||||
maxSize: 1000,
|
||||
clearOnLogout: true,
|
||||
methods: ['GET'],
|
||||
},
|
||||
];
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit cd09d7254551d0f091d49a8be22c1bdfc5b8e60b
|
||||
Subproject commit 2bc66542a6032b7b54cc3a3acec9737af6618031
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.ts"],
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "redaction",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "redaction",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"max-len": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
# iqser-cache
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test iqser-cache` to execute the unit tests.
|
||||
@ -1,5 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of communication-lib
|
||||
*/
|
||||
|
||||
export * from './lib/index';
|
||||
@ -1,246 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { APP_LEVEL_CACHE, DYNAMIC_CACHES } from './cache-utils';
|
||||
import { HttpEvent, HttpHeaders, HttpRequest, HttpResponse } from '@angular/common/http';
|
||||
import { from, Observable, throwError } from 'rxjs';
|
||||
import { map, mergeMap } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CacheApiService {
|
||||
constructor() {
|
||||
this.checkCachesExpiration();
|
||||
}
|
||||
|
||||
get cachesAvailable(): boolean {
|
||||
return !!window.caches;
|
||||
}
|
||||
|
||||
cacheRequest(request: HttpRequest<any>, httpResponse: HttpResponse<any>): Promise<any> {
|
||||
if (httpResponse.status < 300 && httpResponse.status >= 200) {
|
||||
const url = this._buildUrl(request);
|
||||
for (const dynCache of DYNAMIC_CACHES) {
|
||||
for (const cacheUrl of dynCache.urls) {
|
||||
if (url.indexOf(cacheUrl) >= 0) {
|
||||
return caches.open(dynCache.name).then(cache => this._handleFetchResponse(httpResponse, dynCache, cache, url));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getCachedRequest(request: HttpRequest<any>): Observable<HttpEvent<any>> {
|
||||
const url = this._buildUrl(request);
|
||||
|
||||
return from(caches.match(url)).pipe(
|
||||
mergeMap(response => {
|
||||
if (response) {
|
||||
const expires = response.headers.get('_expires');
|
||||
if (expires) {
|
||||
// if not expired, return, else override
|
||||
if (parseInt(expires, 10) > new Date().getTime()) {
|
||||
// console.log('[CACHE-API] Returning from cache: ', url);
|
||||
return this._toHttpResponse(response);
|
||||
}
|
||||
// console.log('[CACHE-API] cache expired: ', url);
|
||||
} else {
|
||||
// console.log('[CACHE-API] Returning from cache: ', url);
|
||||
return this._toHttpResponse(response);
|
||||
}
|
||||
}
|
||||
return throwError(() => new Error('Request not Cached'));
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
cacheValue(name: string, valueReference: any, ttl = 3600): Promise<any> {
|
||||
if (this.cachesAvailable) {
|
||||
return caches.open(APP_LEVEL_CACHE).then(cache => {
|
||||
const string = JSON.stringify(valueReference);
|
||||
const expires = new Date().getTime() + ttl * 1000;
|
||||
const response = new Response(string, {
|
||||
headers: {
|
||||
_expires: `${expires}`,
|
||||
},
|
||||
});
|
||||
const request = new Request(name);
|
||||
// console.log('should cache', valueReference, string, response);
|
||||
return cache.put(request, response);
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
getCachedValue(name: string): Promise<any> {
|
||||
if (this.cachesAvailable) {
|
||||
return caches.open(APP_LEVEL_CACHE).then(cache =>
|
||||
cache.match(name).then(result => {
|
||||
if (result) {
|
||||
const expires = result.headers.get('_expires');
|
||||
try {
|
||||
if (parseInt(expires, 10) > new Date().getTime()) {
|
||||
return result.json();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
return undefined;
|
||||
}),
|
||||
);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
_buildUrl(request: HttpRequest<any>) {
|
||||
let url: string;
|
||||
if (request.method === 'GET') {
|
||||
url = request.urlWithParams;
|
||||
}
|
||||
if (request.method === 'POST') {
|
||||
const body = request.body;
|
||||
let hash: string;
|
||||
if (Array.isArray(body)) {
|
||||
hash = JSON.stringify(body.sort());
|
||||
} else {
|
||||
hash = JSON.stringify(body);
|
||||
}
|
||||
const separator = request.urlWithParams.indexOf('?') > 0 ? '&' : '?';
|
||||
url = request.urlWithParams + separator + btoa(hash);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
isCachable(event: HttpRequest<any>) {
|
||||
// only do shit for post and get
|
||||
|
||||
if (this.cachesAvailable && (event.method === 'GET' || event.method === 'POST')) {
|
||||
// quick check if it has the potential of caching
|
||||
const preliminaryUrl = event.url;
|
||||
let tryCache = false;
|
||||
|
||||
for (const cache of DYNAMIC_CACHES) {
|
||||
if (cache.methods.indexOf(event.method) >= 0) {
|
||||
for (const url of cache.urls) {
|
||||
if (preliminaryUrl.indexOf(url) >= 0) {
|
||||
tryCache = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tryCache;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
checkCachesExpiration() {
|
||||
if (this.cachesAvailable) {
|
||||
const now = new Date().getTime();
|
||||
for (const dynCache of DYNAMIC_CACHES) {
|
||||
this._handleCacheExpiration(dynCache, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _toHttpResponse(response: Response): Observable<HttpResponse<any>> {
|
||||
response = response.clone();
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
let obs: Observable<any>;
|
||||
if (contentType) {
|
||||
if (contentType.toLowerCase().indexOf('application/json') >= 0) {
|
||||
obs = from(response.json());
|
||||
}
|
||||
if (contentType.toLowerCase().indexOf('text/') >= 0) {
|
||||
obs = from(response.text());
|
||||
}
|
||||
if (contentType.toLowerCase().indexOf('image/') >= 0) {
|
||||
if (contentType.toLowerCase().indexOf('image/svg') >= 0) {
|
||||
obs = from(response.text());
|
||||
} else {
|
||||
obs = from(response.blob());
|
||||
}
|
||||
}
|
||||
if (contentType.toLowerCase().indexOf('application/pdf') >= 0) {
|
||||
obs = from(response.blob());
|
||||
}
|
||||
}
|
||||
// console.log('[CACHE-API] content type', contentType, response.url);
|
||||
return obs.pipe(
|
||||
map(
|
||||
body =>
|
||||
// console.log('[CACHE-API] BODY', body);
|
||||
new HttpResponse({
|
||||
body,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
url: response.url,
|
||||
headers: this._toHttpHeaders(response.headers),
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private _handleFetchResponse(httpResponse: HttpResponse<any>, dynCache, cache, url) {
|
||||
const expires = new Date().getTime() + dynCache.maxAge * 1000;
|
||||
|
||||
const cachedResponseFields = {
|
||||
status: httpResponse.status,
|
||||
statusText: httpResponse.statusText,
|
||||
headers: {
|
||||
_expires: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
httpResponse.headers.keys().forEach(key => {
|
||||
cachedResponseFields.headers[key] = httpResponse.headers.get(key);
|
||||
});
|
||||
cachedResponseFields.headers._expires = expires;
|
||||
|
||||
let body;
|
||||
if (cachedResponseFields.headers['content-type'].indexOf('application/json') >= 0) {
|
||||
body = JSON.stringify(httpResponse.body);
|
||||
} else {
|
||||
body = httpResponse.body;
|
||||
}
|
||||
cache.put(url, new Response(body, cachedResponseFields));
|
||||
}
|
||||
|
||||
private _handleCacheExpiration(dynCache, now) {
|
||||
caches.open(dynCache.name).then(cache => {
|
||||
cache.keys().then(keys => {
|
||||
// removed expired;
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
// console.log('[CACHE-API] checking cache key: ', key);
|
||||
cache.match(key).then(response => {
|
||||
const expires = response.headers.get('_expires');
|
||||
try {
|
||||
if (parseInt(expires, 10) < now) {
|
||||
cache.delete(key);
|
||||
}
|
||||
} catch (e) {}
|
||||
});
|
||||
}
|
||||
});
|
||||
cache.keys().then(keys => {
|
||||
if (keys.length > dynCache.maxSize) {
|
||||
const keysToRemove = keys.slice(0, keys.length - dynCache.maxSize);
|
||||
// console.log('[CACHE-API] cache to large - removing keys: ', keysToRemove);
|
||||
for (let i = 0; i < keysToRemove.length; i++) {
|
||||
const key = keys[i];
|
||||
cache.delete(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _toHttpHeaders(headers: Headers): HttpHeaders {
|
||||
let httpHeaders = new HttpHeaders();
|
||||
headers.forEach((value, key) => {
|
||||
httpHeaders = httpHeaders.append(key, value);
|
||||
});
|
||||
return httpHeaders;
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
export const APP_LEVEL_CACHE = 'app-level-cache';
|
||||
|
||||
export const DYNAMIC_CACHES = [
|
||||
{
|
||||
urls: ['/assets/icons', '/assets/wv-resources'],
|
||||
name: 'cached-assets',
|
||||
clearOnLogout: false,
|
||||
maxAge: 24 * 3600,
|
||||
maxSize: 1000,
|
||||
methods: ['GET'],
|
||||
},
|
||||
{
|
||||
urls: ['/download/original'],
|
||||
name: 'files',
|
||||
maxAge: 3600 * 24 * 7,
|
||||
maxSize: 1000,
|
||||
clearOnLogout: true,
|
||||
methods: ['GET'],
|
||||
},
|
||||
];
|
||||
|
||||
export async function wipeAllCaches() {
|
||||
const keys = await caches.keys();
|
||||
for (const cache of keys) {
|
||||
await caches.delete(cache);
|
||||
}
|
||||
}
|
||||
|
||||
export async function wipeCaches(logoutDependant: boolean = false) {
|
||||
await caches.delete(APP_LEVEL_CACHE);
|
||||
for (const cache of DYNAMIC_CACHES) {
|
||||
if (!logoutDependant) {
|
||||
await caches.delete(cache.name);
|
||||
}
|
||||
if (logoutDependant && cache.clearOnLogout) {
|
||||
await caches.delete(cache.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function wipeFilesCache() {
|
||||
return caches.delete('files');
|
||||
}
|
||||
|
||||
export async function wipeCacheEntry(cacheName: string, entry: string) {
|
||||
return caches.open(cacheName).then(cache => {
|
||||
cache.delete(entry, { ignoreSearch: false });
|
||||
});
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CacheApiService } from './cache-api.service';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class HttpCacheInterceptor implements HttpInterceptor {
|
||||
constructor(private _cacheApiService: CacheApiService) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// continue if not cachable.
|
||||
if (!this._cacheApiService.isCachable(req)) {
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
return this._cacheApiService.getCachedRequest(req).pipe(
|
||||
tap(() => {
|
||||
// console.log('[CACHE-API] got from cache', ok);
|
||||
}),
|
||||
catchError(() =>
|
||||
// console.log("[CACHE-API] Cache fetch error", cacheError, req.url);
|
||||
this.sendRequest(req, next),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server response observable by sending request to `next()`.
|
||||
* Will add the response to the cache on the way out.
|
||||
*/
|
||||
sendRequest(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// console.log('[CACHE-API] request', request.url);
|
||||
return next.handle(request).pipe(
|
||||
tap(event => {
|
||||
// There may be other events besides the response.
|
||||
if (event instanceof HttpResponse) {
|
||||
this._cacheApiService.cacheRequest(request, event);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from './cache-api.service';
|
||||
export * from './cache-utils';
|
||||
export * from './http-cache-interceptor';
|
||||
@ -1 +0,0 @@
|
||||
export * from './caches/index';
|
||||
@ -1,15 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"enableResourceInlining": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@ -23,7 +23,6 @@
|
||||
"@i18n/*": ["apps/red-ui/src/app/i18n/*"],
|
||||
"@iqser/common-ui": ["libs/common-ui/src/index.ts"],
|
||||
"@models/*": ["apps/red-ui/src/app/models/*"],
|
||||
"@iqser/cache": ["libs/iqser-cache/src/index.ts"],
|
||||
"@red/domain": ["libs/red-domain/src/index.ts"],
|
||||
"@services/*": ["apps/red-ui/src/app/services/*"],
|
||||
"@shared/*": ["apps/red-ui/src/app/modules/shared/*"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user