@kuzntsv/uikit 1.0.92 → 1.0.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { InjectionToken, inject, makeEnvironmentProviders } from "@angular/core";
2
- import { API_NAME, AuthEventService, BYPASS_AUTH, LoaderService, REQUEST_TIMEOUT, SKIP_LOADER, UIKIT_CONFIG, UIKIT_NOTIFICATION_TOKEN } from "@kuzntsv/uikit";
2
+ import { API_NAME, AuthEventService, BYPASS_AUTH, LoaderService, REQUEST_TIMEOUT, SKIP_LOADER, SKIP_NOTIFICATION, UIKIT_CONFIG, UIKIT_NOTIFICATION_TOKEN } from "@kuzntsv/uikit";
3
3
  import { HttpErrorResponse } from "@angular/common/http";
4
4
  import { TimeoutError, catchError, finalize, throwError } from "rxjs";
5
5
  import { timeout } from "rxjs/operators";
@@ -32,11 +32,12 @@ const coreAuthInterceptor = (req, next) => {
32
32
  const coreErrorInterceptor = (req, next) => {
33
33
  const notification = inject(UIKIT_NOTIFICATION_TOKEN, { optional: true });
34
34
  const eventService = inject(AuthEventService);
35
+ const skipNotification = req.context.get(SKIP_NOTIFICATION);
35
36
  return next(req).pipe(catchError((error) => {
36
37
  let errorMsg = "Произошла непредвиденная ошибка";
37
38
  if (error instanceof TimeoutError) {
38
39
  errorMsg = "Время ожидания ответа от сервера истекло. Попробуйте позже.";
39
- notification?.error(errorMsg);
40
+ if (!skipNotification) notification?.error(errorMsg);
40
41
  return throwError(() => new Error(errorMsg));
41
42
  }
42
43
  switch (error.status) {
@@ -58,7 +59,7 @@ const coreErrorInterceptor = (req, next) => {
58
59
  break;
59
60
  default: errorMsg = error.error?.message || error.message || errorMsg;
60
61
  }
61
- notification?.error(errorMsg);
62
+ if (!skipNotification) notification?.error(errorMsg);
62
63
  return throwError(() => error);
63
64
  }));
64
65
  };
@@ -1 +1 @@
1
- {"version":3,"file":"kuzntsv-uikit-interceptors.mjs","names":[],"sources":["../../interceptors/src/api-prefix.interceptor.ts","../../interceptors/src/auth.interceptor.ts","../../interceptors/src/error.interceptor.ts","../../interceptors/src/loader.interceptor.ts","../../interceptors/src/timeout.interceptor.ts","../../interceptors/src/providers.ts","../../interceptors/src/public-api.ts"],"sourcesContent":["import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { API_NAME, UIKIT_CONFIG } from '@kuzntsv/uikit';\r\n\r\nexport const apiPrefixInterceptor: HttpInterceptorFn = (req, next) => {\r\n // Если запрос уже абсолютный (начинается с http/https), игнорируем его\r\n if (req.url.startsWith('http://') || req.url.startsWith('https://')) {\r\n return next(req);\r\n }\r\n\r\n const config = inject(UIKIT_CONFIG);\r\n\r\n // Читаем, какой API запросил сервис (например, 'analytics')\r\n const apiKeys = req.context.get(API_NAME);\r\n\r\n // Ищем адрес в конфиге. Если не нашли такой ключ, берем 'default'\r\n const targetBaseUrl = config.apiUrls[apiKeys] || config.apiUrls['default'];\r\n\r\n if (!targetBaseUrl) {\r\n // Если даже default не настроен, пропускаем как есть\r\n return next(req);\r\n }\r\n\r\n // Нормализуем слеши при склеивании\r\n const baseUrl = targetBaseUrl.replace(/\\/$/, '');\r\n const url = req.url.startsWith('/') ? req.url : `/${req.url}`;\r\n\r\n const clonedReq = req.clone({ url: `${baseUrl}${url}` });\r\n return next(clonedReq);\r\n};\r\n","import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { BYPASS_AUTH, UIKIT_CONFIG } from '@kuzntsv/uikit';\r\nimport { throwError } from 'rxjs';\r\n\r\nexport const coreAuthInterceptor: HttpInterceptorFn = (req, next) => {\r\n const config = inject(UIKIT_CONFIG);\r\n\r\n const isAuthDisabled = !!config.auth.disabled;\r\n if (isAuthDisabled) {\r\n return next(req); // авторизация полностью отключена\r\n }\r\n\r\n // 0. маркер пропуска авторизации\r\n if (req.context.get(BYPASS_AUTH) === true) {\r\n return next(req); // Пропускаем проверку токенов для этого запроса\r\n }\r\n\r\n // 1. Проверяем, является ли URL публичным (не требующим авторизации)\r\n const isPublic = config.auth.publicUrls.some((url) => req.url.includes(url));\r\n if (isPublic) {\r\n return next(req);\r\n }\r\n\r\n // 2. Если в заголовках уже есть Authorization (например, передан вручную), отправляем как есть\r\n if (req.headers.has('Authorization')) {\r\n return next(req);\r\n }\r\n\r\n // 3. Пытаемся достать токен из LocalStorage по ключу из конфигурации\r\n const storageValue = localStorage.getItem(config.auth.userStorageKey);\r\n const user = storageValue ? JSON.parse(storageValue) : null;\r\n\r\n if (user && user.token) {\r\n // 4. Если токен есть, клонируем запрос и добавляем заголовок Bearer\r\n const authReq = req.clone({\r\n headers: req.headers\r\n .set('Authorization', `Bearer ${user.token}`)\r\n .set('User-Id', String(user.id))\r\n .set('Human-Id', String(user.idHuman)),\r\n });\r\n return next(authReq);\r\n }\r\n\r\n // 5. Если токена нет и запрос приватный — прерываем и редиректим на страницу логина\r\n // router.navigate([config.auth.loginRoute]);\r\n // 5. Если токена нет и запрос приватный — не редиректим вручную!\r\n // Просто кидаем 401 ошибку. Она сама прилетит в coreErrorInterceptor.\r\n return throwError(\r\n () =>\r\n new HttpErrorResponse({\r\n status: 401,\r\n statusText: 'Unauthorized',\r\n url: req.url,\r\n error: { message:'Запрос отменен: пользователь не авторизован' },\r\n }),\r\n );\r\n};\r\n","import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { catchError, throwError, TimeoutError } from 'rxjs';\r\nimport { UIKIT_NOTIFICATION_TOKEN, AuthEventService } from '@kuzntsv/uikit';\r\n\r\n\r\n// Состояние для управления очередью запросов во время рефреша\r\nlet isRefreshing = false; // флаг того, что обновление уже идет\r\n// для ожидания токена\r\n// const refreshTokenSubject: BehaviorSubject<string | null> = new BehaviorSubject<string | null>(null);\r\n\r\nexport const coreErrorInterceptor: HttpInterceptorFn = (req, next) => {\r\n const notification = inject(UIKIT_NOTIFICATION_TOKEN, { optional: true });\r\n // const authService = inject(LibraryAuthService);\r\n // const config = inject(UIKIT_CONFIG);\r\n\r\n const eventService = inject(AuthEventService);\r\n\r\n return next(req).pipe(\r\n catchError((error: any) => { // Меняем тип на any, так как RxJS TimeoutError — это не HttpErrorResponse\r\n let errorMsg = 'Произошла непредвиденная ошибка';\r\n // Проверяем, что это ошибка 401 и запрос не шел на сам эндпоинт рефреша\r\n // const isRefreshRequest = req.url.includes(config.auth.refreshEndpoint);\r\n\r\n // 1. Проверяем, не является ли ошибка таймаутом RxJS\r\n if (error instanceof TimeoutError) {\r\n errorMsg = 'Время ожидания ответа от сервера истекло. Попробуйте позже.';\r\n notification?.error(errorMsg);\r\n return throwError(() => new Error(errorMsg));\r\n }\r\n\r\n switch (error.status) {\r\n case 0:\r\n errorMsg = 'Сервер не доступен';\r\n break;\r\n case 401:\r\n // if (!isRefreshRequest) {\r\n // // return handle401Error(req, next, authService, config);\r\n // }\r\n // else {\r\n eventService.notify401(); // Просто кидаем событие в шину\r\n errorMsg = 'Сессия истекла. Войдите заново.';\r\n // }\r\n break;\r\n case 403:\r\n errorMsg = 'Недостаточно прав для выполнения действия.';\r\n break;\r\n case 404:\r\n errorMsg = 'Ресурс не найден'; \r\n \r\n break;\r\n case 500:\r\n errorMsg = 'Ошибка сервера. Попробуйте позже.'; \r\n break;\r\n default:\r\n // Для 400 и прочих берем сообщение с бэка или дефолтное\r\n errorMsg = error.error?.message || error.message || errorMsg;\r\n break;\r\n }\r\n notification?.error(errorMsg);\r\n \r\n // Возвращаем исходную ошибку, чтобы вызывающий сервис/компонент \r\n // мог прочитать error.status или error.error при необходимости,\r\n // но при этом подменяем или дополняем сообщение.\r\n return throwError(() => error);\r\n })\r\n );\r\n};\r\n\r\n\r\n// // Функция управления очередью и вызова Refresh Token\r\n// function handle401Error(\r\n// req: HttpRequest<unknown>,\r\n// next: HttpHandlerFn,\r\n// authService: LibraryAuthService,\r\n// config: UikitConfig\r\n// ): Observable<any> {\r\n \r\n// if (!isRefreshing) {\r\n// isRefreshing = true;\r\n// refreshTokenSubject.next(null); // Сбрасываем старый токен в потоке ожидания\r\n\r\n// return authService.refreshToken().pipe(\r\n// switchMap((response) => {\r\n// isRefreshing = false;\r\n// refreshTokenSubject.next(response.accessToken); // Разблокируем стоящие в очереди запросы\r\n \r\n// // Повторяем текущий упавший запрос с НОВЫМ токеном\r\n// return next(injectNewToken(req, response.accessToken, config));\r\n// }),\r\n// catchError((refreshError) => {\r\n// isRefreshing = false;\r\n// return throwError(() => refreshError);\r\n// })\r\n// );\r\n// } else {\r\n// // Если обновление уже запущено другим запросом, текущий запрос \"встает в очередь\"\r\n// // и ждет, пока в refreshTokenSubject появится новое строковое значение токена\r\n// return refreshTokenSubject.pipe(\r\n// filter(token => token !== null),\r\n// take(1), // Берем только первое валидное значение и отписываемся\r\n// switchMap(newToken => {\r\n// // Повторяем запрос с обновленным токеном\r\n// return next(injectNewToken(req, newToken!, config));\r\n// })\r\n// );\r\n// }\r\n// }\r\n\r\n// // Хелпер для подстановки свежего токена в заголовки повторяемого запроса\r\n// function injectNewToken(req: HttpRequest<unknown>, token: string, config: any): HttpRequest<unknown> {\r\n// return req.clone({\r\n// headers: req.headers.set('Authorization', `Bearer ${token}`)\r\n// });\r\n// }","import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { LoaderService, SKIP_LOADER } from '@kuzntsv/uikit';\r\nimport { finalize } from 'rxjs';\r\n\r\nexport const loaderInterceptor: HttpInterceptorFn = (req, next) => {\r\n const loaderService = inject(LoaderService);\r\n\r\n // Если токен передан со значением true, просто пропускаем запрос дальше\r\n if (req.context.get(SKIP_LOADER)) {\r\n return next(req);\r\n }\r\n\r\n loaderService.show();\r\n return next(req).pipe(finalize(() => loaderService.hide()));\r\n};\r\n","import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { timeout } from 'rxjs/operators';\r\nimport { REQUEST_TIMEOUT } from '@kuzntsv/uikit';\r\n\r\nexport const timeoutInterceptor: HttpInterceptorFn = (req, next) => {\r\n const defaultTimeout = req.context.get(REQUEST_TIMEOUT);\r\n\r\n return next(req).pipe(\r\n timeout(defaultTimeout)\r\n );\r\n};","import { InjectionToken, makeEnvironmentProviders, EnvironmentProviders } from '@angular/core';\r\nimport { HttpInterceptorFn } from '@angular/common/http';\r\nimport { UIKIT_CONFIG, UikitConfig } from '@kuzntsv/uikit';\r\n\r\nimport { UikitInterceptors } from './public-api';\r\n\r\n// Это кастомный Multi-InjectionToken, в который мы прячем интерцепторы библиотеки\r\nexport const UIKIT_INTERCEPTORS = new InjectionToken<HttpInterceptorFn[]>('UikitInterceptors');\r\n\r\nexport function provideUikit(config: UikitConfig): EnvironmentProviders {\r\n return makeEnvironmentProviders([\r\n { provide: UIKIT_CONFIG, useValue: config },\r\n \r\n // Регистрируем интерцептор внутри нашего собственного токена\r\n { provide: UIKIT_INTERCEPTORS, useValue: UikitInterceptors, multi: true }\r\n ]);\r\n};\r\n\r\n","import { apiPrefixInterceptor } from './api-prefix.interceptor';\r\nimport { coreAuthInterceptor } from './auth.interceptor';\r\nimport { coreErrorInterceptor } from './error.interceptor';\r\nimport { loaderInterceptor } from './loader.interceptor';\r\nimport { timeoutInterceptor } from './timeout.interceptor';\r\n\r\n// Экспортируем готовый пирог для приложений\r\n// Порядок интерцепторов очень важен. Сначала должен отработать интерцептор авторизации\r\n// (чтобы проверить оригинальный относительный URL и прикрепить токен),\r\n// а затем интерцептор подстановки Base URL (превращающий путь в абсолютный).\r\nexport const UikitInterceptors = [\r\n coreErrorInterceptor, // 1. Ловит абсолютно ВСЕ ошибки: и сетевые, и таймауты, и искусственные 401\r\n loaderInterceptor, // 2. Вторым — включаем лоадер (он закроется в finalize ПОСЛЕ обработки ошибок)\r\n apiPrefixInterceptor, // 3. Сначала превращаем URL в абсолютный (https://...)\r\n coreAuthInterceptor, // 4. Теперь проверяем publicUrls по полному пути и подставляем токен\r\n timeoutInterceptor, // 5. Ближе всего к сети — контролирует время выполнения самого запроса\r\n];\r\n\r\nexport * from './providers';\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"kuzntsv-uikit-interceptors.mjs","names":[],"sources":["../../interceptors/src/api-prefix.interceptor.ts","../../interceptors/src/auth.interceptor.ts","../../interceptors/src/error.interceptor.ts","../../interceptors/src/loader.interceptor.ts","../../interceptors/src/timeout.interceptor.ts","../../interceptors/src/providers.ts","../../interceptors/src/public-api.ts"],"sourcesContent":["import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { API_NAME, UIKIT_CONFIG } from '@kuzntsv/uikit';\r\n\r\nexport const apiPrefixInterceptor: HttpInterceptorFn = (req, next) => {\r\n // Если запрос уже абсолютный (начинается с http/https), игнорируем его\r\n if (req.url.startsWith('http://') || req.url.startsWith('https://')) {\r\n return next(req);\r\n }\r\n\r\n const config = inject(UIKIT_CONFIG);\r\n\r\n // Читаем, какой API запросил сервис (например, 'analytics')\r\n const apiKeys = req.context.get(API_NAME);\r\n\r\n // Ищем адрес в конфиге. Если не нашли такой ключ, берем 'default'\r\n const targetBaseUrl = config.apiUrls[apiKeys] || config.apiUrls['default'];\r\n\r\n if (!targetBaseUrl) {\r\n // Если даже default не настроен, пропускаем как есть\r\n return next(req);\r\n }\r\n\r\n // Нормализуем слеши при склеивании\r\n const baseUrl = targetBaseUrl.replace(/\\/$/, '');\r\n const url = req.url.startsWith('/') ? req.url : `/${req.url}`;\r\n\r\n const clonedReq = req.clone({ url: `${baseUrl}${url}` });\r\n return next(clonedReq);\r\n};\r\n","import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { BYPASS_AUTH, UIKIT_CONFIG } from '@kuzntsv/uikit';\r\nimport { throwError } from 'rxjs';\r\n\r\nexport const coreAuthInterceptor: HttpInterceptorFn = (req, next) => {\r\n const config = inject(UIKIT_CONFIG);\r\n\r\n const isAuthDisabled = !!config.auth.disabled;\r\n if (isAuthDisabled) {\r\n return next(req); // авторизация полностью отключена\r\n }\r\n\r\n // 0. маркер пропуска авторизации\r\n if (req.context.get(BYPASS_AUTH) === true) {\r\n return next(req); // Пропускаем проверку токенов для этого запроса\r\n }\r\n\r\n // 1. Проверяем, является ли URL публичным (не требующим авторизации)\r\n const isPublic = config.auth.publicUrls.some((url) => req.url.includes(url));\r\n if (isPublic) {\r\n return next(req);\r\n }\r\n\r\n // 2. Если в заголовках уже есть Authorization (например, передан вручную), отправляем как есть\r\n if (req.headers.has('Authorization')) {\r\n return next(req);\r\n }\r\n\r\n // 3. Пытаемся достать токен из LocalStorage по ключу из конфигурации\r\n const storageValue = localStorage.getItem(config.auth.userStorageKey);\r\n const user = storageValue ? JSON.parse(storageValue) : null;\r\n\r\n if (user && user.token) {\r\n // 4. Если токен есть, клонируем запрос и добавляем заголовок Bearer\r\n const authReq = req.clone({\r\n headers: req.headers\r\n .set('Authorization', `Bearer ${user.token}`)\r\n .set('User-Id', String(user.id))\r\n .set('Human-Id', String(user.idHuman)),\r\n });\r\n return next(authReq);\r\n }\r\n\r\n // 5. Если токена нет и запрос приватный — прерываем и редиректим на страницу логина\r\n // router.navigate([config.auth.loginRoute]);\r\n // 5. Если токена нет и запрос приватный — не редиректим вручную!\r\n // Просто кидаем 401 ошибку. Она сама прилетит в coreErrorInterceptor.\r\n return throwError(\r\n () =>\r\n new HttpErrorResponse({\r\n status: 401,\r\n statusText: 'Unauthorized',\r\n url: req.url,\r\n error: { message:'Запрос отменен: пользователь не авторизован' },\r\n }),\r\n );\r\n};\r\n","import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { catchError, throwError, TimeoutError } from 'rxjs';\r\nimport { UIKIT_NOTIFICATION_TOKEN, AuthEventService, SKIP_NOTIFICATION } from '@kuzntsv/uikit';\r\n\r\n\r\n// Состояние для управления очередью запросов во время рефреша\r\nlet isRefreshing = false; // флаг того, что обновление уже идет\r\n// для ожидания токена\r\n// const refreshTokenSubject: BehaviorSubject<string | null> = new BehaviorSubject<string | null>(null);\r\n\r\nexport const coreErrorInterceptor: HttpInterceptorFn = (req, next) => {\r\n const notification = inject(UIKIT_NOTIFICATION_TOKEN, { optional: true });\r\n const eventService = inject(AuthEventService);\r\n\r\n // Читаем значение токена из контекста запроса\r\n const skipNotification = req.context.get(SKIP_NOTIFICATION);\r\n\r\n return next(req).pipe(\r\n catchError((error: any) => { // Меняем тип на any, так как RxJS TimeoutError — это не HttpErrorResponse\r\n let errorMsg = 'Произошла непредвиденная ошибка';\r\n // Проверяем, что это ошибка 401 и запрос не шел на сам эндпоинт рефреша\r\n // const isRefreshRequest = req.url.includes(config.auth.refreshEndpoint);\r\n\r\n // 1. Проверяем, не является ли ошибка таймаутом RxJS\r\n if (error instanceof TimeoutError) {\r\n errorMsg = 'Время ожидания ответа от сервера истекло. Попробуйте позже.';\r\n // Если стоит флаг скрытия, не показываем ошибку таймаута\r\n if (!skipNotification) notification?.error(errorMsg); \r\n return throwError(() => new Error(errorMsg));\r\n }\r\n\r\n switch (error.status) {\r\n case 0:\r\n errorMsg = 'Сервер не доступен';\r\n break;\r\n case 401:\r\n // if (!isRefreshRequest) {\r\n // // return handle401Error(req, next, authService, config);\r\n // }\r\n // else {\r\n eventService.notify401(); // Просто кидаем событие в шину\r\n errorMsg = 'Сессия истекла. Войдите заново.';\r\n // }\r\n break;\r\n case 403:\r\n errorMsg = 'Недостаточно прав для выполнения действия.';\r\n break;\r\n case 404:\r\n errorMsg = 'Ресурс не найден'; \r\n \r\n break;\r\n case 500:\r\n errorMsg = 'Ошибка сервера. Попробуйте позже.'; \r\n break;\r\n default:\r\n // Для 400 и прочих берем сообщение с бэка или дефолтное\r\n errorMsg = error.error?.message || error.message || errorMsg;\r\n break;\r\n }\r\n \r\n if (!skipNotification) notification?.error(errorMsg);\r\n \r\n // Возвращаем исходную ошибку, чтобы вызывающий сервис/компонент \r\n // мог прочитать error.status или error.error при необходимости,\r\n // но при этом подменяем или дополняем сообщение.\r\n return throwError(() => error);\r\n })\r\n );\r\n};\r\n\r\n\r\n// // Функция управления очередью и вызова Refresh Token\r\n// function handle401Error(\r\n// req: HttpRequest<unknown>,\r\n// next: HttpHandlerFn,\r\n// authService: LibraryAuthService,\r\n// config: UikitConfig\r\n// ): Observable<any> {\r\n \r\n// if (!isRefreshing) {\r\n// isRefreshing = true;\r\n// refreshTokenSubject.next(null); // Сбрасываем старый токен в потоке ожидания\r\n\r\n// return authService.refreshToken().pipe(\r\n// switchMap((response) => {\r\n// isRefreshing = false;\r\n// refreshTokenSubject.next(response.accessToken); // Разблокируем стоящие в очереди запросы\r\n \r\n// // Повторяем текущий упавший запрос с НОВЫМ токеном\r\n// return next(injectNewToken(req, response.accessToken, config));\r\n// }),\r\n// catchError((refreshError) => {\r\n// isRefreshing = false;\r\n// return throwError(() => refreshError);\r\n// })\r\n// );\r\n// } else {\r\n// // Если обновление уже запущено другим запросом, текущий запрос \"встает в очередь\"\r\n// // и ждет, пока в refreshTokenSubject появится новое строковое значение токена\r\n// return refreshTokenSubject.pipe(\r\n// filter(token => token !== null),\r\n// take(1), // Берем только первое валидное значение и отписываемся\r\n// switchMap(newToken => {\r\n// // Повторяем запрос с обновленным токеном\r\n// return next(injectNewToken(req, newToken!, config));\r\n// })\r\n// );\r\n// }\r\n// }\r\n\r\n// // Хелпер для подстановки свежего токена в заголовки повторяемого запроса\r\n// function injectNewToken(req: HttpRequest<unknown>, token: string, config: any): HttpRequest<unknown> {\r\n// return req.clone({\r\n// headers: req.headers.set('Authorization', `Bearer ${token}`)\r\n// });\r\n// }","import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { LoaderService, SKIP_LOADER } from '@kuzntsv/uikit';\r\nimport { finalize } from 'rxjs';\r\n\r\nexport const loaderInterceptor: HttpInterceptorFn = (req, next) => {\r\n const loaderService = inject(LoaderService);\r\n\r\n // Если токен передан со значением true, просто пропускаем запрос дальше\r\n if (req.context.get(SKIP_LOADER)) {\r\n return next(req);\r\n }\r\n\r\n loaderService.show();\r\n return next(req).pipe(finalize(() => loaderService.hide()));\r\n};\r\n","import { HttpInterceptorFn } from '@angular/common/http';\r\nimport { timeout } from 'rxjs/operators';\r\nimport { REQUEST_TIMEOUT } from '@kuzntsv/uikit';\r\n\r\nexport const timeoutInterceptor: HttpInterceptorFn = (req, next) => {\r\n const defaultTimeout = req.context.get(REQUEST_TIMEOUT);\r\n\r\n return next(req).pipe(\r\n timeout(defaultTimeout)\r\n );\r\n};","import { InjectionToken, makeEnvironmentProviders, EnvironmentProviders } from '@angular/core';\r\nimport { HttpInterceptorFn } from '@angular/common/http';\r\nimport { UIKIT_CONFIG, UikitConfig } from '@kuzntsv/uikit';\r\n\r\nimport { UikitInterceptors } from './public-api';\r\n\r\n// Это кастомный Multi-InjectionToken, в который мы прячем интерцепторы библиотеки\r\nexport const UIKIT_INTERCEPTORS = new InjectionToken<HttpInterceptorFn[]>('UikitInterceptors');\r\n\r\nexport function provideUikit(config: UikitConfig): EnvironmentProviders {\r\n return makeEnvironmentProviders([\r\n { provide: UIKIT_CONFIG, useValue: config },\r\n \r\n // Регистрируем интерцептор внутри нашего собственного токена\r\n { provide: UIKIT_INTERCEPTORS, useValue: UikitInterceptors, multi: true }\r\n ]);\r\n};\r\n\r\n","import { apiPrefixInterceptor } from './api-prefix.interceptor';\r\nimport { coreAuthInterceptor } from './auth.interceptor';\r\nimport { coreErrorInterceptor } from './error.interceptor';\r\nimport { loaderInterceptor } from './loader.interceptor';\r\nimport { timeoutInterceptor } from './timeout.interceptor';\r\n\r\n// Экспортируем готовый пирог для приложений\r\n// Порядок интерцепторов очень важен. Сначала должен отработать интерцептор авторизации\r\n// (чтобы проверить оригинальный относительный URL и прикрепить токен),\r\n// а затем интерцептор подстановки Base URL (превращающий путь в абсолютный).\r\nexport const UikitInterceptors = [\r\n coreErrorInterceptor, // 1. Ловит абсолютно ВСЕ ошибки: и сетевые, и таймауты, и искусственные 401\r\n loaderInterceptor, // 2. Вторым — включаем лоадер (он закроется в finalize ПОСЛЕ обработки ошибок)\r\n apiPrefixInterceptor, // 3. Сначала превращаем URL в абсолютный (https://...)\r\n coreAuthInterceptor, // 4. Теперь проверяем publicUrls по полному пути и подставляем токен\r\n timeoutInterceptor, // 5. Ближе всего к сети — контролирует время выполнения самого запроса\r\n];\r\n\r\nexport * from './providers';\r\n"],"mappings":"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"}
@@ -19,6 +19,7 @@ const DEFAULT_TIMEOUT = new InjectionToken("DEFAULT_TIMEOUT", {
19
19
  });
20
20
  const REQUEST_TIMEOUT = new HttpContextToken(() => inject(DEFAULT_TIMEOUT));
21
21
  const SKIP_LOADER = new HttpContextToken(() => false);
22
+ const SKIP_NOTIFICATION = new HttpContextToken(() => false);
22
23
  const APP_ENVIRONMENT = new InjectionToken("ApplicationEnvironment");
23
24
  const UIKIT_NOTIFICATION_TOKEN = new InjectionToken("UikitNotificationToken");
24
25
  var AuthEventService = class AuthEventService {
@@ -461,6 +462,6 @@ function formatZodError(error, contextName) {
461
462
  return ` • Ошибка ${path ? `в "${path}"` : "в корне"}: ${issue.expected ? `ожидалось "${issue.expected}"` : ""}${issue.received ? `, получено "${issue.received}"` : ""}. Сообщение: ${issue.message}`;
462
463
  }).join("\n")}\n`;
463
464
  }
464
- export { ADMIN_ROLE, API_NAME, APP_ENVIRONMENT, AuthEventService, AuthService, BYPASS_AUTH, DEFAULT_TIMEOUT, HIDE_ROLE_MARKER, LoaderService, NotificationService, REQUEST_TIMEOUT, RoleService, SKIP_LOADER, SignalSelectionModel, UIKIT_CONFIG, UIKIT_NOTIFICATION_TOKEN, User, UserService, dateToString, formatZodError, hasArrayIntersection };
465
+ export { ADMIN_ROLE, API_NAME, APP_ENVIRONMENT, AuthEventService, AuthService, BYPASS_AUTH, DEFAULT_TIMEOUT, HIDE_ROLE_MARKER, LoaderService, NotificationService, REQUEST_TIMEOUT, RoleService, SKIP_LOADER, SKIP_NOTIFICATION, SignalSelectionModel, UIKIT_CONFIG, UIKIT_NOTIFICATION_TOKEN, User, UserService, dateToString, formatZodError, hasArrayIntersection };
465
466
 
466
467
  //# sourceMappingURL=kuzntsv-uikit.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"kuzntsv-uikit.mjs","names":[],"sources":["../../src/config/lib-config.ts","../../src/constants/auth.constants.ts","../../src/http/http-context-tokens.ts","../../src/models/interfaces/environment.interface.ts","../../src/models/interfaces/notification-service.interface.ts","../../src/services/auth-event.service.ts","../../src/services/auth-user.service.ts","../../src/services/auth.service.ts","../../src/utils/array.utils.ts","../../src/services/auth-role.service.ts","../../src/shared/progress-spinner/progress-spinner.component.ts","../../src/services/loader.service.ts","../../src/services/notification.service.ts","../../src/utils/date.utils.ts","../../src/utils/signal-selection-model.ts","../../src/utils/zod-error-formatter.ts"],"sourcesContent":["import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface UikitConfig {\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string;\r\n };\r\n auth: {\r\n disabled?: boolean; // true = авторизация полностью отключена\r\n userStorageKey: string; // Например, 'app_user_jwt'\r\n tokenRoute: string; // Например, '/jwt'\r\n // refreshTokenStorageKey: string; // Ключ для Refresh токена (например, 'refresh_token')\r\n // refreshEndpoint: string; // Относительный путь (например, '/auth/refresh')\r\n loginRoute: string; // Например, '/auth/login'\r\n publicUrls: string[]; // Относительные пути, которые не требуют токена (например, '/login', '/assets')\r\n };\r\n}\r\n\r\nexport const UIKIT_CONFIG = new InjectionToken<UikitConfig>('UIKIT_CONFIG');\r\n","/**\r\n * Специальный маркер-роль в массиве ограничений, \r\n * который полностью скрывает элемент от обычных пользователей.\r\n */\r\nexport const HIDE_ROLE_MARKER = 'HIDE';\r\n\r\n/**\r\n * Системная роль Администратора, которая игнорирует любые ограничения прав.\r\n */\r\nexport const ADMIN_ROLE = 'ADMIN';\r\n","import { HttpContextToken } from '@angular/common/http';\r\nimport { inject, InjectionToken } from '@angular/core';\r\n\r\n// Токен, в который мы будем передавать строку-идентификатор нужного API\r\n// По умолчанию пустая строка означает \"использовать дефолтное (основное) API\"\r\nexport const API_NAME = new HttpContextToken<string>(() => '');\r\n\r\n// Иногда внутри сервиса нужно сделать один специфический запрос (например, стянуть публичный JSON-файл конфигурации), не добавляя его глобально в publicUrls.\r\n// Используя подход с HttpContext, мы можем добавить в библиотеку маркер пропуска авторизации:\r\n// А в коде приложения вызывать так:\r\n// this.http.get('/assets/config.json', { \r\n// context: new HttpContext().set(BYPASS_AUTH, true) \r\n// });\r\nexport const BYPASS_AUTH = new HttpContextToken<boolean>(() => false);\r\n\r\n// DI-токен, который переопределяем в appConfig\r\nexport const DEFAULT_TIMEOUT = new InjectionToken<number>('DEFAULT_TIMEOUT', {\r\n providedIn: 'root',\r\n factory: () => 10000 // Запасной дефолт (10 сек), если в appConfig ничего не указали\r\n});\r\n\r\n// Если для каких-то тяжелых запросов (например, загрузка больших файлов или генерация отчетов) \r\n// вам нужно увеличить таймаут, вы можете передавать его значение прямо в запросе через HttpContext.\r\nexport const REQUEST_TIMEOUT = new HttpContextToken<number>(() => inject(DEFAULT_TIMEOUT));\r\n\r\n// Передать в токене значение true, для запросов (например, автосохранение черновика \r\n// или периодический опрос сервера), при которых показывать блокирующий спиннер пользователю не нужно.\r\nexport const SKIP_LOADER = new HttpContextToken<boolean>(() => false);\r\n","import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface AppEnvironment {\r\n appVersion: string;\r\n production: boolean;\r\n \r\n // Объявляем словарь адресов. \r\n // Ключи default и auth — обязательные, остальные — любые строки.\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string; // <-- Это позволяет писать billing, analytics, crm и т.д.\r\n };\r\n};\r\n\r\nexport const APP_ENVIRONMENT = new InjectionToken<AppEnvironment>('ApplicationEnvironment');","import { InjectionToken } from \"@angular/core\";\r\n\r\n/**\r\n * Сервис уведомлений.\r\n */\r\nexport interface INotificationService {\r\n /**\r\n * Показ всплывашки с сообщением.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n openSnackBar(message: string, action: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о успехе.\r\n *\r\n * @param message сообщение\r\n * @param duration задержка в миллисекундах\r\n */\r\n success(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о информации.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n info(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о сообщения помощи.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n helper(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о предупреждении.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n warning(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление об ошибке.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n error(message: string, duration?: number): void;\r\n}\r\n\r\nexport const UIKIT_NOTIFICATION_TOKEN = new InjectionToken<INotificationService>('UikitNotificationToken');\r\n","import { Injectable } from '@angular/core';\r\nimport { Subject } from 'rxjs';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthEventService {\r\n // Ошибки авторизации\r\n private readonly authErrorSource = new Subject<void>();\r\n\r\n // Стрим для прослушивания ошибок авторизации (экспортируется наружу)\r\n readonly authError$ = this.authErrorSource.asObservable();\r\n\r\n /** Вызывается в интерцепторе при 401 ошибке */\r\n notify401(): void {\r\n this.authErrorSource.next();\r\n }\r\n}","import { inject, Service } from '@angular/core';\r\nimport { UIKIT_CONFIG } from '../config';\r\n/**\r\n * Сущность пользователя из API\r\n */\r\nexport class User {\r\n id: number = 0;\r\n username: string = '';\r\n firstName: string = '';\r\n lastName: string = '';\r\n prof: string = '';\r\n depart: number = 0;\r\n area: string = '';\r\n tabnum: string = '';\r\n mail: string = '';\r\n phone: string = '';\r\n roles: string = '';\r\n rolesArray: string[] = [];\r\n roleList: string[] = [];\r\n idHuman: string = '';\r\n token: string = '';\r\n // Время действия токена\r\n expires_in: number = 0;\r\n}\r\n@Service()\r\n/**\r\n * Сервис для работы с объектом в local Storage\r\n */\r\nexport class UserService {\r\n private user: User;\r\n\r\n private config = inject(UIKIT_CONFIG);\r\n\r\n constructor() {\r\n const saved = localStorage.getItem(this.config.auth.userStorageKey);\r\n this.user = saved ? JSON.parse(saved) : new User();\r\n }\r\n\r\n get dept(): number {\r\n return this.user?.depart;\r\n }\r\n\r\n /** Получить данные о пользователе */\r\n get userData(): User {\r\n return this.user;\r\n }\r\n\r\n /** Изменить данные пользователя в localStorage */\r\n set userData(user: User) {\r\n this.user = user;\r\n localStorage.setItem(this.config.auth.userStorageKey, JSON.stringify(user));\r\n }\r\n\r\n /** Удаляет данные о пользователе из localStorage. */\r\n removeUser() {\r\n localStorage.removeItem(this.config.auth.userStorageKey);\r\n }\r\n}\r\n","import { DestroyRef, Injectable, inject, signal } from '@angular/core';\r\nimport { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';\r\nimport { Router } from '@angular/router';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\n\r\nimport { AuthEventService } from './auth-event.service';\r\nimport { User, UserService } from './auth-user.service';\r\nimport { UIKIT_CONFIG } from '../config';\r\nimport { UIKIT_NOTIFICATION_TOKEN } from '../models';\r\nimport { catchError, Observable, tap, throwError } from 'rxjs';\r\n\r\n// interface RefreshResponse {\r\n// accessToken: string;\r\n// refreshToken: string;\r\n// }\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthService {\r\n // Хранит роли пользователя\r\n private readonly _roles = signal<string[]>([]);\r\n // Состояние авторизации\r\n private readonly _loggedIn = signal<boolean>(false);\r\n\r\n readonly roles = this._roles.asReadonly();\r\n readonly loggedIn = this._loggedIn.asReadonly();\r\n\r\n // Заголовки для URL-encoded формы\r\n private readonly defaultOptions = {\r\n headers: new HttpHeaders().set(\r\n 'content-type',\r\n 'application/x-www-form-urlencoded',\r\n ),\r\n };\r\n\r\n private readonly notificationService = inject(UIKIT_NOTIFICATION_TOKEN);\r\n private readonly userService = inject(UserService);\r\n private readonly eventService = inject(AuthEventService);\r\n private readonly destroyRef = inject(DestroyRef);\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(UIKIT_CONFIG);\r\n // Внедряем роутер опционально, чтобы библиотека не падала, если роутера нет в приложении\r\n private readonly router = inject(Router, { optional: true });\r\n\r\n constructor() {\r\n const initialUser = this.userService.userData as User | null;\r\n\r\n if (initialUser?.token) {\r\n this._loggedIn.set(true);\r\n this._roles.set(initialUser.roleList ?? []);\r\n }\r\n\r\n // Слушаем шину событий: если интерцептор поймал 401, сбрасываем сессию\r\n this.eventService.authError$\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe(() => this.logout());\r\n }\r\n\r\n /**\r\n * Запрос авторизации\r\n * Возвращает Observable, позволяя компоненту реагировать на статус запроса.\r\n */\r\n login(username: string, password: string, idCas: number): Observable<User> {\r\n let params = new HttpParams()\r\n .set('username', username)\r\n .set('password', password); // HttpParams закодирует спецсимволы сам!\r\n\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http\r\n .post<User>(url, params.toString(), this.defaultOptions)\r\n .pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Login error:', error);\r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n /** Aвторизация по пропуску */\r\n loginByPermit(permit: string, idCas: number): Observable<User> {\r\n let params = new HttpParams().set('permit', permit);\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http.post<User>(url, params.toString(), this.defaultOptions).pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Permit login error:', error); \r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n logout(): void {\r\n // Убирает(удаляет) пользователя из локального хранилища когда он разлогинивается\r\n this.userService.removeUser();\r\n this._loggedIn.set(false);\r\n this._roles.set([]);\r\n\r\n // localStorage.removeItem(this.config.auth.userStorageKey);\r\n // localStorage.removeItem(this.config.auth.refreshTokenStorageKey);\r\n // ЛОГИКА СОХРАНЕНИЯ URL ПРИ РАЗЛОГИНЕ (Ваш улучшенный кусок кода)\r\n if (this.router) {\r\n const currentUrl = this.router.url;\r\n\r\n // Проверяем, не находимся ли мы уже на странице логина, чтобы избежать зацикливания\r\n if (currentUrl.includes(this.config.auth.loginRoute)) {\r\n return;\r\n }\r\n\r\n // Безопасное формирование query-параметров через Router\r\n this.router.navigate([this.config.auth.loginRoute], {\r\n queryParams: { returnUrl: encodeURIComponent(currentUrl) },\r\n });\r\n }\r\n }\r\n\r\n /** При успешной авторизации */\r\n private successLoginAction(user: User) {\r\n this.userService.userData = user;\r\n this._loggedIn.set(true);\r\n this._roles.set(user.roleList ?? []);\r\n }\r\n\r\n // // Метод отправляет старый refresh-токен на сервер\r\n // refreshToken(): Observable<RefreshResponse> {\r\n // const refreshToken = localStorage.getItem(this.config.auth.refreshTokenStorageKey);\r\n\r\n // if (!refreshToken) {\r\n // this.logout();\r\n // return throwError(() => new Error('No refresh token available'));\r\n // }\r\n\r\n // // Собираем абсолютный урл (берем default baseUrl + эндпоинт рефреша)\r\n // const baseUrl = this.config.apiUrls.default.replace(/\\/\\$/, '');\r\n // const url = `${baseUrl}${this.config.auth.refreshEndpoint}`;\r\n\r\n // // Передаем токен (в зависимости от вашего бэкенда: в body или в куках/заголовках)\r\n // return this.http.post<RefreshResponse>(url, { refreshToken }).pipe(\r\n // tap(response => {\r\n // // Сохраняем новые токены в локалсторадж\r\n // localStorage.setItem(this.config.auth.tokenStorageKey, response.accessToken);\r\n // localStorage.setItem(this.config.auth.refreshTokenStorageKey, response.refreshToken);\r\n // }),\r\n // catchError(err => {\r\n // // Если даже рефреш-токен невалиден (истек) — принудительный разлогин\r\n // this.logout();\r\n // return throwError(() => err);\r\n // })\r\n // );\r\n // }\r\n}\r\n","/**\r\n * Проверяет, пересекаются ли два массива (есть ли хотя бы один общий элемент).\r\n */\r\nexport function hasArrayIntersection<T>(arrayA: T[], arrayB: T[]): boolean {\r\n if (!arrayA || !arrayB) return false;\r\n return arrayA.some(item => arrayB.includes(item));\r\n}\r\n","import { inject, Injectable } from '@angular/core';\r\n\r\nimport { AuthService } from './auth.service';\r\nimport { hasArrayIntersection } from '../utils/array.utils';\r\nimport { ADMIN_ROLE, HIDE_ROLE_MARKER } from '../constants';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\n/** Вычисление разрешений по ролям */ \r\nexport class RoleService {\r\n private readonly authService = inject(AuthService);\r\n\r\n /**\r\n * Проверяет, заблокирован ли доступ к элементу на основе списка ограничений.\r\n * Возвращает true, если у пользователя есть запрещенная роль или передан маркер 'HIDE'.\r\n * Администратор (ADMIN) всегда игнорирует любые запреты и маркер 'HIDE'.\r\n */\r\n public hasForbiddenRole(forbiddenRoles: string[]): boolean {\r\n if (!forbiddenRoles || forbiddenRoles.length === 0) return false;\r\n\r\n const userRoles = this.authService.roles() ?? [];\r\n\r\n // Если пользователь ADMIN — он игнорирует списки запретов\r\n if (userRoles.includes(ADMIN_ROLE)) return false;\r\n\r\n // Если в условиях зашит маркер 'HIDE' — скрываем элемент от обычного пользователя\r\n if (forbiddenRoles.includes(HIDE_ROLE_MARKER)) return true;\r\n\r\n // В остальных случаях проверяем стандартное пересечение ролей\r\n return hasArrayIntersection(userRoles, forbiddenRoles);\r\n }\r\n\r\n /**\r\n * Проверяет, есть ли у пользователя хотя бы одна из разрешенных ролей.\r\n * ADMIN всегда возвращает true.\r\n */\r\npublic hasAnyRole(allowedRoles: string[]): boolean {\r\n const userRoles = this.authService.roles() ?? [];\r\n if (userRoles.includes(ADMIN_ROLE)) return true;\r\n \r\n return hasArrayIntersection(userRoles, allowedRoles);\r\n}\r\n}\r\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\r\nimport { MatProgressSpinner } from '@angular/material/progress-spinner';\r\n\r\n@Component({\r\n selector: 'ngx-uik-loader',\r\n template: `\r\n <div class=\"loading-indicator\">\r\n <mat-progress-spinner mode=\"indeterminate\" aria-label=\"Загрузка...\"/>\r\n </div>\r\n `,\r\n styles: [\r\n `\r\n /* Разместить по центру */\r\n .loading-indicator {\r\n position: fixed;\r\n z-index: 999;\r\n height: 2em;\r\n width: 2em;\r\n overflow: initial;\r\n margin: auto;\r\n inset: 0; /* 2. Современное CSS-свойство взамен top/left/bottom/right */\r\n }\r\n\r\n /* Заблокировать оверлей */\r\n .loading-indicator::before {\r\n content: '';\r\n display: block;\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.3);\r\n }\r\n `,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [MatProgressSpinner],\r\n})\r\nexport class ProgressSpinnerComponent {}\r\n","import { Overlay, OverlayRef } from '@angular/cdk/overlay';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { inject, OnDestroy, Service } from '@angular/core';\r\n\r\nimport { ProgressSpinnerComponent } from '../shared/progress-spinner';\r\n\r\n@Service()\r\nexport class LoaderService implements OnDestroy {\r\n private overlayRef: OverlayRef | null = null;\r\n private readonly overlay = inject(Overlay);\r\n\r\n // Счетчик для контроля параллельных HTTP-запросов\r\n private activeRequests = 0;\r\n\r\n public show(): void {\r\n this.activeRequests++;\r\n\r\n if (!this.overlayRef) {\r\n this.overlayRef = this.overlay.create({\r\n hasBackdrop: false, // Настройки бэкдропа теперь берет на себя стилистика лоадера\r\n positionStrategy: this.overlay\r\n .position()\r\n .global()\r\n .centerHorizontally()\r\n .centerVertically(),\r\n });\r\n }\r\n if (!this.overlayRef.hasAttached()) {\r\n const spinnerPortal = new ComponentPortal(ProgressSpinnerComponent);\r\n this.overlayRef.attach(spinnerPortal);\r\n }\r\n }\r\n\r\n public hide(): void {\r\n if (this.activeRequests > 0) {\r\n this.activeRequests--;\r\n }\r\n // Скрываем спиннер только тогда, когда завершился ПОСЛЕДНИЙ активный запрос\r\n if (this.activeRequests === 0 && this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n /**\r\n * Принудительный сброс (например, при смене роута или критической ошибке)\r\n */\r\n public reset(): void {\r\n this.activeRequests = 0;\r\n if (this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (this.overlayRef) {\r\n this.overlayRef.dispose(); // Полностью удаляет элемент оверлея из DOM\r\n }\r\n }\r\n}\r\n","import { inject, Service } from '@angular/core';\r\nimport { MatSnackBar } from '@angular/material/snack-bar';\r\n\r\nimport { INotificationService } from '../models';\r\n\r\n// Сервис для вывода сообщений(ошибки, успеха)\r\n@Service()\r\nexport class NotificationService implements INotificationService {\r\n private readonly duration = 5000;\r\n private readonly _snackBar = inject(MatSnackBar);\r\n\r\n openSnackBar(message: string, action: string, duration: number = this.duration) {\r\n this._snackBar.open(message, action, {\r\n duration,\r\n panelClass: ['red-snackbar'],\r\n });\r\n }\r\n\r\n success(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n info(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n helper(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n warning(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n error(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n}\r\n","/**\r\n * Преобразует дату в локальную строковую дату (Europe/Samara).\r\n * Чистая функция, независимая от контекста Angular.\r\n */\r\nexport function dateToString(\r\n date: string | number | Date | null | undefined,\r\n): string {\r\n if (!date) return '';\r\n\r\n return new Date(date).toLocaleString('ru-RU', {\r\n timeZone: 'Europe/Samara',\r\n });\r\n}\r\n","import { computed, signal, type Signal } from '@angular/core';\r\n\r\nexport class SignalSelectionModel<T> {\r\n private readonly _selectedSet = signal<Set<T>>(new Set());\r\n\r\n // Свойства-сигналы для эффективной работы в Angular шаблонах\r\n readonly selected: Signal<T[]> = computed(() => Array.from(this._selectedSet()));\r\n readonly isEmpty = computed(() => this._selectedSet().size === 0);\r\n readonly hasValue = computed(() => this._selectedSet().size > 0);\r\n\r\n constructor(\r\n public readonly multiple = false, // Поле multiple как в CDK\r\n initialValues: T[] = [],\r\n private _compareWith?: (o1: T, o2: T) => boolean // Поддержка сравнения по ID (как в MatSelect)\r\n ) {\r\n if (initialValues.length > 0) {\r\n this.select(...initialValues);\r\n }\r\n }\r\n\r\n // Проверка наличия элемента с учетом кастомного сравнения\r\n isSelected(value: T): boolean {\r\n const set = this._selectedSet();\r\n if (!this._compareWith) {\r\n return set.has(value);\r\n }\r\n \r\n for (const item of set) {\r\n if (this._compareWith(item, value)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n select(...values: T[]): void {\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n \r\n if (!this.multiple) {\r\n newSet.clear();\r\n if (values.length > 0) {\r\n this._addWithCompare(newSet, values[values.length - 1]);\r\n }\r\n } else {\r\n values.forEach(v => this._addWithCompare(newSet, v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n deselect(...values: T[]): void {\r\n if (values.length === 0) return; // Ранний выход, если массив пустой\r\n\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n if (this._compareWith) {\r\n const toDelete = new Set<T>(); // Сюда складываем кандидатов на удаление\r\n \r\n for (const value of values) {\r\n for (const current of newSet) {\r\n if (this._compareWith(current, value)) {\r\n toDelete.add(current);\r\n }\r\n }\r\n }\r\n // Удаляем только после того, как все циклы по newSet завершены\r\n toDelete.forEach(item => newSet.delete(item));\r\n } else {\r\n values.forEach(v => newSet.delete(v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n toggle(value: T): void {\r\n this.isSelected(value) ? this.deselect(value) : this.select(value);\r\n }\r\n\r\n clear(): void {\r\n if (this._selectedSet().size === 0) return; \r\n this._selectedSet.set(new Set());\r\n }\r\n\r\n // Вспомогательный метод для корректного добавления объектов\r\n private _addWithCompare(set: Set<T>, value: T): void {\r\n if (this._compareWith) {\r\n let duplicate: T | undefined;\r\n for (const current of set) {\r\n if (this._compareWith(current, value)) {\r\n duplicate = current;\r\n break;\r\n }\r\n }\r\n if (duplicate !== undefined) {\r\n set.delete(duplicate);\r\n }\r\n }\r\n set.add(value);\r\n }\r\n}\r\n","import { ZodError } from 'zod';\r\n\r\nexport function formatZodError(error: ZodError, contextName: string): string {\r\n const issuesText = error.issues\r\n .map((issue) => {\r\n // Форматируем путь: [0, \"user\"] -> \"[0] -> user\"\r\n const path = issue.path\r\n .map((p) => (typeof p === 'number' ? `[${p}]` : p))\r\n .join(' -> ');\r\n\r\n const location = path ? `в \"${path}\"` : 'в корне';\r\n const expected = (issue as any).expected\r\n ? `ожидалось \"${(issue as any).expected}\"`\r\n : '';\r\n const received = (issue as any).received\r\n ? `, получено \"${(issue as any).received}\"`\r\n : '';\r\n\r\n return ` • Ошибка ${location}: ${expected}${received}. Сообщение: ${issue.message}`;\r\n })\r\n .join('\\n');\r\n\r\n return `\\n[Zod Валидация] Ошибка парсинга для ресурса: ${contextName}\\n${issuesText}\\n`;\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"kuzntsv-uikit.mjs","names":[],"sources":["../../src/config/lib-config.ts","../../src/constants/auth.constants.ts","../../src/http/http-context-tokens.ts","../../src/models/interfaces/environment.interface.ts","../../src/models/interfaces/notification-service.interface.ts","../../src/services/auth-event.service.ts","../../src/services/auth-user.service.ts","../../src/services/auth.service.ts","../../src/utils/array.utils.ts","../../src/services/auth-role.service.ts","../../src/shared/progress-spinner/progress-spinner.component.ts","../../src/services/loader.service.ts","../../src/services/notification.service.ts","../../src/utils/date.utils.ts","../../src/utils/signal-selection-model.ts","../../src/utils/zod-error-formatter.ts"],"sourcesContent":["import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface UikitConfig {\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string;\r\n };\r\n auth: {\r\n disabled?: boolean; // true = авторизация полностью отключена\r\n userStorageKey: string; // Например, 'app_user_jwt'\r\n tokenRoute: string; // Например, '/jwt'\r\n // refreshTokenStorageKey: string; // Ключ для Refresh токена (например, 'refresh_token')\r\n // refreshEndpoint: string; // Относительный путь (например, '/auth/refresh')\r\n loginRoute: string; // Например, '/auth/login'\r\n publicUrls: string[]; // Относительные пути, которые не требуют токена (например, '/login', '/assets')\r\n };\r\n}\r\n\r\nexport const UIKIT_CONFIG = new InjectionToken<UikitConfig>('UIKIT_CONFIG');\r\n","/**\r\n * Специальный маркер-роль в массиве ограничений, \r\n * который полностью скрывает элемент от обычных пользователей.\r\n */\r\nexport const HIDE_ROLE_MARKER = 'HIDE';\r\n\r\n/**\r\n * Системная роль Администратора, которая игнорирует любые ограничения прав.\r\n */\r\nexport const ADMIN_ROLE = 'ADMIN';\r\n","import { HttpContextToken } from '@angular/common/http';\r\nimport { inject, InjectionToken } from '@angular/core';\r\n\r\n// Токен, в который мы будем передавать строку-идентификатор нужного API\r\n// По умолчанию пустая строка означает \"использовать дефолтное (основное) API\"\r\nexport const API_NAME = new HttpContextToken<string>(() => '');\r\n\r\n// Иногда внутри сервиса нужно сделать один специфический запрос (например, стянуть публичный JSON-файл конфигурации), не добавляя его глобально в publicUrls.\r\n// Используя подход с HttpContext, мы можем добавить в библиотеку маркер пропуска авторизации:\r\n// А в коде приложения вызывать так:\r\n// this.http.get('/assets/config.json', { \r\n// context: new HttpContext().set(BYPASS_AUTH, true) \r\n// });\r\nexport const BYPASS_AUTH = new HttpContextToken<boolean>(() => false);\r\n\r\n// DI-токен, который переопределяем в appConfig\r\nexport const DEFAULT_TIMEOUT = new InjectionToken<number>('DEFAULT_TIMEOUT', {\r\n providedIn: 'root',\r\n factory: () => 10000 // Запасной дефолт (10 сек), если в appConfig ничего не указали\r\n});\r\n\r\n// Если для каких-то тяжелых запросов (например, загрузка больших файлов или генерация отчетов) \r\n// вам нужно увеличить таймаут, вы можете передавать его значение прямо в запросе через HttpContext.\r\nexport const REQUEST_TIMEOUT = new HttpContextToken<number>(() => inject(DEFAULT_TIMEOUT));\r\n\r\n// Передать в токене значение true, для запросов (например, автосохранение черновика \r\n// или периодический опрос сервера), при которых показывать блокирующий спиннер пользователю не нужно.\r\nexport const SKIP_LOADER = new HttpContextToken<boolean>(() => false);\r\n\r\n// Передайте true для запросов, ошибки которых (например, 404 при проверке уникальности)\r\n// не должны приводить к показу глобального нотификатора.\r\nexport const SKIP_NOTIFICATION = new HttpContextToken<boolean>(() => false);\r\n","import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface AppEnvironment {\r\n appVersion: string;\r\n production: boolean;\r\n \r\n // Объявляем словарь адресов. \r\n // Ключи default и auth — обязательные, остальные — любые строки.\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string; // <-- Это позволяет писать billing, analytics, crm и т.д.\r\n };\r\n};\r\n\r\nexport const APP_ENVIRONMENT = new InjectionToken<AppEnvironment>('ApplicationEnvironment');","import { InjectionToken } from \"@angular/core\";\r\n\r\n/**\r\n * Сервис уведомлений.\r\n */\r\nexport interface INotificationService {\r\n /**\r\n * Показ всплывашки с сообщением.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n openSnackBar(message: string, action: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о успехе.\r\n *\r\n * @param message сообщение\r\n * @param duration задержка в миллисекундах\r\n */\r\n success(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о информации.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n info(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о сообщения помощи.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n helper(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о предупреждении.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n warning(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление об ошибке.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n error(message: string, duration?: number): void;\r\n}\r\n\r\nexport const UIKIT_NOTIFICATION_TOKEN = new InjectionToken<INotificationService>('UikitNotificationToken');\r\n","import { Injectable } from '@angular/core';\r\nimport { Subject } from 'rxjs';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthEventService {\r\n // Ошибки авторизации\r\n private readonly authErrorSource = new Subject<void>();\r\n\r\n // Стрим для прослушивания ошибок авторизации (экспортируется наружу)\r\n readonly authError$ = this.authErrorSource.asObservable();\r\n\r\n /** Вызывается в интерцепторе при 401 ошибке */\r\n notify401(): void {\r\n this.authErrorSource.next();\r\n }\r\n}","import { inject, Service } from '@angular/core';\r\nimport { UIKIT_CONFIG } from '../config';\r\n/**\r\n * Сущность пользователя из API\r\n */\r\nexport class User {\r\n id: number = 0;\r\n username: string = '';\r\n firstName: string = '';\r\n lastName: string = '';\r\n prof: string = '';\r\n depart: number = 0;\r\n area: string = '';\r\n tabnum: string = '';\r\n mail: string = '';\r\n phone: string = '';\r\n roles: string = '';\r\n rolesArray: string[] = [];\r\n roleList: string[] = [];\r\n idHuman: string = '';\r\n token: string = '';\r\n // Время действия токена\r\n expires_in: number = 0;\r\n}\r\n@Service()\r\n/**\r\n * Сервис для работы с объектом в local Storage\r\n */\r\nexport class UserService {\r\n private user: User;\r\n\r\n private config = inject(UIKIT_CONFIG);\r\n\r\n constructor() {\r\n const saved = localStorage.getItem(this.config.auth.userStorageKey);\r\n this.user = saved ? JSON.parse(saved) : new User();\r\n }\r\n\r\n get dept(): number {\r\n return this.user?.depart;\r\n }\r\n\r\n /** Получить данные о пользователе */\r\n get userData(): User {\r\n return this.user;\r\n }\r\n\r\n /** Изменить данные пользователя в localStorage */\r\n set userData(user: User) {\r\n this.user = user;\r\n localStorage.setItem(this.config.auth.userStorageKey, JSON.stringify(user));\r\n }\r\n\r\n /** Удаляет данные о пользователе из localStorage. */\r\n removeUser() {\r\n localStorage.removeItem(this.config.auth.userStorageKey);\r\n }\r\n}\r\n","import { DestroyRef, Injectable, inject, signal } from '@angular/core';\r\nimport { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';\r\nimport { Router } from '@angular/router';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\n\r\nimport { AuthEventService } from './auth-event.service';\r\nimport { User, UserService } from './auth-user.service';\r\nimport { UIKIT_CONFIG } from '../config';\r\nimport { UIKIT_NOTIFICATION_TOKEN } from '../models';\r\nimport { catchError, Observable, tap, throwError } from 'rxjs';\r\n\r\n// interface RefreshResponse {\r\n// accessToken: string;\r\n// refreshToken: string;\r\n// }\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthService {\r\n // Хранит роли пользователя\r\n private readonly _roles = signal<string[]>([]);\r\n // Состояние авторизации\r\n private readonly _loggedIn = signal<boolean>(false);\r\n\r\n readonly roles = this._roles.asReadonly();\r\n readonly loggedIn = this._loggedIn.asReadonly();\r\n\r\n // Заголовки для URL-encoded формы\r\n private readonly defaultOptions = {\r\n headers: new HttpHeaders().set(\r\n 'content-type',\r\n 'application/x-www-form-urlencoded',\r\n ),\r\n };\r\n\r\n private readonly notificationService = inject(UIKIT_NOTIFICATION_TOKEN);\r\n private readonly userService = inject(UserService);\r\n private readonly eventService = inject(AuthEventService);\r\n private readonly destroyRef = inject(DestroyRef);\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(UIKIT_CONFIG);\r\n // Внедряем роутер опционально, чтобы библиотека не падала, если роутера нет в приложении\r\n private readonly router = inject(Router, { optional: true });\r\n\r\n constructor() {\r\n const initialUser = this.userService.userData as User | null;\r\n\r\n if (initialUser?.token) {\r\n this._loggedIn.set(true);\r\n this._roles.set(initialUser.roleList ?? []);\r\n }\r\n\r\n // Слушаем шину событий: если интерцептор поймал 401, сбрасываем сессию\r\n this.eventService.authError$\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe(() => this.logout());\r\n }\r\n\r\n /**\r\n * Запрос авторизации\r\n * Возвращает Observable, позволяя компоненту реагировать на статус запроса.\r\n */\r\n login(username: string, password: string, idCas: number): Observable<User> {\r\n let params = new HttpParams()\r\n .set('username', username)\r\n .set('password', password); // HttpParams закодирует спецсимволы сам!\r\n\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http\r\n .post<User>(url, params.toString(), this.defaultOptions)\r\n .pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Login error:', error);\r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n /** Aвторизация по пропуску */\r\n loginByPermit(permit: string, idCas: number): Observable<User> {\r\n let params = new HttpParams().set('permit', permit);\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http.post<User>(url, params.toString(), this.defaultOptions).pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Permit login error:', error); \r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n logout(): void {\r\n // Убирает(удаляет) пользователя из локального хранилища когда он разлогинивается\r\n this.userService.removeUser();\r\n this._loggedIn.set(false);\r\n this._roles.set([]);\r\n\r\n // localStorage.removeItem(this.config.auth.userStorageKey);\r\n // localStorage.removeItem(this.config.auth.refreshTokenStorageKey);\r\n // ЛОГИКА СОХРАНЕНИЯ URL ПРИ РАЗЛОГИНЕ (Ваш улучшенный кусок кода)\r\n if (this.router) {\r\n const currentUrl = this.router.url;\r\n\r\n // Проверяем, не находимся ли мы уже на странице логина, чтобы избежать зацикливания\r\n if (currentUrl.includes(this.config.auth.loginRoute)) {\r\n return;\r\n }\r\n\r\n // Безопасное формирование query-параметров через Router\r\n this.router.navigate([this.config.auth.loginRoute], {\r\n queryParams: { returnUrl: encodeURIComponent(currentUrl) },\r\n });\r\n }\r\n }\r\n\r\n /** При успешной авторизации */\r\n private successLoginAction(user: User) {\r\n this.userService.userData = user;\r\n this._loggedIn.set(true);\r\n this._roles.set(user.roleList ?? []);\r\n }\r\n\r\n // // Метод отправляет старый refresh-токен на сервер\r\n // refreshToken(): Observable<RefreshResponse> {\r\n // const refreshToken = localStorage.getItem(this.config.auth.refreshTokenStorageKey);\r\n\r\n // if (!refreshToken) {\r\n // this.logout();\r\n // return throwError(() => new Error('No refresh token available'));\r\n // }\r\n\r\n // // Собираем абсолютный урл (берем default baseUrl + эндпоинт рефреша)\r\n // const baseUrl = this.config.apiUrls.default.replace(/\\/\\$/, '');\r\n // const url = `${baseUrl}${this.config.auth.refreshEndpoint}`;\r\n\r\n // // Передаем токен (в зависимости от вашего бэкенда: в body или в куках/заголовках)\r\n // return this.http.post<RefreshResponse>(url, { refreshToken }).pipe(\r\n // tap(response => {\r\n // // Сохраняем новые токены в локалсторадж\r\n // localStorage.setItem(this.config.auth.tokenStorageKey, response.accessToken);\r\n // localStorage.setItem(this.config.auth.refreshTokenStorageKey, response.refreshToken);\r\n // }),\r\n // catchError(err => {\r\n // // Если даже рефреш-токен невалиден (истек) — принудительный разлогин\r\n // this.logout();\r\n // return throwError(() => err);\r\n // })\r\n // );\r\n // }\r\n}\r\n","/**\r\n * Проверяет, пересекаются ли два массива (есть ли хотя бы один общий элемент).\r\n */\r\nexport function hasArrayIntersection<T>(arrayA: T[], arrayB: T[]): boolean {\r\n if (!arrayA || !arrayB) return false;\r\n return arrayA.some(item => arrayB.includes(item));\r\n}\r\n","import { inject, Injectable } from '@angular/core';\r\n\r\nimport { AuthService } from './auth.service';\r\nimport { hasArrayIntersection } from '../utils/array.utils';\r\nimport { ADMIN_ROLE, HIDE_ROLE_MARKER } from '../constants';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\n/** Вычисление разрешений по ролям */ \r\nexport class RoleService {\r\n private readonly authService = inject(AuthService);\r\n\r\n /**\r\n * Проверяет, заблокирован ли доступ к элементу на основе списка ограничений.\r\n * Возвращает true, если у пользователя есть запрещенная роль или передан маркер 'HIDE'.\r\n * Администратор (ADMIN) всегда игнорирует любые запреты и маркер 'HIDE'.\r\n */\r\n public hasForbiddenRole(forbiddenRoles: string[]): boolean {\r\n if (!forbiddenRoles || forbiddenRoles.length === 0) return false;\r\n\r\n const userRoles = this.authService.roles() ?? [];\r\n\r\n // Если пользователь ADMIN — он игнорирует списки запретов\r\n if (userRoles.includes(ADMIN_ROLE)) return false;\r\n\r\n // Если в условиях зашит маркер 'HIDE' — скрываем элемент от обычного пользователя\r\n if (forbiddenRoles.includes(HIDE_ROLE_MARKER)) return true;\r\n\r\n // В остальных случаях проверяем стандартное пересечение ролей\r\n return hasArrayIntersection(userRoles, forbiddenRoles);\r\n }\r\n\r\n /**\r\n * Проверяет, есть ли у пользователя хотя бы одна из разрешенных ролей.\r\n * ADMIN всегда возвращает true.\r\n */\r\npublic hasAnyRole(allowedRoles: string[]): boolean {\r\n const userRoles = this.authService.roles() ?? [];\r\n if (userRoles.includes(ADMIN_ROLE)) return true;\r\n \r\n return hasArrayIntersection(userRoles, allowedRoles);\r\n}\r\n}\r\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\r\nimport { MatProgressSpinner } from '@angular/material/progress-spinner';\r\n\r\n@Component({\r\n selector: 'ngx-uik-loader',\r\n template: `\r\n <div class=\"loading-indicator\">\r\n <mat-progress-spinner mode=\"indeterminate\" aria-label=\"Загрузка...\"/>\r\n </div>\r\n `,\r\n styles: [\r\n `\r\n /* Разместить по центру */\r\n .loading-indicator {\r\n position: fixed;\r\n z-index: 999;\r\n height: 2em;\r\n width: 2em;\r\n overflow: initial;\r\n margin: auto;\r\n inset: 0; /* 2. Современное CSS-свойство взамен top/left/bottom/right */\r\n }\r\n\r\n /* Заблокировать оверлей */\r\n .loading-indicator::before {\r\n content: '';\r\n display: block;\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.3);\r\n }\r\n `,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [MatProgressSpinner],\r\n})\r\nexport class ProgressSpinnerComponent {}\r\n","import { Overlay, OverlayRef } from '@angular/cdk/overlay';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { inject, OnDestroy, Service } from '@angular/core';\r\n\r\nimport { ProgressSpinnerComponent } from '../shared/progress-spinner';\r\n\r\n@Service()\r\nexport class LoaderService implements OnDestroy {\r\n private overlayRef: OverlayRef | null = null;\r\n private readonly overlay = inject(Overlay);\r\n\r\n // Счетчик для контроля параллельных HTTP-запросов\r\n private activeRequests = 0;\r\n\r\n public show(): void {\r\n this.activeRequests++;\r\n\r\n if (!this.overlayRef) {\r\n this.overlayRef = this.overlay.create({\r\n hasBackdrop: false, // Настройки бэкдропа теперь берет на себя стилистика лоадера\r\n positionStrategy: this.overlay\r\n .position()\r\n .global()\r\n .centerHorizontally()\r\n .centerVertically(),\r\n });\r\n }\r\n if (!this.overlayRef.hasAttached()) {\r\n const spinnerPortal = new ComponentPortal(ProgressSpinnerComponent);\r\n this.overlayRef.attach(spinnerPortal);\r\n }\r\n }\r\n\r\n public hide(): void {\r\n if (this.activeRequests > 0) {\r\n this.activeRequests--;\r\n }\r\n // Скрываем спиннер только тогда, когда завершился ПОСЛЕДНИЙ активный запрос\r\n if (this.activeRequests === 0 && this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n /**\r\n * Принудительный сброс (например, при смене роута или критической ошибке)\r\n */\r\n public reset(): void {\r\n this.activeRequests = 0;\r\n if (this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (this.overlayRef) {\r\n this.overlayRef.dispose(); // Полностью удаляет элемент оверлея из DOM\r\n }\r\n }\r\n}\r\n","import { inject, Service } from '@angular/core';\r\nimport { MatSnackBar } from '@angular/material/snack-bar';\r\n\r\nimport { INotificationService } from '../models';\r\n\r\n// Сервис для вывода сообщений(ошибки, успеха)\r\n@Service()\r\nexport class NotificationService implements INotificationService {\r\n private readonly duration = 5000;\r\n private readonly _snackBar = inject(MatSnackBar);\r\n\r\n openSnackBar(message: string, action: string, duration: number = this.duration) {\r\n this._snackBar.open(message, action, {\r\n duration,\r\n panelClass: ['red-snackbar'],\r\n });\r\n }\r\n\r\n success(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n info(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n helper(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n warning(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n error(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n}\r\n","/**\r\n * Преобразует дату в локальную строковую дату (Europe/Samara).\r\n * Чистая функция, независимая от контекста Angular.\r\n */\r\nexport function dateToString(\r\n date: string | number | Date | null | undefined,\r\n): string {\r\n if (!date) return '';\r\n\r\n return new Date(date).toLocaleString('ru-RU', {\r\n timeZone: 'Europe/Samara',\r\n });\r\n}\r\n","import { computed, signal, type Signal } from '@angular/core';\r\n\r\nexport class SignalSelectionModel<T> {\r\n private readonly _selectedSet = signal<Set<T>>(new Set());\r\n\r\n // Свойства-сигналы для эффективной работы в Angular шаблонах\r\n readonly selected: Signal<T[]> = computed(() => Array.from(this._selectedSet()));\r\n readonly isEmpty = computed(() => this._selectedSet().size === 0);\r\n readonly hasValue = computed(() => this._selectedSet().size > 0);\r\n\r\n constructor(\r\n public readonly multiple = false, // Поле multiple как в CDK\r\n initialValues: T[] = [],\r\n private _compareWith?: (o1: T, o2: T) => boolean // Поддержка сравнения по ID (как в MatSelect)\r\n ) {\r\n if (initialValues.length > 0) {\r\n this.select(...initialValues);\r\n }\r\n }\r\n\r\n // Проверка наличия элемента с учетом кастомного сравнения\r\n isSelected(value: T): boolean {\r\n const set = this._selectedSet();\r\n if (!this._compareWith) {\r\n return set.has(value);\r\n }\r\n \r\n for (const item of set) {\r\n if (this._compareWith(item, value)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n select(...values: T[]): void {\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n \r\n if (!this.multiple) {\r\n newSet.clear();\r\n if (values.length > 0) {\r\n this._addWithCompare(newSet, values[values.length - 1]);\r\n }\r\n } else {\r\n values.forEach(v => this._addWithCompare(newSet, v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n deselect(...values: T[]): void {\r\n if (values.length === 0) return; // Ранний выход, если массив пустой\r\n\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n if (this._compareWith) {\r\n const toDelete = new Set<T>(); // Сюда складываем кандидатов на удаление\r\n \r\n for (const value of values) {\r\n for (const current of newSet) {\r\n if (this._compareWith(current, value)) {\r\n toDelete.add(current);\r\n }\r\n }\r\n }\r\n // Удаляем только после того, как все циклы по newSet завершены\r\n toDelete.forEach(item => newSet.delete(item));\r\n } else {\r\n values.forEach(v => newSet.delete(v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n toggle(value: T): void {\r\n this.isSelected(value) ? this.deselect(value) : this.select(value);\r\n }\r\n\r\n clear(): void {\r\n if (this._selectedSet().size === 0) return; \r\n this._selectedSet.set(new Set());\r\n }\r\n\r\n // Вспомогательный метод для корректного добавления объектов\r\n private _addWithCompare(set: Set<T>, value: T): void {\r\n if (this._compareWith) {\r\n let duplicate: T | undefined;\r\n for (const current of set) {\r\n if (this._compareWith(current, value)) {\r\n duplicate = current;\r\n break;\r\n }\r\n }\r\n if (duplicate !== undefined) {\r\n set.delete(duplicate);\r\n }\r\n }\r\n set.add(value);\r\n }\r\n}\r\n","import { ZodError } from 'zod';\r\n\r\nexport function formatZodError(error: ZodError, contextName: string): string {\r\n const issuesText = error.issues\r\n .map((issue) => {\r\n // Форматируем путь: [0, \"user\"] -> \"[0] -> user\"\r\n const path = issue.path\r\n .map((p) => (typeof p === 'number' ? `[${p}]` : p))\r\n .join(' -> ');\r\n\r\n const location = path ? `в \"${path}\"` : 'в корне';\r\n const expected = (issue as any).expected\r\n ? `ожидалось \"${(issue as any).expected}\"`\r\n : '';\r\n const received = (issue as any).received\r\n ? `, получено \"${(issue as any).received}\"`\r\n : '';\r\n\r\n return ` • Ошибка ${location}: ${expected}${received}. Сообщение: ${issue.message}`;\r\n })\r\n .join('\\n');\r\n\r\n return `\\n[Zod Валидация] Ошибка парсинга для ресурса: ${contextName}\\n${issuesText}\\n`;\r\n}\r\n"],"mappings":";;;;;;;;;;AAmBA,MAAa,eAAe,IAAI,eAA4B,cAAc;ACf1E,MAAa,mBAAmB;AAKhC,MAAa,aAAa;ACJ1B,MAAa,WAAW,IAAI,uBAA+B,EAAE;AAQ7D,MAAa,cAAc,IAAI,uBAAgC,KAAK;AAGpE,MAAa,kBAAkB,IAAI,eAAuB,mBAAmB;CAC3E,YAAY;CACZ,eAAe;AAChB,CAAA;AAID,MAAa,kBAAkB,IAAI,uBAA+B,OAAO,eAAe,CAAC;AAIzF,MAAa,cAAc,IAAI,uBAAgC,KAAK;AAIpE,MAAa,oBAAoB,IAAI,uBAAgC,KAAK;AChB1E,MAAa,kBAAkB,IAAI,eAA+B,wBAAwB;ACwC1F,MAAa,2BAA2B,IAAI,eAAqC,wBAAwB;ACnDzG,IAAa,mBAAb,MAAa,iBAAgB;CAEV,kBAAkB,IAAI,QAAO;CAGrC,aAAa,KAAK,gBAAgB,aAAY;CAGvD,YAAS;EACP,KAAK,gBAAgB,KAAI;CAC3B;CAVW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAhB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,YADH;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EAD5B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACElC,IAAa,OAAb,MAAiB;CACf,KAAa;CACb,WAAmB;CACnB,YAAoB;CACpB,WAAmB;CACnB,OAAe;CACf,SAAiB;CACjB,OAAe;CACf,SAAiB;CACjB,OAAe;CACf,QAAgB;CAChB,QAAgB;CAChB,aAAuB,CAAA;CACvB,WAAqB,CAAA;CACrB,UAAkB;CAClB,QAAgB;CAEhB,aAAqB;AACtB;AAKD,IAAa,cAAb,MAAa,YAAW;CACd;CAEA,SAAS,OAAO,YAAY;CAEpC,cAAA;EACE,MAAM,QAAQ,aAAa,QAAQ,KAAK,OAAO,KAAK,cAAc;EAClE,KAAK,OAAO,QAAQ,KAAK,MAAM,KAAK,IAAI,IAAI,KAAI;CAClD;CAEA,IAAI,OAAI;EACN,OAAO,KAAK,MAAM;CACpB;CAGA,IAAI,WAAQ;EACV,OAAO,KAAK;CACd;CAGA,IAAI,SAAS,MAAU;EACrB,KAAK,OAAO;EACZ,aAAa,QAAQ,KAAK,OAAO,KAAK,gBAAgB,KAAK,UAAU,IAAI,CAAC;CAC5E;CAGA,aAAU;EACR,aAAa,WAAW,KAAK,OAAO,KAAK,cAAc;CACzD;CA5BW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAW,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAX,OAAA,QAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;CAAW,CAAA;;AAAX,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAW,YAAA,CAAA,EAJvB,MAAA,QAAA,CAAA;;;ACPD,IAAa,cAAb,MAAa,YAAW;CAEL,SAAS,OAAiB,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAE7B,YAAY,OAAgB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAE1C,QAAQ,KAAK,OAAO,WAAU;CAC9B,WAAW,KAAK,UAAU,WAAU;CAG5B,iBAAiB,EAChC,SAAS,IAAI,YAAW,CAAE,CAAC,IACzB,gBACA,mCAAmC,EAEtC;CAEgB,sBAAsB,OAAO,wBAAwB;CACrD,cAAc,OAAO,WAAW;CAChC,eAAe,OAAO,gBAAgB;CACtC,aAAa,OAAO,UAAU;CAC9B,OAAO,OAAO,UAAU;CACxB,SAAS,OAAO,YAAY;CAE5B,SAAS,OAAO,QAAQ,EAAE,UAAU,KAAI,CAAE;CAE3D,cAAA;EACE,MAAM,cAAc,KAAK,YAAY;EAErC,IAAI,aAAa,OAAO;GACtB,KAAK,UAAU,IAAI,IAAI;GACvB,KAAK,OAAO,IAAI,YAAY,YAAY,CAAA,CAAE;EAC5C;EAGA,KAAK,aAAa,WACf,KAAK,mBAAmB,KAAK,UAAU,CAAC,CAAC,CACzC,gBAAgB,KAAK,OAAM,CAAE;CAClC;CAMA,MAAM,UAAkB,UAAkB,OAAa;EACrD,IAAI,SAAS,IAAI,WAAU,CAAE,CAC1B,IAAI,YAAY,QAAQ,CAAC,CACzB,IAAI,YAAY,QAAQ;EAE3B,IAAI,OACF,SAAS,OAAO,IAAI,SAAS,MAAM,SAAQ,CAAE;EAG/C,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,OAAO,KAAK,OAAO,KAAK;EAE3D,OAAO,KAAK,KACT,KAAW,KAAK,OAAO,SAAQ,GAAI,KAAK,cAAc,CAAC,CACvD,KACC,KAAK,SAAS,KAAK,mBAAmB,IAAI,CAAC,GAC3C,YAAY,UAAS;GACnB,QAAQ,IAAI,gBAAgB,KAAK;GACjC,OAAO,iBAAiB,KAAK;EAC/B,CAAC,CAAC;CAER;CAGA,cAAc,QAAgB,OAAa;EACzC,IAAI,SAAS,IAAI,WAAU,CAAE,CAAC,IAAI,UAAU,MAAM;EAClD,IAAI,OACF,SAAS,OAAO,IAAI,SAAS,MAAM,SAAQ,CAAE;EAG/C,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,OAAO,KAAK,OAAO,KAAK;EAE3D,OAAO,KAAK,KAAK,KAAW,KAAK,OAAO,SAAQ,GAAI,KAAK,cAAc,CAAC,CAAC,KACvE,KAAK,SAAS,KAAK,mBAAmB,IAAI,CAAC,GAC3C,YAAY,UAAS;GACnB,QAAQ,IAAI,uBAAuB,KAAK;GACxC,OAAO,iBAAiB,KAAK;EAC/B,CAAC,CAAC;CAEN;CAEA,SAAM;EAEJ,KAAK,YAAY,WAAU;EAC3B,KAAK,UAAU,IAAI,KAAK;EACxB,KAAK,OAAO,IAAI,CAAA,CAAE;EAKlB,IAAI,KAAK,QAAQ;GACf,MAAM,aAAa,KAAK,OAAO;GAG/B,IAAI,WAAW,SAAS,KAAK,OAAO,KAAK,UAAU,GACjD;GAIF,KAAK,OAAO,SAAS,CAAC,KAAK,OAAO,KAAK,UAAU,GAAG,EAClD,aAAa,EAAE,WAAW,mBAAmB,UAAU,EAAC,EACzD,CAAA;EACH;CACF;CAGQ,mBAAmB,MAAU;EACnC,KAAK,YAAY,WAAW;EAC5B,KAAK,UAAU,IAAI,IAAI;EACvB,KAAK,OAAO,IAAI,KAAK,YAAY,CAAA,CAAE;CACrC;CAjHW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAW,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAX,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAW,YADE;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAW,YAAA,CAAA;EADvB,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;;ACblC,SAAgB,qBAAwB,QAAa,QAAW;CAC9D,IAAI,CAAC,UAAU,CAAC,QAAQ,OAAO;CAC/B,OAAO,OAAO,MAAK,SAAQ,OAAO,SAAS,IAAI,CAAC;AAClD;ACIA,IAAa,cAAb,MAAa,YAAW;CACL,cAAc,OAAO,WAAW;CAO1C,iBAAiB,gBAAwB;EAC9C,IAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG,OAAO;EAE3D,MAAM,YAAY,KAAK,YAAY,MAAK,KAAM,CAAA;EAG9C,IAAI,UAAU,SAAA,OAAmB,GAAG,OAAO;EAG3C,IAAI,eAAe,SAAA,MAAyB,GAAG,OAAO;EAGtD,OAAO,qBAAqB,WAAW,cAAc;CACvD;CAMK,WAAW,cAAsB;EACtC,MAAM,YAAY,KAAK,YAAY,MAAK,KAAM,CAAA;EAC9C,IAAI,UAAU,SAAA,OAAmB,GAAG,OAAO;EAE3C,OAAO,qBAAqB,WAAW,YAAY;CACrD;CAhCa,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAW,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAX,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAW,YAHV;CAAM,CAAA;;AAGP,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAW,YAAA,CAAA;EAJvB,MAAA;EAAW,MAAA,CAAA,EACV,YAAY,OACb,CAAA;;;AC+BD,IAAa,2BAAb,MAAa,yBAAwB;CAAxB,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAwB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAxB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAwB,cAAA;EAAA,UAAA;EAAA,UAAA;EAAA,UAlCzB;;;;;EAIT,UAAA;EAAA,QAAA,CAAA,+OAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MA4BS;GAAkB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA,UAAA,CAAA,oBAAA;EAAA,CAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAEjB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAwB,YAAA,CAAA;EApCpC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAgB,UAChB;;;;;GAIT,iBA2BgB,wBAAwB;GAAM,SACtC,CAAC,kBAAkB;GAAC,QAAA,CAAA,+OAAA;EAAA,CAAA;;;AC9B/B,IAAa,gBAAb,MAAa,cAAa;CAChB,aAAgC;CACvB,UAAU,OAAO,OAAO;CAGjC,iBAAiB;CAElB,OAAI;EACT,KAAK;EAEL,IAAI,CAAC,KAAK,YACR,KAAK,aAAa,KAAK,QAAQ,OAAO;GACpC,aAAa;GACb,kBAAkB,KAAK,QACpB,SAAQ,CAAE,CACV,OAAM,CAAE,CACR,mBAAkB,CAAE,CACpB,iBAAgB;EACpB,CAAA;EAEH,IAAI,CAAC,KAAK,WAAW,YAAW,GAAI;GAClC,MAAM,gBAAgB,IAAI,gBAAgB,wBAAwB;GAClE,KAAK,WAAW,OAAO,aAAa;EACtC;CACF;CAEO,OAAI;EACT,IAAI,KAAK,iBAAiB,GACxB,KAAK;EAGP,IAAI,KAAK,mBAAmB,KAAK,KAAK,cAAc,KAAK,WAAW,YAAW,GAC7E,KAAK,WAAW,OAAM;CAE1B;CAKO,QAAK;EACV,KAAK,iBAAiB;EACtB,IAAI,KAAK,cAAc,KAAK,WAAW,YAAW,GAChD,KAAK,WAAW,OAAM;CAE1B;CAEA,cAAW;EACT,IAAI,KAAK,YACP,KAAK,WAAW,QAAO;CAE3B;CAlDW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAa,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAb,OAAA,QAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;CAAa,CAAA;;AAAb,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAa,YAAA,CAAA,EADzB,MAAA,QAAA,CAAA;;ACCD,IAAa,sBAAb,MAAa,oBAAmB;CACb,WAAW;CACX,YAAY,OAAO,WAAW;CAE/C,aAAa,SAAiB,QAAgB,WAAmB,KAAK,UAAQ;EAC5E,KAAK,UAAU,KAAK,SAAS,QAAQ;GACnC;GACA,YAAY,CAAC,cAAc;EAC5B,CAAA;CACH;CAEA,QAAQ,SAAiB,WAAmB,KAAK,UAAQ;EACvD,KAAK,aAAa,SAAS,WAAW,QAAQ;CAChD;CAEA,KAAK,SAAiB,WAAmB,KAAK,UAAQ;EACpD,KAAK,aAAa,SAAS,WAAW,QAAQ;CAChD;CAEA,OAAO,SAAiB,WAAmB,KAAK,UAAQ;EACtD,KAAK,aAAa,SAAS,WAAW,QAAQ;CAChD;CAEA,QAAQ,SAAiB,WAAmB,KAAK,UAAQ;EACvD,KAAK,aAAa,SAAS,WAAW,QAAQ;CAChD;CAEA,MAAM,SAAiB,WAAmB,KAAK,UAAQ;EACrD,KAAK,aAAa,SAAS,WAAW,QAAQ;CAChD;CA7BW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAnB,OAAA,QAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;CAAmB,CAAA;;AAAnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA,EAD/B,MAAA,QAAA,CAAA;;ACFD,SAAgB,aACd,MAA+C;CAE/C,IAAI,CAAC,MAAM,OAAO;CAElB,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,eAAe,SAAS,EAC5C,UAAU,gBACX,CAAA;AACH;ACVA,IAAa,uBAAb,MAAiC;CASb;CAER;CAVO,eAAe,uBAAe,IAAI,IAAG,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGhD,WAAwB,eAAe,MAAM,KAAK,KAAK,aAAY,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CACvE,UAAU,eAAe,KAAK,aAAY,CAAE,CAAC,SAAS,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CACxD,WAAW,eAAe,KAAK,aAAY,CAAE,CAAC,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAEhE,YACkB,WAAW,OAC3B,gBAAqB,CAAA,GACb,cAAA;EAFQ,KAAA,WAAA;EAER,KAAA,eAAA;EAER,IAAI,cAAc,SAAS,GACzB,KAAK,OAAO,GAAG,aAAa;CAEhC;CAGA,WAAW,OAAQ;EACjB,MAAM,MAAM,KAAK,aAAY;EAC7B,IAAI,CAAC,KAAK,cACR,OAAO,IAAI,IAAI,KAAK;EAGtB,KAAK,MAAM,QAAQ,KACjB,IAAI,KAAK,aAAa,MAAM,KAAK,GAC/B,OAAO;EAGX,OAAO;CACT;CAEA,OAAO,GAAG,QAAW;EACnB,KAAK,aAAa,QAAO,QAAM;GAC7B,MAAM,SAAS,IAAI,IAAI,GAAG;GAE1B,IAAI,CAAC,KAAK,UAAU;IAClB,OAAO,MAAK;IACZ,IAAI,OAAO,SAAS,GAClB,KAAK,gBAAgB,QAAQ,OAAO,OAAO,SAAS,EAAE;GAE1D,OACE,OAAO,SAAQ,MAAK,KAAK,gBAAgB,QAAQ,CAAC,CAAC;GAErD,OAAO;EACT,CAAC;CACH;CAEA,SAAS,GAAG,QAAW;EACrB,IAAI,OAAO,WAAW,GAAG;EAEzB,KAAK,aAAa,QAAO,QAAM;GAC7B,MAAM,SAAS,IAAI,IAAI,GAAG;GAC1B,IAAI,KAAK,cAAc;IACrB,MAAM,2BAAW,IAAI,IAAG;IAExB,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,WAAW,QACpB,IAAI,KAAK,aAAa,SAAS,KAAK,GAClC,SAAS,IAAI,OAAO;IAK1B,SAAS,SAAQ,SAAQ,OAAO,OAAO,IAAI,CAAC;GAC9C,OACE,OAAO,SAAQ,MAAK,OAAO,OAAO,CAAC,CAAC;GAEtC,OAAO;EACT,CAAC;CACH;CAEA,OAAO,OAAQ;EACb,KAAK,WAAW,KAAK,IAAI,KAAK,SAAS,KAAK,IAAI,KAAK,OAAO,KAAK;CACnE;CAEA,QAAK;EACH,IAAI,KAAK,aAAY,CAAE,CAAC,SAAS,GAAG;EACpC,KAAK,aAAa,oBAAI,IAAI,IAAG,CAAE;CACjC;CAGQ,gBAAgB,KAAa,OAAQ;EAC3C,IAAI,KAAK,cAAc;GACrB,IAAI;GACJ,KAAK,MAAM,WAAW,KACpB,IAAI,KAAK,aAAa,SAAS,KAAK,GAAG;IACrC,YAAY;IACZ;GACF;GAEF,IAAI,cAAc,QAChB,IAAI,OAAO,SAAS;EAExB;EACA,IAAI,IAAI,KAAK;CACf;AACD;AClGD,SAAgB,eAAe,OAAiB,aAAmB;CAoBjE,OAAO,kDAAkD,YAAW,IAnBjD,MAAM,OACtB,KAAK,UAAS;EAEb,MAAM,OAAO,MAAM,KAChB,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,EAAC,KAAM,CAAE,CAAC,CAClD,KAAK,MAAM;EAUd,OAAO,cARU,OAAO,MAAM,KAAI,KAAM,UAQX,IAPX,MAAc,WAC5B,cAAe,MAAc,SAAQ,KACrC,KACc,MAAc,WAC5B,eAAgB,MAAc,SAAQ,KACtC,GAEiD,eAAgB,MAAM;CAC7E,CAAC,CAAC,CACD,KAAK,IAEiE,EAAU;AACrF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kuzntsv/uikit",
3
- "version": "1.0.92",
3
+ "version": "1.0.93",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^22.2.2",
6
6
  "@angular/core": "^22.2.2",
@@ -32,6 +32,7 @@ export declare const BYPASS_AUTH: HttpContextToken<boolean>;
32
32
  export declare const DEFAULT_TIMEOUT: InjectionToken<number>;
33
33
  export declare const REQUEST_TIMEOUT: HttpContextToken<number>;
34
34
  export declare const SKIP_LOADER: HttpContextToken<boolean>;
35
+ export declare const SKIP_NOTIFICATION: HttpContextToken<boolean>;
35
36
  export interface AppEnvironment {
36
37
  appVersion: string;
37
38
  production: boolean;