@sinequa/atomic-angular 1.5.2 → 1.5.5
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.
- package/README.md +28 -0
- package/features/auth/i18n/de.json +3 -1
- package/features/auth/i18n/en.json +3 -1
- package/features/auth/i18n/fr.json +3 -1
- package/fesm2022/sinequa-atomic-angular.mjs +464 -202
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +109 -24
- package/package.json +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, HostBinding, Component, Pipe, InjectionToken, computed, ChangeDetectorRef, DestroyRef, LOCALE_ID, Inject, Optional, input, output, signal, effect, assertInInjectionContext, runInInjectionContext, Injector, EventEmitter, Directive, viewChild, ElementRef, afterNextRender, untracked, linkedSignal, model, TemplateRef, HostListener, Renderer2, contentChildren, contentChild, booleanAttribute, resource, ChangeDetectionStrategy, ViewContainerRef, numberAttribute, viewChildren, afterRenderEffect, afterEveryRender } from '@angular/core';
|
|
2
|
+
import { Injectable, inject, HostBinding, Component, Pipe, InjectionToken, computed, ChangeDetectorRef, DestroyRef, LOCALE_ID, Inject, Optional, input, output, signal, effect, assertInInjectionContext, runInInjectionContext, EnvironmentInjector, Injector, EventEmitter, Directive, viewChild, ElementRef, afterNextRender, untracked, linkedSignal, model, TemplateRef, HostListener, Renderer2, contentChildren, contentChild, booleanAttribute, resource, ChangeDetectionStrategy, ViewContainerRef, numberAttribute, viewChildren, afterRenderEffect, afterEveryRender } from '@angular/core';
|
|
3
3
|
import { BehaviorSubject, Subscription, firstValueFrom, catchError, map, Subject, of, tap, EMPTY, throwError, filter, shareReplay, switchMap, from, fromEvent, debounceTime } from 'rxjs';
|
|
4
4
|
import { TranslocoService, TranslocoPipe, provideTranslocoScope } from '@jsverse/transloco';
|
|
5
|
-
import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, FaIconComponent, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, LinkComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SquareCheckIcon, SquareMinusIcon, SquareIcon, SwitchComponent, SelectOptionDirective, DialogService, XMarkIcon, InboxIcon, SparklesIcon, FileOutputIcon, TabsComponent, TabsListComponent, TabComponent, SidebarMenuComponent, SidebarMenuItemComponent, SidebarMenuButtonComponent, TooltipDirective, FrownIcon, ChevronLeftIcon, ChevronsLeftIcon, ChevronsRightIcon, ArrowUpAzIcon, ArrowDownZaIcon, ArrowUpRightFromSquareIcon, ToggleRightIcon, ToggleLeftIcon, Separator, SheetCloseDirective, ArrowLeftIcon, SheetService, DateRangePickerDirective, DatepickerDirective, FilterIcon, FilterXIcon, IconButtonComponent, HighlighterIcon, TagsIcon, SpinnerIcon, MagnifyingGlassIcon, LoadingCircleIcon, CircleCheckIcon, PopoverComponent, BellIcon, TrashIcon, BarsIcon,
|
|
5
|
+
import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, FaIconComponent, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, LinkComponent, CardComponent, CardHeaderComponent, CardContentComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SquareCheckIcon, SquareMinusIcon, SquareIcon, SwitchComponent, SelectOptionDirective, DialogService, XMarkIcon, InboxIcon, SparklesIcon, FileOutputIcon, TabsComponent, TabsListComponent, TabComponent, SidebarMenuComponent, SidebarMenuItemComponent, SidebarMenuButtonComponent, TooltipDirective, FrownIcon, ChevronLeftIcon, ChevronsLeftIcon, ChevronsRightIcon, ArrowUpAzIcon, ArrowDownZaIcon, ArrowUpRightFromSquareIcon, ToggleRightIcon, ToggleLeftIcon, Separator, SheetCloseDirective, ArrowLeftIcon, SheetService, DateRangePickerDirective, DatepickerDirective, FilterIcon, FilterXIcon, IconButtonComponent, HighlighterIcon, TagsIcon, SpinnerIcon, MagnifyingGlassIcon, LoadingCircleIcon, CircleCheckIcon, PopoverComponent, BellIcon, TrashIcon, BarsIcon, CardFooterComponent, InputGroupInput, InputGroupComponent, InputGroupAddonComponent, EyeSlashIcon, EyeIcon, BookmarkIcon, PopoverContentComponent, UserIcon, FolderIcon, VerticalDividerComponent, CommentIcon, ThumbsUpIcon, ThumbsDownIcon, DropdownDirective, SearchIcon, TriangleAlertIcon, ListFilterIcon, BreakpointObserverService, TrashCanIcon, CircleXIcon, InfoCircleIcon, HorizontalDividerComponent, HistoryIcon, StarIcon, FlagEnglishIcon, FlagFrenchIcon, EditIcon, UndoIcon, SaveIcon, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent } from '@sinequa/ui';
|
|
6
6
|
import highlightWords from 'highlight-words';
|
|
7
7
|
import { ActivatedRoute, Router, NavigationEnd, RouterLink, RouterLinkActive, RouterModule } from '@angular/router';
|
|
8
8
|
import { withDevtools } from '@angular-architects/ngrx-toolkit';
|
|
9
9
|
import { signalStore, signalStoreFeature, withState, withMethods, patchState, getState, withComputed } from '@ngrx/signals';
|
|
10
|
-
import { EngineType, fetchApp, extraColumns, sysLang, globalConfig, getQueryParamsFromUrl, clearSessionTokens, login, info,
|
|
10
|
+
import { EngineType, fetchApp, extraColumns, sysLang, globalConfig, getQueryParamsFromUrl, clearSessionTokens, login, info, error, setGlobalConfig, initializeAppConfig, notify, addConcepts, queryParamsFromUrl, patchUserSettings, deleteUserSettings, fetchUserSettings, warn, buildPathsAndLevels, escapeExpr, isAuthenticated, isExpired, debug, AuditEventType, fetchSuggest, isObject, Audit, getMetadata, bisect, isNotInputEvent, fetchSponsoredLinks, fetchQuery, translateAggregationToDateOptions, aggItemRegex, parseValueAndOperatorFromItem, fetchSuggestField, fetchSimilarDocuments, logout, fetchChangePassword, fetchSendPasswordResetEmail, expiresSoon, suggestionsToTreeAggregationNodes, labels, fetchLabels, guid, getRelativeDate, createUserProfile, deleteUserProfileProperty, patchUserProfile, isJsonable, addAuditAdditionalInfo, getToken, setToken, createHeaders } from '@sinequa/atomic';
|
|
11
11
|
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
|
|
12
12
|
import { DatePipe, DATE_PIPE_DEFAULT_TIMEZONE, DATE_PIPE_DEFAULT_OPTIONS, Location, NgTemplateOutlet, NgStyle, NgClass, NgComponentOutlet } from '@angular/common';
|
|
13
|
-
import { HttpClient, HttpParams, httpResource, HttpResponse,
|
|
13
|
+
import { HttpClient, HttpParams, httpResource, HttpResponse, HttpContextToken, HttpHeaders } from '@angular/common/http';
|
|
14
14
|
import { Title, DomSanitizer } from '@angular/platform-browser';
|
|
15
15
|
import { cva } from 'class-variance-authority';
|
|
16
16
|
import * as i1 from '@angular/forms';
|
|
@@ -1525,100 +1525,174 @@ function withThemes(app, themes) {
|
|
|
1525
1525
|
}
|
|
1526
1526
|
|
|
1527
1527
|
/**
|
|
1528
|
-
* Signs
|
|
1528
|
+
* Signs the user in according to the resolved {@link globalConfig.authMode}.
|
|
1529
1529
|
*
|
|
1530
|
-
*
|
|
1531
|
-
*
|
|
1532
|
-
* credentials
|
|
1533
|
-
*
|
|
1534
|
-
*
|
|
1535
|
-
*
|
|
1530
|
+
* The mode is expected to be resolved beforehand (by `initializeAppConfig`, awaited in
|
|
1531
|
+
* `bootstrapApp`). This function clears any existing session, then:
|
|
1532
|
+
* - `credentials` → redirect to the login form;
|
|
1533
|
+
* - `sso` → reload the page so the browser/proxy performs the handshake;
|
|
1534
|
+
* - `oauth` / `saml` → delegate to `login()`, which redirects to the provider;
|
|
1535
|
+
* - `bearer` → delegate to `login()`;
|
|
1536
|
+
* - `unknown` → `login()` tries SSO silently then resolves to credentials; on failure the login
|
|
1537
|
+
* form is shown.
|
|
1538
|
+
*
|
|
1539
|
+
* @returns A promise resolving to a boolean indicating whether the user is authenticated.
|
|
1536
1540
|
*/
|
|
1537
1541
|
async function signIn() {
|
|
1538
1542
|
assertInInjectionContext(signIn);
|
|
1539
1543
|
const router = inject(Router);
|
|
1540
1544
|
const lastUrlAfterNavigation = inject(NavigationService).urlAfterNavigation;
|
|
1541
|
-
const {
|
|
1545
|
+
const { loginPath, authMode } = globalConfig;
|
|
1542
1546
|
// Always clear authentication tokens first to clear any existing session
|
|
1543
1547
|
clearSessionTokens();
|
|
1544
|
-
//
|
|
1545
|
-
if (
|
|
1548
|
+
// Credentials: show the login form.
|
|
1549
|
+
if (authMode?.kind === "credentials") {
|
|
1546
1550
|
router.navigate([loginPath], { queryParams: { returnUrl: lastUrlAfterNavigation } });
|
|
1547
1551
|
return false; // prevent further execution
|
|
1548
1552
|
}
|
|
1549
|
-
// SSO
|
|
1550
|
-
|
|
1551
|
-
if (useSSO) {
|
|
1552
|
-
// reload the page to trigger SSO login
|
|
1553
|
+
// SSO: the browser/proxy handles authentication — reload to trigger it.
|
|
1554
|
+
if (authMode?.kind === "sso") {
|
|
1553
1555
|
window.location.reload();
|
|
1554
1556
|
return false; // prevent further execution
|
|
1555
1557
|
}
|
|
1556
|
-
//
|
|
1558
|
+
// oauth / saml / bearer / unknown — let login() drive the handshake.
|
|
1557
1559
|
try {
|
|
1558
1560
|
const response = await login();
|
|
1559
1561
|
if (response) {
|
|
1560
1562
|
info("Response from login", response);
|
|
1561
1563
|
return true;
|
|
1562
1564
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
+
// Not authenticated. For provider redirects (oauth/saml) the page is already navigating away,
|
|
1566
|
+
// so we don't touch the router. Otherwise (unknown resolved to credentials, or bearer failed)
|
|
1567
|
+
// show the login form.
|
|
1568
|
+
if (authMode?.kind !== "oauth" && authMode?.kind !== "saml") {
|
|
1569
|
+
router.navigate([loginPath], { queryParams: { returnUrl: lastUrlAfterNavigation } });
|
|
1565
1570
|
}
|
|
1571
|
+
return false;
|
|
1566
1572
|
}
|
|
1567
1573
|
catch (err) {
|
|
1568
1574
|
error("Error during login", err);
|
|
1569
|
-
|
|
1575
|
+
// A 401 is recoverable: the user just needs to authenticate → show the login form.
|
|
1576
|
+
if (err?.status === 401) {
|
|
1570
1577
|
error("Unauthorized access - please check your credentials:", err?.errorMessage);
|
|
1571
|
-
router.navigate([loginPath]);
|
|
1578
|
+
router.navigate([loginPath], { queryParams: { returnUrl: lastUrlAfterNavigation } });
|
|
1579
|
+
return false;
|
|
1572
1580
|
}
|
|
1573
|
-
|
|
1581
|
+
// Any other error (e.g. a misconfigured OAuth/SAML provider → 5xx) is fatal and not something
|
|
1582
|
+
// the user can resolve from the login form. Stop here: navigate straight to the error page with
|
|
1583
|
+
// the reason. We return false (instead of rethrowing) so the application does NOT initialize and
|
|
1584
|
+
// fire further authenticated API calls (e.g. usersettings → 401).
|
|
1585
|
+
const message = (err?.errorMessage ?? err?.message) || undefined;
|
|
1586
|
+
router.navigate(["/error"], { queryParams: { message } });
|
|
1587
|
+
return false;
|
|
1574
1588
|
}
|
|
1575
|
-
return false; // prevent further execution
|
|
1576
1589
|
}
|
|
1577
1590
|
|
|
1578
1591
|
/**
|
|
1579
1592
|
* Bootstraps the application by ensuring the user is authenticated and initializing the application.
|
|
1580
1593
|
*
|
|
1581
|
-
* This function first attempts to sign in the user
|
|
1582
|
-
* it
|
|
1583
|
-
*
|
|
1594
|
+
* This function first attempts to sign in the user via `signIn()`. If authentication is successful,
|
|
1595
|
+
* it initializes the application (stores and, optionally, dynamic routes) through `ApplicationService`
|
|
1596
|
+
* and waits for the initialization to complete before resolving. Any errors during authentication or
|
|
1597
|
+
* initialization are logged to the console, but the returned promise never rejects.
|
|
1584
1598
|
*
|
|
1585
|
-
*
|
|
1586
|
-
*
|
|
1587
|
-
*
|
|
1599
|
+
* Note: this function relies on Angular's injection context, so it must be called within one
|
|
1600
|
+
* (e.g. from `provideAppInitializer`).
|
|
1601
|
+
*
|
|
1602
|
+
* @param options - Configuration options for the bootstrap process.
|
|
1603
|
+
* @param options.createRoutes - Whether to create routes during initialization. Defaults to `true`.
|
|
1604
|
+
* @returns A promise that resolves to `true` when the application has been fully initialized,
|
|
1605
|
+
* or `false` when the user is not authenticated or an error occurred.
|
|
1588
1606
|
*/
|
|
1589
|
-
async function
|
|
1590
|
-
|
|
1591
|
-
//
|
|
1607
|
+
async function bootstrapApp({ createRoutes = true } = {}) {
|
|
1608
|
+
assertInInjectionContext(bootstrapApp);
|
|
1609
|
+
// Capture the injector synchronously: Angular's injection context does not survive across an
|
|
1610
|
+
// `await`, so after awaiting `initializeAppConfig()` we must re-enter it to inject services and
|
|
1611
|
+
// run `signIn()` (which use `inject()` internally).
|
|
1612
|
+
const injector = inject(EnvironmentInjector);
|
|
1613
|
+
// set the createRoutes flag early so it is defined even when sign-in fails
|
|
1614
|
+
// (ApplicationService.initialize() sets it again when it runs)
|
|
1592
1615
|
setGlobalConfig({ createRoutes });
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1616
|
+
// Resolve the authentication mode (queries the server pre-login when needed) BEFORE signing in.
|
|
1617
|
+
// Doing it here — rather than as a separate, concurrent APP_INITIALIZER — guarantees that
|
|
1618
|
+
// signIn() reads a fully resolved `authMode` and removes the long-standing bootstrap race.
|
|
1619
|
+
// Crucially, this also sets `backendUrl` BEFORE any service/store is constructed, so those that
|
|
1620
|
+
// derive their API URL from `globalConfig.backendUrl` (e.g. PrincipalStore) don't capture
|
|
1621
|
+
// `undefined` (which produced requests to `/undefined/api/v1/...`).
|
|
1622
|
+
try {
|
|
1623
|
+
await initializeAppConfig();
|
|
1624
|
+
}
|
|
1625
|
+
catch (err) {
|
|
1626
|
+
error('Error while initializing application config:', err);
|
|
1627
|
+
// Pre-login failed (e.g. a 500 "app not found: 'mint_rnd-yoyo'" for a bad app name). This is
|
|
1628
|
+
// fatal — backendUrl/authMode can't be resolved — so route to the error page with the reason
|
|
1629
|
+
// instead of continuing to signIn() (which would otherwise fail with a less meaningful error).
|
|
1630
|
+
const message = err?.errorMessage
|
|
1631
|
+
?? err?.message;
|
|
1632
|
+
runInInjectionContext(injector, () => inject(Router).navigate(['/error'], { queryParams: { message } }));
|
|
1633
|
+
return false;
|
|
1634
|
+
}
|
|
1635
|
+
// Inject ApplicationService AFTER initializeAppConfig (re-entering the injection context lost
|
|
1636
|
+
// across the await), so its dependent services/stores are constructed with `backendUrl` set.
|
|
1637
|
+
const applicationService = runInInjectionContext(injector, () => inject(ApplicationService));
|
|
1638
|
+
let authenticated = false;
|
|
1639
|
+
try {
|
|
1640
|
+
// Re-enter the injection context lost across the await above.
|
|
1641
|
+
authenticated = await runInInjectionContext(injector, () => signIn());
|
|
1642
|
+
}
|
|
1643
|
+
catch (err) {
|
|
1644
|
+
error('Error while signing in:', err);
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
if (!authenticated) {
|
|
1648
|
+
info('User not authenticated, skipping application initialization.');
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
info('User authenticated, initializing application...');
|
|
1652
|
+
try {
|
|
1653
|
+
await applicationService.initialize(createRoutes);
|
|
1654
|
+
info(`Application initialized successfully (createRoutes: ${createRoutes}).`);
|
|
1655
|
+
return true;
|
|
1656
|
+
}
|
|
1657
|
+
catch (err) {
|
|
1658
|
+
error(`Error initializing application (createRoutes: ${createRoutes}):`, err);
|
|
1659
|
+
// Authenticated, but the application failed to initialize (e.g. fetchApp/usersettings failed).
|
|
1660
|
+
// Route to the error page with the failure reason instead of leaving a half-initialized app.
|
|
1661
|
+
const message = err?.errorMessage
|
|
1662
|
+
?? err?.message;
|
|
1663
|
+
runInInjectionContext(injector, () => inject(Router).navigate(['/error'], { queryParams: { message } }));
|
|
1664
|
+
return false;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Bootstraps the application by ensuring the user is authenticated and initializing the application.
|
|
1669
|
+
*
|
|
1670
|
+
* @deprecated Use {@link bootstrapApp} instead, and let it inject `ApplicationService` itself.
|
|
1671
|
+
*
|
|
1672
|
+
* Migration — in your `app.config.ts`, replace:
|
|
1673
|
+
* ```ts
|
|
1674
|
+
* // ❌ Deprecated: eagerly injecting ApplicationService in the factory constructs it (and its
|
|
1675
|
+
* // dependent stores/services) BEFORE bootstrapApp resolves the config, so services that build
|
|
1676
|
+
* // their API URL from `globalConfig.backendUrl` capture `undefined` (→ `/undefined/api/v1/...`).
|
|
1677
|
+
* provideAppInitializer(() => withBootstrapApp(inject(ApplicationService), { createRoutes: true })),
|
|
1678
|
+
* ```
|
|
1679
|
+
* with:
|
|
1680
|
+
* ```ts
|
|
1681
|
+
* // ✅ bootstrapApp injects ApplicationService internally, AFTER initializeAppConfig() has set
|
|
1682
|
+
* // `backendUrl` and resolved the auth mode.
|
|
1683
|
+
* provideAppInitializer(() => bootstrapApp({ createRoutes: true })),
|
|
1684
|
+
* ```
|
|
1685
|
+
* (Remove the now-unused `inject` / `ApplicationService` imports.)
|
|
1686
|
+
*
|
|
1687
|
+
* @param applicationService - Ignored; kept for backward compatibility. `ApplicationService` is
|
|
1688
|
+
* provided in root and injected internally by {@link bootstrapApp}, so the instance is the same.
|
|
1689
|
+
* Passing `inject(ApplicationService)` here is discouraged — see the migration note above.
|
|
1690
|
+
* @param options - Configuration options for the bootstrap process.
|
|
1691
|
+
* @param options.createRoutes - Whether to create routes during initialization. Defaults to `true`.
|
|
1692
|
+
* @returns A promise that resolves when the bootstrap process is complete, regardless of success or failure.
|
|
1693
|
+
*/
|
|
1694
|
+
async function withBootstrapApp(_applicationService, { createRoutes = true } = {}) {
|
|
1695
|
+
await bootstrapApp({ createRoutes });
|
|
1622
1696
|
}
|
|
1623
1697
|
|
|
1624
1698
|
/**
|
|
@@ -3384,8 +3458,9 @@ class AppService {
|
|
|
3384
3458
|
*
|
|
3385
3459
|
* @remarks
|
|
3386
3460
|
* This method constructs an HTTP GET request to fetch the application configuration
|
|
3387
|
-
* using the `app` parameter from the global configuration. If the request fails,
|
|
3388
|
-
*
|
|
3461
|
+
* using the `app` parameter from the global configuration. If the request fails, it logs the
|
|
3462
|
+
* error and re-throws a normalized `Error` carrying the server's `errorMessage` when available
|
|
3463
|
+
* (e.g. "app not found: '...'"), so callers can surface the reason on the error page.
|
|
3389
3464
|
*
|
|
3390
3465
|
* @example
|
|
3391
3466
|
* ```typescript
|
|
@@ -3397,9 +3472,19 @@ class AppService {
|
|
|
3397
3472
|
getApp(appName) {
|
|
3398
3473
|
const app = appName || globalConfig.app;
|
|
3399
3474
|
const params = new HttpParams().set('app', app || '');
|
|
3400
|
-
return this.http.get(this.API_URL + '/app', { params }).pipe(catchError(
|
|
3401
|
-
console.error('AppService.getApp failure - error: ',
|
|
3402
|
-
|
|
3475
|
+
return this.http.get(this.API_URL + '/app', { params }).pipe(catchError((err) => {
|
|
3476
|
+
console.error('AppService.getApp failure - error: ', err);
|
|
3477
|
+
// Propagate the failure (previously swallowed with EMPTY, which hid the cause and surfaced a
|
|
3478
|
+
// generic "no elements in sequence" downstream). Surface the Sinequa error-envelope message
|
|
3479
|
+
// when present — e.g. a 500 with { errorMessage: "app not found: 'mint_rnd-yoyo'" } — so the
|
|
3480
|
+
// bootstrap/login flow can route to the error page with the actual reason.
|
|
3481
|
+
const e = err;
|
|
3482
|
+
const message = e?.error?.errorMessage ??
|
|
3483
|
+
e?.error?.message ??
|
|
3484
|
+
e?.errorMessage ??
|
|
3485
|
+
e?.message ??
|
|
3486
|
+
'Failed to fetch application configuration';
|
|
3487
|
+
return throwError(() => new Error(message));
|
|
3403
3488
|
}));
|
|
3404
3489
|
}
|
|
3405
3490
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AppService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -3421,12 +3506,13 @@ function AuthGuard() {
|
|
|
3421
3506
|
return async (_, state) => {
|
|
3422
3507
|
const router = inject(Router);
|
|
3423
3508
|
const principalStore = inject(PrincipalStore);
|
|
3424
|
-
const { loginPath,
|
|
3509
|
+
const { loginPath, authMode } = globalConfig;
|
|
3425
3510
|
if (state.url.startsWith("/login"))
|
|
3426
3511
|
return true;
|
|
3427
3512
|
// If the user is not authenticated, navigate to the login page.
|
|
3428
3513
|
// The login page handles every authentication method (credentials, OAuth, SAML).
|
|
3429
|
-
|
|
3514
|
+
// In SSO mode the browser/proxy carries the auth, so we let it through.
|
|
3515
|
+
if (!isAuthenticated() && authMode?.kind !== "sso") {
|
|
3430
3516
|
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3431
3517
|
return false;
|
|
3432
3518
|
}
|
|
@@ -3452,7 +3538,7 @@ function AuthGuard() {
|
|
|
3452
3538
|
// check if the password is expired and if the partition is editable
|
|
3453
3539
|
// changing password is only possible when user use credentials to auhtenticate
|
|
3454
3540
|
// only in credentials mode
|
|
3455
|
-
if (
|
|
3541
|
+
if (authMode?.kind === "credentials") {
|
|
3456
3542
|
const exp = passwordExpirationDate;
|
|
3457
3543
|
const editable = !!editablePartition;
|
|
3458
3544
|
if (editable && exp && isExpired(exp)) {
|
|
@@ -5278,101 +5364,160 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
5278
5364
|
}], ctorParameters: () => [], propDecorators: { article: [{ type: i0.Input, args: [{ isSignal: true, alias: "article", required: true }] }], aggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregation", required: true }] }], shadow: [{ type: i0.ViewChild, args: ["shadowRender", { ...{ read: ElementRef }, isSignal: true }] }], client: [{ type: i0.ViewChild, args: ["documentLocator", { ...{ read: ElementRef }, isSignal: true }] }] } });
|
|
5279
5365
|
|
|
5280
5366
|
class ErrorComponent {
|
|
5367
|
+
route = inject(ActivatedRoute);
|
|
5281
5368
|
router = inject(Router);
|
|
5282
|
-
|
|
5369
|
+
/**
|
|
5370
|
+
* Human-readable error detail shown on the page, taken from the `message` query param.
|
|
5371
|
+
* Callers navigating here can pass it, e.g. on an auth failure:
|
|
5372
|
+
* `router.navigate(["error"], { queryParams: { message: err.message } })`.
|
|
5373
|
+
*/
|
|
5374
|
+
message = (() => {
|
|
5375
|
+
const value = this.route.snapshot.queryParams['message'];
|
|
5376
|
+
return typeof value === 'string' && value.trim().length ? value : undefined;
|
|
5377
|
+
})();
|
|
5378
|
+
/** Navigate home and re-bootstrap the application (fresh authentication attempt). */
|
|
5379
|
+
goHome() {
|
|
5283
5380
|
this.router.navigate(['/']).then(() => window.location.reload());
|
|
5284
5381
|
}
|
|
5285
5382
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5286
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
5287
|
-
<
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
viewBox="0 0 24 24"
|
|
5294
|
-
fill="none"
|
|
5295
|
-
stroke="currentColor"
|
|
5296
|
-
stroke-width="2"
|
|
5297
|
-
stroke-linecap="round"
|
|
5298
|
-
stroke-linejoin="round">
|
|
5299
|
-
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
5300
|
-
<path d="M12 9v4" />
|
|
5301
|
-
<path d="M12 17h.01" />
|
|
5302
|
-
</svg>
|
|
5303
|
-
<h1 class="mb-4 text-4xl font-bold">Oops! Something went wrong</h1>
|
|
5304
|
-
<p class="text-muted-foreground mb-8 text-xl">We apologize for the inconvenience.</p>
|
|
5305
|
-
<div class="flex space-x-4">
|
|
5306
|
-
<button
|
|
5307
|
-
(click)="reload()"
|
|
5308
|
-
class="btn btn-outline rounded-lg border-neutral-300 text-neutral-700 transition hover:bg-neutral-200 hover:text-neutral-700">
|
|
5383
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: ErrorComponent, isStandalone: true, selector: "error-component, ErrorComponent", host: { classAttribute: "bg-background text-foreground grid min-h-dvh w-full place-content-center p-6" }, ngImport: i0, template: `
|
|
5384
|
+
<Card
|
|
5385
|
+
hover="no"
|
|
5386
|
+
class="bg-background border-(--popover-border) w-full max-w-md rounded-3xl border shadow-2xl">
|
|
5387
|
+
<CardHeader class="flex flex-col items-center gap-4 pt-8 text-center">
|
|
5388
|
+
<span
|
|
5389
|
+
class="bg-destructive/10 text-destructive inline-flex h-16 w-16 items-center justify-center rounded-full">
|
|
5309
5390
|
<svg
|
|
5310
|
-
class="mr-2 h-4 w-4"
|
|
5311
5391
|
xmlns="http://www.w3.org/2000/svg"
|
|
5312
|
-
|
|
5313
|
-
height="24"
|
|
5392
|
+
class="h-8 w-8"
|
|
5314
5393
|
viewBox="0 0 24 24"
|
|
5315
5394
|
fill="none"
|
|
5316
5395
|
stroke="currentColor"
|
|
5317
5396
|
stroke-width="2"
|
|
5318
5397
|
stroke-linecap="round"
|
|
5319
|
-
stroke-linejoin="round"
|
|
5320
|
-
|
|
5321
|
-
<path d="
|
|
5398
|
+
stroke-linejoin="round"
|
|
5399
|
+
aria-hidden="true">
|
|
5400
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
5401
|
+
<path d="M12 9v4" />
|
|
5402
|
+
<path d="M12 17h.01" />
|
|
5322
5403
|
</svg>
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5404
|
+
</span>
|
|
5405
|
+
|
|
5406
|
+
<div class="grid gap-1.5">
|
|
5407
|
+
<h1 class="text-2xl font-semibold tracking-tight">Something went wrong</h1>
|
|
5408
|
+
<p class="text-muted-foreground text-sm">
|
|
5409
|
+
We're sorry — the application ran into a problem and couldn't continue.
|
|
5410
|
+
</p>
|
|
5411
|
+
</div>
|
|
5412
|
+
</CardHeader>
|
|
5413
|
+
|
|
5414
|
+
<CardContent class="grid gap-5 pb-8">
|
|
5415
|
+
@if (message) {
|
|
5416
|
+
<div class="border-border bg-muted/40 grid gap-1 rounded-lg border p-3 text-left">
|
|
5417
|
+
<span class="text-muted-foreground text-[0.7rem] font-medium uppercase tracking-wider">
|
|
5418
|
+
Details
|
|
5419
|
+
</span>
|
|
5420
|
+
<p class="text-foreground text-sm break-words" role="alert">{{ message }}</p>
|
|
5421
|
+
</div>
|
|
5422
|
+
}
|
|
5423
|
+
|
|
5424
|
+
<div class="flex justify-center">
|
|
5425
|
+
<button variant="primary" (click)="goHome()">
|
|
5426
|
+
<svg
|
|
5427
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5428
|
+
class="mr-2 h-4 w-4"
|
|
5429
|
+
viewBox="0 0 24 24"
|
|
5430
|
+
fill="none"
|
|
5431
|
+
stroke="currentColor"
|
|
5432
|
+
stroke-width="2"
|
|
5433
|
+
stroke-linecap="round"
|
|
5434
|
+
stroke-linejoin="round"
|
|
5435
|
+
aria-hidden="true">
|
|
5436
|
+
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
|
|
5437
|
+
<path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
|
5438
|
+
</svg>
|
|
5439
|
+
Go to homepage
|
|
5440
|
+
</button>
|
|
5441
|
+
</div>
|
|
5442
|
+
</CardContent>
|
|
5443
|
+
</Card>
|
|
5444
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }] });
|
|
5328
5445
|
}
|
|
5329
5446
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ErrorComponent, decorators: [{
|
|
5330
5447
|
type: Component,
|
|
5331
5448
|
args: [{
|
|
5332
5449
|
selector: 'error-component, ErrorComponent',
|
|
5333
5450
|
standalone: true,
|
|
5334
|
-
imports: [
|
|
5451
|
+
imports: [
|
|
5452
|
+
ButtonComponent,
|
|
5453
|
+
CardComponent,
|
|
5454
|
+
CardHeaderComponent,
|
|
5455
|
+
CardContentComponent
|
|
5456
|
+
],
|
|
5457
|
+
host: {
|
|
5458
|
+
class: 'bg-background text-foreground grid min-h-dvh w-full place-content-center p-6'
|
|
5459
|
+
},
|
|
5335
5460
|
template: `
|
|
5336
|
-
<
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
viewBox="0 0 24 24"
|
|
5343
|
-
fill="none"
|
|
5344
|
-
stroke="currentColor"
|
|
5345
|
-
stroke-width="2"
|
|
5346
|
-
stroke-linecap="round"
|
|
5347
|
-
stroke-linejoin="round">
|
|
5348
|
-
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
5349
|
-
<path d="M12 9v4" />
|
|
5350
|
-
<path d="M12 17h.01" />
|
|
5351
|
-
</svg>
|
|
5352
|
-
<h1 class="mb-4 text-4xl font-bold">Oops! Something went wrong</h1>
|
|
5353
|
-
<p class="text-muted-foreground mb-8 text-xl">We apologize for the inconvenience.</p>
|
|
5354
|
-
<div class="flex space-x-4">
|
|
5355
|
-
<button
|
|
5356
|
-
(click)="reload()"
|
|
5357
|
-
class="btn btn-outline rounded-lg border-neutral-300 text-neutral-700 transition hover:bg-neutral-200 hover:text-neutral-700">
|
|
5461
|
+
<Card
|
|
5462
|
+
hover="no"
|
|
5463
|
+
class="bg-background border-(--popover-border) w-full max-w-md rounded-3xl border shadow-2xl">
|
|
5464
|
+
<CardHeader class="flex flex-col items-center gap-4 pt-8 text-center">
|
|
5465
|
+
<span
|
|
5466
|
+
class="bg-destructive/10 text-destructive inline-flex h-16 w-16 items-center justify-center rounded-full">
|
|
5358
5467
|
<svg
|
|
5359
|
-
class="mr-2 h-4 w-4"
|
|
5360
5468
|
xmlns="http://www.w3.org/2000/svg"
|
|
5361
|
-
|
|
5362
|
-
height="24"
|
|
5469
|
+
class="h-8 w-8"
|
|
5363
5470
|
viewBox="0 0 24 24"
|
|
5364
5471
|
fill="none"
|
|
5365
5472
|
stroke="currentColor"
|
|
5366
5473
|
stroke-width="2"
|
|
5367
5474
|
stroke-linecap="round"
|
|
5368
|
-
stroke-linejoin="round"
|
|
5369
|
-
|
|
5370
|
-
<path d="
|
|
5475
|
+
stroke-linejoin="round"
|
|
5476
|
+
aria-hidden="true">
|
|
5477
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
5478
|
+
<path d="M12 9v4" />
|
|
5479
|
+
<path d="M12 17h.01" />
|
|
5371
5480
|
</svg>
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5481
|
+
</span>
|
|
5482
|
+
|
|
5483
|
+
<div class="grid gap-1.5">
|
|
5484
|
+
<h1 class="text-2xl font-semibold tracking-tight">Something went wrong</h1>
|
|
5485
|
+
<p class="text-muted-foreground text-sm">
|
|
5486
|
+
We're sorry — the application ran into a problem and couldn't continue.
|
|
5487
|
+
</p>
|
|
5488
|
+
</div>
|
|
5489
|
+
</CardHeader>
|
|
5490
|
+
|
|
5491
|
+
<CardContent class="grid gap-5 pb-8">
|
|
5492
|
+
@if (message) {
|
|
5493
|
+
<div class="border-border bg-muted/40 grid gap-1 rounded-lg border p-3 text-left">
|
|
5494
|
+
<span class="text-muted-foreground text-[0.7rem] font-medium uppercase tracking-wider">
|
|
5495
|
+
Details
|
|
5496
|
+
</span>
|
|
5497
|
+
<p class="text-foreground text-sm break-words" role="alert">{{ message }}</p>
|
|
5498
|
+
</div>
|
|
5499
|
+
}
|
|
5500
|
+
|
|
5501
|
+
<div class="flex justify-center">
|
|
5502
|
+
<button variant="primary" (click)="goHome()">
|
|
5503
|
+
<svg
|
|
5504
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5505
|
+
class="mr-2 h-4 w-4"
|
|
5506
|
+
viewBox="0 0 24 24"
|
|
5507
|
+
fill="none"
|
|
5508
|
+
stroke="currentColor"
|
|
5509
|
+
stroke-width="2"
|
|
5510
|
+
stroke-linecap="round"
|
|
5511
|
+
stroke-linejoin="round"
|
|
5512
|
+
aria-hidden="true">
|
|
5513
|
+
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
|
|
5514
|
+
<path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
|
5515
|
+
</svg>
|
|
5516
|
+
Go to homepage
|
|
5517
|
+
</button>
|
|
5518
|
+
</div>
|
|
5519
|
+
</CardContent>
|
|
5520
|
+
</Card>
|
|
5376
5521
|
`
|
|
5377
5522
|
}]
|
|
5378
5523
|
}] });
|
|
@@ -10588,6 +10733,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
10588
10733
|
}]
|
|
10589
10734
|
}], propDecorators: { cancel: [{ type: i0.Output, args: ["cancel"] }], success: [{ type: i0.Output, args: ["success"] }], userName: [{ type: i0.Input, args: [{ isSignal: true, alias: "userName", required: false }] }, { type: i0.Output, args: ["userNameChange"] }] } });
|
|
10590
10735
|
|
|
10736
|
+
/**
|
|
10737
|
+
* Post-logout confirmation shown on the `/logout` route.
|
|
10738
|
+
*
|
|
10739
|
+
* Rendering this view (instead of the sign-in form) is essential for the external-auth modes
|
|
10740
|
+
* (`sso` / `oauth` / `saml`): the sign-in form auto-initiates the handshake when
|
|
10741
|
+
* `authMode` is external, so showing it on `/logout` would immediately re-authenticate the user
|
|
10742
|
+
* (a spinner that loops back in). This view gives a clear "signed out" state and a single explicit
|
|
10743
|
+
* action to sign in again.
|
|
10744
|
+
*/
|
|
10745
|
+
class SignedOutComponent {
|
|
10746
|
+
router = inject(Router);
|
|
10747
|
+
/**
|
|
10748
|
+
* Navigate to the login screen, which then drives the normal sign-in handshake.
|
|
10749
|
+
*
|
|
10750
|
+
* A `returnUrl` is required: in the external-auth modes the sign-in screen only navigates away
|
|
10751
|
+
* once the handshake completes IF a `returnUrl` is present (otherwise it stays on the loader).
|
|
10752
|
+
* We send the user back to the app root after signing in again.
|
|
10753
|
+
*/
|
|
10754
|
+
signInAgain() {
|
|
10755
|
+
this.router.navigate([globalConfig.loginPath ?? "/login"], {
|
|
10756
|
+
queryParams: { returnUrl: "/" }
|
|
10757
|
+
});
|
|
10758
|
+
}
|
|
10759
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: SignedOutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10760
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: SignedOutComponent, isStandalone: true, selector: "signed-out, SignedOut, signedout", providers: [provideTranslocoScope("login")], ngImport: i0, template: `
|
|
10761
|
+
<Card hover="no" class="bg-card rounded-xl shadow-sm">
|
|
10762
|
+
<CardHeader class="flex flex-col items-center gap-3">
|
|
10763
|
+
<img class="h-12 content-(--logo-large)" alt="logo" />
|
|
10764
|
+
<h2 class="text-lg font-semibold">
|
|
10765
|
+
{{ 'login.signedOutTitle' | transloco }}
|
|
10766
|
+
</h2>
|
|
10767
|
+
<p class="text-xs text-muted-foreground text-center">
|
|
10768
|
+
{{ 'login.signedOutDescription' | transloco }}
|
|
10769
|
+
</p>
|
|
10770
|
+
</CardHeader>
|
|
10771
|
+
|
|
10772
|
+
<CardContent />
|
|
10773
|
+
|
|
10774
|
+
<CardFooter class="mt-4 flex justify-center">
|
|
10775
|
+
<button
|
|
10776
|
+
type="button"
|
|
10777
|
+
class="w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90"
|
|
10778
|
+
(click)="signInAgain()"
|
|
10779
|
+
>
|
|
10780
|
+
{{ 'login.connect' | transloco }}
|
|
10781
|
+
</button>
|
|
10782
|
+
</CardFooter>
|
|
10783
|
+
</Card>
|
|
10784
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "directive", type: CardFooterComponent, selector: ".card-footer, card-footer, CardFooter, cardfooter", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
10785
|
+
}
|
|
10786
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: SignedOutComponent, decorators: [{
|
|
10787
|
+
type: Component,
|
|
10788
|
+
args: [{
|
|
10789
|
+
selector: "signed-out, SignedOut, signedout",
|
|
10790
|
+
imports: [
|
|
10791
|
+
TranslocoPipe,
|
|
10792
|
+
CardComponent,
|
|
10793
|
+
CardHeaderComponent,
|
|
10794
|
+
CardContentComponent,
|
|
10795
|
+
CardFooterComponent
|
|
10796
|
+
],
|
|
10797
|
+
providers: [provideTranslocoScope("login")],
|
|
10798
|
+
template: `
|
|
10799
|
+
<Card hover="no" class="bg-card rounded-xl shadow-sm">
|
|
10800
|
+
<CardHeader class="flex flex-col items-center gap-3">
|
|
10801
|
+
<img class="h-12 content-(--logo-large)" alt="logo" />
|
|
10802
|
+
<h2 class="text-lg font-semibold">
|
|
10803
|
+
{{ 'login.signedOutTitle' | transloco }}
|
|
10804
|
+
</h2>
|
|
10805
|
+
<p class="text-xs text-muted-foreground text-center">
|
|
10806
|
+
{{ 'login.signedOutDescription' | transloco }}
|
|
10807
|
+
</p>
|
|
10808
|
+
</CardHeader>
|
|
10809
|
+
|
|
10810
|
+
<CardContent />
|
|
10811
|
+
|
|
10812
|
+
<CardFooter class="mt-4 flex justify-center">
|
|
10813
|
+
<button
|
|
10814
|
+
type="button"
|
|
10815
|
+
class="w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90"
|
|
10816
|
+
(click)="signInAgain()"
|
|
10817
|
+
>
|
|
10818
|
+
{{ 'login.connect' | transloco }}
|
|
10819
|
+
</button>
|
|
10820
|
+
</CardFooter>
|
|
10821
|
+
</Card>
|
|
10822
|
+
`
|
|
10823
|
+
}]
|
|
10824
|
+
}] });
|
|
10825
|
+
|
|
10591
10826
|
/**
|
|
10592
10827
|
* Represents the LoginComponent class, which is responsible for handling the login functionality.
|
|
10593
10828
|
* This component is used to authenticate users and manage the user's authentication status.
|
|
@@ -10598,13 +10833,18 @@ class SignInComponent {
|
|
|
10598
10833
|
config = globalConfig;
|
|
10599
10834
|
/**
|
|
10600
10835
|
* True when authentication is handled outside the credentials form — i.e. by the
|
|
10601
|
-
* browser/proxy (`
|
|
10602
|
-
*
|
|
10603
|
-
*
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10836
|
+
* browser/proxy (`sso`) or by an auto-configured OAuth/SAML provider. In those modes
|
|
10837
|
+
* this screen shows a loader instead of a login form and initiates the handshake
|
|
10838
|
+
* automatically by calling `handleLogin()`.
|
|
10839
|
+
*
|
|
10840
|
+
* Note: the ambiguous `unknown` mode is intentionally excluded — it is resolved upstream
|
|
10841
|
+
* (in `login()`/`signIn()`) to either `sso` or `credentials` before this screen renders,
|
|
10842
|
+
* so reaching here in `unknown` should still show the form, never a dead-end loader.
|
|
10843
|
+
*/
|
|
10844
|
+
externalAuth = (() => {
|
|
10845
|
+
const kind = globalConfig.authMode?.kind;
|
|
10846
|
+
return kind === "sso" || kind === "oauth" || kind === "saml";
|
|
10847
|
+
})();
|
|
10608
10848
|
class = input(...(ngDevMode ? [undefined, { debugName: "class" }] : []));
|
|
10609
10849
|
forgotPassword = output();
|
|
10610
10850
|
username = model("", ...(ngDevMode ? [{ debugName: "username" }] : []));
|
|
@@ -10700,10 +10940,13 @@ class SignInComponent {
|
|
|
10700
10940
|
this.auditService.notifyLogin();
|
|
10701
10941
|
}
|
|
10702
10942
|
return result;
|
|
10703
|
-
}).catch(
|
|
10704
|
-
warn("An error occurred while logging in",
|
|
10943
|
+
}).catch((err) => {
|
|
10944
|
+
warn("An error occurred while logging in", err);
|
|
10705
10945
|
this.auditService.notify({ type: 'Login_Denied' });
|
|
10706
|
-
|
|
10946
|
+
// Surface the failure reason on the error page (e.g. "OAuth provider not found: identity-dev")
|
|
10947
|
+
// so the user/admin knows what to fix, instead of a bare generic error screen.
|
|
10948
|
+
const message = (err?.errorMessage ?? err?.message) || undefined;
|
|
10949
|
+
this.router.navigate(["error"], { queryParams: { message } });
|
|
10707
10950
|
return false;
|
|
10708
10951
|
});
|
|
10709
10952
|
}
|
|
@@ -10718,7 +10961,16 @@ class SignInComponent {
|
|
|
10718
10961
|
}
|
|
10719
10962
|
this.auditService.notifyLogin();
|
|
10720
10963
|
const { createRoutes = false } = globalConfig;
|
|
10721
|
-
|
|
10964
|
+
try {
|
|
10965
|
+
await this.applicationService.initialize(createRoutes);
|
|
10966
|
+
}
|
|
10967
|
+
catch (initErr) {
|
|
10968
|
+
// Authenticated, but the application failed to initialize (e.g. fetchApp failed). Surface the
|
|
10969
|
+
// reason on the error page rather than leaving the user stuck on the login form.
|
|
10970
|
+
const { errorMessage, message } = (initErr ?? {});
|
|
10971
|
+
this.router.navigate(["/error"], { queryParams: { message: errorMessage ?? message } });
|
|
10972
|
+
return;
|
|
10973
|
+
}
|
|
10722
10974
|
this.checkPasswordExpiresSoon();
|
|
10723
10975
|
const url = this.route.snapshot.queryParams["returnUrl"] || "/";
|
|
10724
10976
|
this.router.navigateByUrl(url);
|
|
@@ -10885,10 +11137,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
10885
11137
|
|
|
10886
11138
|
class AuthPageComponent {
|
|
10887
11139
|
mode = input(...(ngDevMode ? [undefined, { debugName: "mode" }] : []));
|
|
10888
|
-
view = linkedSignal(() => this.mode() ??
|
|
11140
|
+
view = linkedSignal(() => this.mode() ?? this.routeView(), ...(ngDevMode ? [{ debugName: "view" }] : []));
|
|
10889
11141
|
username = signal("", ...(ngDevMode ? [{ debugName: "username" }] : []));
|
|
10890
11142
|
alert = signal(undefined, ...(ngDevMode ? [{ debugName: "alert" }] : []));
|
|
10891
11143
|
route = inject(ActivatedRoute);
|
|
11144
|
+
/**
|
|
11145
|
+
* Default view derived from the route. The `/logout` route renders the "signed out" confirmation
|
|
11146
|
+
* (NOT the sign-in form): in external-auth modes the form would auto-restart the handshake and
|
|
11147
|
+
* re-authenticate the user, defeating the logout. Everything else defaults to the sign-in form.
|
|
11148
|
+
*/
|
|
11149
|
+
routeView() {
|
|
11150
|
+
return this.route.snapshot.routeConfig?.path === "logout" ? "signedout" : "signin";
|
|
11151
|
+
}
|
|
10892
11152
|
constructor() {
|
|
10893
11153
|
const params = this.route.snapshot.queryParams;
|
|
10894
11154
|
const u = params["username"] || "";
|
|
@@ -10915,6 +11175,8 @@ class AuthPageComponent {
|
|
|
10915
11175
|
(cancel)="view.set('signin')"
|
|
10916
11176
|
(success)="view.set('signin')"
|
|
10917
11177
|
/>
|
|
11178
|
+
} @else if (view() === 'signedout') {
|
|
11179
|
+
<signed-out />
|
|
10918
11180
|
} @else {
|
|
10919
11181
|
<sign-in
|
|
10920
11182
|
(changePassword)="view.set('changepassword')"
|
|
@@ -10922,14 +11184,14 @@ class AuthPageComponent {
|
|
|
10922
11184
|
/>
|
|
10923
11185
|
}
|
|
10924
11186
|
</div>
|
|
10925
|
-
`, isInline: true, dependencies: [{ kind: "component", type: SignInComponent, selector: "signIn, signin, sign-in", inputs: ["class", "username", "password"], outputs: ["forgotPassword", "usernameChange", "passwordChange"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "redirectAfterCancel", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "component", type: ForgotPasswordComponent, selector: "forgot-password, ForgotPassword, forgotpassword", inputs: ["userName"], outputs: ["cancel", "success", "userNameChange"] }] });
|
|
11187
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SignInComponent, selector: "signIn, signin, sign-in", inputs: ["class", "username", "password"], outputs: ["forgotPassword", "usernameChange", "passwordChange"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "redirectAfterCancel", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "component", type: ForgotPasswordComponent, selector: "forgot-password, ForgotPassword, forgotpassword", inputs: ["userName"], outputs: ["cancel", "success", "userNameChange"] }, { kind: "component", type: SignedOutComponent, selector: "signed-out, SignedOut, signedout" }] });
|
|
10926
11188
|
}
|
|
10927
11189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AuthPageComponent, decorators: [{
|
|
10928
11190
|
type: Component,
|
|
10929
11191
|
args: [{
|
|
10930
11192
|
selector: "auth-page, AuthPage, authpage",
|
|
10931
11193
|
providers: [provideTranslocoScope("login")],
|
|
10932
|
-
imports: [SignInComponent, ChangePasswordComponent, ForgotPasswordComponent],
|
|
11194
|
+
imports: [SignInComponent, ChangePasswordComponent, ForgotPasswordComponent, SignedOutComponent],
|
|
10933
11195
|
host: { class: "min-h-screen grid place-items-center p-6 bg-background" },
|
|
10934
11196
|
template: `
|
|
10935
11197
|
<div class="w-full max-w-md">
|
|
@@ -10944,6 +11206,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
10944
11206
|
(cancel)="view.set('signin')"
|
|
10945
11207
|
(success)="view.set('signin')"
|
|
10946
11208
|
/>
|
|
11209
|
+
} @else if (view() === 'signedout') {
|
|
11210
|
+
<signed-out />
|
|
10947
11211
|
} @else {
|
|
10948
11212
|
<sign-in
|
|
10949
11213
|
(changePassword)="view.set('changepassword')"
|
|
@@ -11272,50 +11536,29 @@ class OverrideUserDialogComponent {
|
|
|
11272
11536
|
}
|
|
11273
11537
|
}
|
|
11274
11538
|
handleOverrideUser(username, domain) {
|
|
11275
|
-
const {
|
|
11539
|
+
const { createRoutes } = globalConfig;
|
|
11276
11540
|
if (username === undefined || domain === undefined) {
|
|
11277
11541
|
setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
|
|
11278
11542
|
}
|
|
11279
11543
|
else {
|
|
11280
11544
|
setGlobalConfig({ userOverrideActive: true, userOverride: { username, domain } });
|
|
11281
11545
|
}
|
|
11282
|
-
//
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
.then((value) => {
|
|
11299
|
-
if (value) {
|
|
11300
|
-
this.appService
|
|
11301
|
-
.initialize(createRoutes)
|
|
11302
|
-
.then(() => {
|
|
11303
|
-
const fullName = this.principalStore.fullName();
|
|
11304
|
-
notify.success(`Welcome back ${fullName}!`, { duration: 2000 });
|
|
11305
|
-
})
|
|
11306
|
-
.catch((err) => {
|
|
11307
|
-
error("An error occured while overriding (initialize)", err);
|
|
11308
|
-
notify.error(err.message, { duration: 2000 });
|
|
11309
|
-
setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
|
|
11310
|
-
});
|
|
11311
|
-
}
|
|
11312
|
-
})
|
|
11313
|
-
.catch((err) => {
|
|
11314
|
-
error("An error occured while overriding (login)", err);
|
|
11315
|
-
notify.error("An error occured while overriding (login)", { duration: 2000 });
|
|
11316
|
-
setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
|
|
11317
|
-
});
|
|
11318
|
-
}
|
|
11546
|
+
// Impersonation is header-driven: `createHeaders` adds `sinequa-override-user`/`-domain` to every
|
|
11547
|
+
// request while `userOverrideActive`, on top of the current (admin) session. We therefore do NOT
|
|
11548
|
+
// need to re-authenticate — re-initializing the stores refetches the principal/usersettings as the
|
|
11549
|
+
// overridden user. This works in every auth mode, including `credentials` where `login()` (without
|
|
11550
|
+
// credentials) is intentionally a no-op in atomic 2.0 and would otherwise skip initialization.
|
|
11551
|
+
this.appService
|
|
11552
|
+
.initialize(createRoutes)
|
|
11553
|
+
.then(() => {
|
|
11554
|
+
const fullName = this.principalStore.fullName();
|
|
11555
|
+
notify.success(`Welcome back ${fullName}!`, { duration: 2000 });
|
|
11556
|
+
})
|
|
11557
|
+
.catch((err) => {
|
|
11558
|
+
error("An error occurred while overriding (initialize)", err);
|
|
11559
|
+
notify.error("An error occurred while overriding (initialize)", { duration: 2000 });
|
|
11560
|
+
setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
|
|
11561
|
+
});
|
|
11319
11562
|
}
|
|
11320
11563
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: OverrideUserDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11321
11564
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.18", type: OverrideUserDialogComponent, isStandalone: true, selector: "override-user-dialog", inputs: { overrideUser: { classPropertyName: "overrideUser", publicName: "overrideUser", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { overrideUser: "overrideUserChange" }, providers: [provideTranslocoScope("dialogs")], viewQueries: [{ propertyName: "dialog", first: true, predicate: DialogComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
@@ -16006,8 +16249,18 @@ const bodyInterceptorFn = (request, next) => {
|
|
|
16006
16249
|
};
|
|
16007
16250
|
|
|
16008
16251
|
/**
|
|
16009
|
-
*
|
|
16010
|
-
*
|
|
16252
|
+
* Marks a request that has already been retried once after a re-authentication attempt.
|
|
16253
|
+
* Carried on the request's HttpContext (not sent to the server) so a second 401 on the retry
|
|
16254
|
+
* propagates instead of triggering another sign-in — which would loop forever.
|
|
16255
|
+
*/
|
|
16256
|
+
const AUTH_RETRIED = new HttpContextToken(() => false);
|
|
16257
|
+
/**
|
|
16258
|
+
* Interceptor function that handles HTTP 401 errors by refreshing authentication and retrying the
|
|
16259
|
+
* original request once. For 403 errors, the error is propagated as a permanent auth failure.
|
|
16260
|
+
*
|
|
16261
|
+
* The retry happens ONLY when `signIn()` reports the user is authenticated, and AT MOST once per
|
|
16262
|
+
* request. Without these two guards a persistent 401 (credentials required, or an endpoint that
|
|
16263
|
+
* keeps rejecting even after a successful CSRF handshake) would retry endlessly.
|
|
16011
16264
|
*
|
|
16012
16265
|
* @param request - The HTTP request object.
|
|
16013
16266
|
* @param next - The HTTP handler function.
|
|
@@ -16020,14 +16273,23 @@ const errorInterceptorFn = (request, next) => {
|
|
|
16020
16273
|
}
|
|
16021
16274
|
return next(request).pipe(catchError$1((err) => {
|
|
16022
16275
|
if (err.status === 401) {
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16276
|
+
// Already retried once after a re-auth — give up to avoid an infinite loop.
|
|
16277
|
+
if (request.context.get(AUTH_RETRIED)) {
|
|
16278
|
+
error("ErrorInterceptor: 401 again after re-auth retry, giving up");
|
|
16279
|
+
return throwError(() => err);
|
|
16280
|
+
}
|
|
16281
|
+
error("ErrorInterceptor: 401 detected, attempting to refresh auth");
|
|
16282
|
+
return runInInjectionContext(injector, () => from(signIn()).pipe(switchMap$1((authenticated) => {
|
|
16283
|
+
// signIn() could not authenticate (e.g. credentials required → redirected to login,
|
|
16284
|
+
// or a provider redirect is in progress). Do NOT retry — propagate the 401 instead,
|
|
16285
|
+
// otherwise every subsequent 401 re-triggers sign-in and loops.
|
|
16286
|
+
if (!authenticated) {
|
|
16287
|
+
error("ErrorInterceptor: re-auth did not authenticate, not retrying");
|
|
16288
|
+
return throwError(() => err);
|
|
16289
|
+
}
|
|
16290
|
+
error("Auth refreshed, retrying original request once");
|
|
16291
|
+
const headers = new HttpHeaders(createHeaders());
|
|
16292
|
+
return next(request.clone({ headers, context: request.context.set(AUTH_RETRIED, true) }));
|
|
16031
16293
|
}), catchError$1((signInErr) => {
|
|
16032
16294
|
error("Failed to refresh auth, redirecting to login", signInErr);
|
|
16033
16295
|
return throwError(() => signInErr);
|
|
@@ -16110,5 +16372,5 @@ const queryNameResolver = () => {
|
|
|
16110
16372
|
* Generated bundle index. Do not edit.
|
|
16111
16373
|
*/
|
|
16112
16374
|
|
|
16113
|
-
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationDateComponent, AggregationDateRangeDialogComponent, AggregationListComponent, AggregationPanelComponent, AggregationTreeComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, ArticleEntities, ArticleExtracts, ArticleLabels, ArticleSimilarDocuments, AsideFiltersComponent, AuditFeedbackType, AuditService, AuthGuard, AuthPageComponent, AutocompleteService, BOOKMARKS_CONFIG, BOOKMARKS_OPTIONS, BackdropComponent, BackdropService, BookmarkButtonComponent, BookmarksComponent, COLLECTIONS_CONFIG, COLLECTIONS_OPTIONS, COMPONENTS_FOR_DOCUMENT_TYPE, ChangePasswordComponent, ChildMarkerDirective, CollectionsComponent, CollectionsDialog, DRAWER_COMPONENT, DRAWER_STACK_MAX_COUNT, DateComponent, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, DrawerAdvancedFiltersComponent, DrawerComponent, DrawerNavbarComponent, DrawerPreviewComponent, DrawerService, DrawerStackComponent, DrawerStackService, DropdownInputComponent, DropdownListComponent, ErrorComponent, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, HIGHLIGHTS, HighlightWordPipe, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardNavigatorDirective, LabelService, LabelsEditDialog, LoadingComponent, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUserDialogComponent, PREVIEW_CONFIG, PagerComponent, PreviewNavigator, PreviewService, PrincipalService, PrincipalStore, QueryParamsStore, QueryService, RECENT_SEARCHES_CONFIG, RECENT_SEARCHES_OPTIONS, ROUTE_COMPONENTS, RecentSearchesComponent, ResetUserSettingsDialogComponent, SAVED_SEARCHES_CONFIG, SAVED_SEARCHES_OPTIONS, SavedSearchDialog, SavedSearchesComponent, SavedSearchesService, SearchFeedbackComponent, SearchInputFooter, SearchService, SelectArticleDirective, SelectArticleOnClickDirective, SelectionHistoryService, SelectionService, SelectionStore, ShowBookmarkDirective, SidebarNavComponent, SignInComponent, SortSelectorComponent, SourceComponent, SourceIconPipe, SponsoredResultsComponent, SyslangPipe, THEMES, TextChunkService, ThemeProviderDirective, ThemeSelectorComponent, ThemeStore, ThemeToggleComponent, TranslocoDateImpurePipe, UserProfileDialog, UserProfileFormComponent, UserProfileService, UserSettingsStore, applyThemeToNativeElement, auditInterceptorFn, authInterceptorFn, bodyInterceptorFn, buildQuery, debouncedSignal, errorInterceptorFn, getCurrentPath, getCurrentQueryName, getQueryNameFromRoute, injectRouteNavigation, processCssVars, queryNameResolver, signIn, themeColorNameToCssVariable, themeColorsToCssVariables, toastInterceptorFn, withAggregationsFeatures, withAlertsFeatures, withAppFeatures, withApplicationFeatures, withAssistantFeatures, withBasketsFeatures, withBookmarkFeatures, withBootstrapApp, withExtractsFeatures, withFetch, withMultiSelectionFeatures, withPrincipalFeatures, withQueryParamsFeatures, withRecentSearchesFeatures, withSavedSearchesFeatures, withSelectionFeatures, withThemeBodyHook, withThemes, withThemesFeatures, withUserSettingsFeatures };
|
|
16375
|
+
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationDateComponent, AggregationDateRangeDialogComponent, AggregationListComponent, AggregationPanelComponent, AggregationTreeComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, ArticleEntities, ArticleExtracts, ArticleLabels, ArticleSimilarDocuments, AsideFiltersComponent, AuditFeedbackType, AuditService, AuthGuard, AuthPageComponent, AutocompleteService, BOOKMARKS_CONFIG, BOOKMARKS_OPTIONS, BackdropComponent, BackdropService, BookmarkButtonComponent, BookmarksComponent, COLLECTIONS_CONFIG, COLLECTIONS_OPTIONS, COMPONENTS_FOR_DOCUMENT_TYPE, ChangePasswordComponent, ChildMarkerDirective, CollectionsComponent, CollectionsDialog, DRAWER_COMPONENT, DRAWER_STACK_MAX_COUNT, DateComponent, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, DrawerAdvancedFiltersComponent, DrawerComponent, DrawerNavbarComponent, DrawerPreviewComponent, DrawerService, DrawerStackComponent, DrawerStackService, DropdownInputComponent, DropdownListComponent, ErrorComponent, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, HIGHLIGHTS, HighlightWordPipe, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardNavigatorDirective, LabelService, LabelsEditDialog, LoadingComponent, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUserDialogComponent, PREVIEW_CONFIG, PagerComponent, PreviewNavigator, PreviewService, PrincipalService, PrincipalStore, QueryParamsStore, QueryService, RECENT_SEARCHES_CONFIG, RECENT_SEARCHES_OPTIONS, ROUTE_COMPONENTS, RecentSearchesComponent, ResetUserSettingsDialogComponent, SAVED_SEARCHES_CONFIG, SAVED_SEARCHES_OPTIONS, SavedSearchDialog, SavedSearchesComponent, SavedSearchesService, SearchFeedbackComponent, SearchInputFooter, SearchService, SelectArticleDirective, SelectArticleOnClickDirective, SelectionHistoryService, SelectionService, SelectionStore, ShowBookmarkDirective, SidebarNavComponent, SignInComponent, SignedOutComponent, SortSelectorComponent, SourceComponent, SourceIconPipe, SponsoredResultsComponent, SyslangPipe, THEMES, TextChunkService, ThemeProviderDirective, ThemeSelectorComponent, ThemeStore, ThemeToggleComponent, TranslocoDateImpurePipe, UserProfileDialog, UserProfileFormComponent, UserProfileService, UserSettingsStore, applyThemeToNativeElement, auditInterceptorFn, authInterceptorFn, bodyInterceptorFn, bootstrapApp, buildQuery, debouncedSignal, errorInterceptorFn, getCurrentPath, getCurrentQueryName, getQueryNameFromRoute, injectRouteNavigation, processCssVars, queryNameResolver, signIn, themeColorNameToCssVariable, themeColorsToCssVariables, toastInterceptorFn, withAggregationsFeatures, withAlertsFeatures, withAppFeatures, withApplicationFeatures, withAssistantFeatures, withBasketsFeatures, withBookmarkFeatures, withBootstrapApp, withExtractsFeatures, withFetch, withMultiSelectionFeatures, withPrincipalFeatures, withQueryParamsFeatures, withRecentSearchesFeatures, withSavedSearchesFeatures, withSelectionFeatures, withThemeBodyHook, withThemes, withThemesFeatures, withUserSettingsFeatures };
|
|
16114
16376
|
//# sourceMappingURL=sinequa-atomic-angular.mjs.map
|