@sinequa/atomic-angular 1.0.15 → 1.0.16

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,14 +1,14 @@
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, ChangeDetectionStrategy, resource, ViewContainerRef, viewChildren, numberAttribute, afterRenderEffect, afterEveryRender } from '@angular/core';
3
- import { BehaviorSubject, Subscription, catchError, EMPTY, firstValueFrom, map, Subject, of, tap, throwError, filter, shareReplay, fromEvent, debounceTime, from, switchMap } from 'rxjs';
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, ChangeDetectionStrategy, resource, ViewContainerRef, viewChildren, numberAttribute, afterRenderEffect, afterEveryRender } from '@angular/core';
3
+ import { BehaviorSubject, Subscription, catchError, throwError, firstValueFrom, map, Subject, of, tap, EMPTY, filter, shareReplay, fromEvent, debounceTime, from, switchMap } from 'rxjs';
4
4
  import { TranslocoService, TranslocoPipe, provideTranslocoScope } from '@jsverse/transloco';
5
- import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, FaIconComponent, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SwitchComponent, SelectOptionDirective, DialogService, TabsComponent, TabsListComponent, TabComponent, ChevronLeftIconComponent, ChevronsLeftIconComponent, ChevronsRightIconComponent, Separator, SheetCloseDirective, SheetService, DateRangePickerDirective, DatepickerDirective, ButtonGroup, InputGroupInput, InputGroupComponent, InputGroupAddonComponent, SearchIcon, FilterIcon, LoadingCircleIconComponent, CircleCheckIconComponent, PopoverComponent, CardComponent, CardHeaderComponent, CardContentComponent, CardFooterComponent, BookmarkIcon, PopoverContentComponent, UserIcon, TrashIcon, FolderIcon, VerticalDividerComponent, BreakpointObserverService, HorizontalDividerComponent, FlagEnglishIconComponent, FlagFrenchIconComponent, EditIcon, UndoIcon, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent } from '@sinequa/ui';
5
+ import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, FaIconComponent, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, CardComponent, CardHeaderComponent, CardContentComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SwitchComponent, SelectOptionDirective, DialogService, TabsComponent, TabsListComponent, TabComponent, ChevronLeftIconComponent, ChevronsLeftIconComponent, ChevronsRightIconComponent, Separator, SheetCloseDirective, SheetService, DateRangePickerDirective, DatepickerDirective, ButtonGroup, InputGroupInput, InputGroupComponent, InputGroupAddonComponent, SearchIcon, FilterIcon, LoadingCircleIconComponent, CircleCheckIconComponent, PopoverComponent, CardFooterComponent, BookmarkIcon, PopoverContentComponent, UserIcon, TrashIcon, FolderIcon, VerticalDividerComponent, BreakpointObserverService, HorizontalDividerComponent, FlagEnglishIconComponent, FlagFrenchIconComponent, EditIcon, UndoIcon, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent } from '@sinequa/ui';
6
6
  import highlightWords from 'highlight-words';
7
7
  import { ActivatedRoute, Router, NavigationEnd, RouterLink, 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 { globalConfig, EngineType, extraColumns, sysLang, getQueryParamsFromUrl, clearSessionTokens, login, info, warn, error, setGlobalConfig, notify, addConcepts, queryParamsFromUrl, patchUserSettings, deleteUserSettings, fetchUserSettings, 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
- import { HttpClient, HttpParams, httpResource, HttpResponse, HttpHeaders, HttpContextToken } from '@angular/common/http';
10
+ import { globalConfig, EngineType, extraColumns, sysLang, 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
+ import { HttpClient, HttpParams, httpResource, HttpResponse, HttpContextToken, HttpHeaders } from '@angular/common/http';
12
12
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
13
13
  import { DatePipe, DATE_PIPE_DEFAULT_TIMEZONE, DATE_PIPE_DEFAULT_OPTIONS, Location, NgTemplateOutlet, NgStyle, NgClass, NgComponentOutlet } from '@angular/common';
14
14
  import { Title, DomSanitizer } from '@angular/platform-browser';
@@ -108,8 +108,9 @@ class AppService {
108
108
  *
109
109
  * @remarks
110
110
  * This method constructs an HTTP GET request to fetch the application configuration
111
- * using the `app` parameter from the global configuration. If the request fails,
112
- * it logs the error to the console and returns an empty observable.
111
+ * using the `app` parameter from the global configuration. If the request fails, it logs the
112
+ * error and re-throws a normalized `Error` carrying the server's `errorMessage` when available
113
+ * (e.g. "app not found: '...'"), so callers can surface the reason on the error page.
113
114
  *
114
115
  * @example
115
116
  * ```typescript
@@ -121,9 +122,19 @@ class AppService {
121
122
  getApp(appName) {
122
123
  const app = appName || globalConfig.app;
123
124
  const params = new HttpParams().set('app', app || '');
124
- return this.http.get(this.API_URL + '/app', { params }).pipe(catchError(error => {
125
- console.error('AppService.getApp failure - error: ', error);
126
- return EMPTY;
125
+ return this.http.get(this.API_URL + '/app', { params }).pipe(catchError((err) => {
126
+ console.error('AppService.getApp failure - error: ', err);
127
+ // Propagate the failure (previously swallowed with EMPTY, which hid the cause and surfaced a
128
+ // generic "no elements in sequence" downstream). Surface the Sinequa error-envelope message
129
+ // when present — e.g. a 500 with { errorMessage: "app not found: 'mint_rnd-yoyo'" } — so the
130
+ // bootstrap/login flow can route to the error page with the actual reason.
131
+ const e = err;
132
+ const message = e?.error?.errorMessage ??
133
+ e?.error?.message ??
134
+ e?.errorMessage ??
135
+ e?.message ??
136
+ 'Failed to fetch application configuration';
137
+ return throwError(() => new Error(message));
127
138
  }));
128
139
  }
129
140
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AppService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -1563,100 +1574,174 @@ function withThemes(app, themes) {
1563
1574
  }
1564
1575
 
1565
1576
  /**
1566
- * Signs in the user by checking the global configuration for authentication method and acting accordingly.
1577
+ * Signs the user in according to the resolved {@link globalConfig.authMode}.
1578
+ *
1579
+ * The mode is expected to be resolved beforehand (by `initializeAppConfig`, awaited in
1580
+ * `bootstrapApp`). This function clears any existing session, then:
1581
+ * - `credentials` → redirect to the login form;
1582
+ * - `sso` → reload the page so the browser/proxy performs the handshake;
1583
+ * - `oauth` / `saml` → delegate to `login()`, which redirects to the provider;
1584
+ * - `bearer` → delegate to `login()`;
1585
+ * - `unknown` → `login()` tries SSO silently then resolves to credentials; on failure the login
1586
+ * form is shown.
1567
1587
  *
1568
- * This function first clears any existing session tokens to ensure a clean authentication state. It then checks the
1569
- * global configuration to determine whether to use credentials-based authentication or Single Sign-On (SSO). If
1570
- * credentials are used, it redirects the user to the login page. If SSO is enabled, it reloads the page to trigger
1571
- * the SSO login process. If neither method is specified, it attempts a standard login and handles any errors that
1572
- * may occur during the process.
1573
- * @returns A promise resolving to a boolean indicating the success of the sign-in operation.
1588
+ * @returns A promise resolving to a boolean indicating whether the user is authenticated.
1574
1589
  */
1575
1590
  async function signIn() {
1576
1591
  assertInInjectionContext(signIn);
1577
1592
  const router = inject(Router);
1578
1593
  const lastUrlAfterNavigation = inject(NavigationService).urlAfterNavigation;
1579
- const { useCredentials, loginPath, useSSO } = globalConfig;
1594
+ const { loginPath, authMode } = globalConfig;
1580
1595
  // Always clear authentication tokens first to clear any existing session
1581
1596
  clearSessionTokens();
1582
- // If credentials are used and user override is not active, redirect to the login page
1583
- if (useCredentials) {
1597
+ // Credentials: show the login form.
1598
+ if (authMode?.kind === "credentials") {
1584
1599
  router.navigate([loginPath], { queryParams: { returnUrl: lastUrlAfterNavigation } });
1585
1600
  return false; // prevent further execution
1586
1601
  }
1587
- // SSO is set to true when the browser handles authentication automatically
1588
- // If SSO is used, reload the page to trigger SSO login
1589
- if (useSSO) {
1590
- // reload the page to trigger SSO login
1602
+ // SSO: the browser/proxy handles authentication reload to trigger it.
1603
+ if (authMode?.kind === "sso") {
1591
1604
  window.location.reload();
1592
1605
  return false; // prevent further execution
1593
1606
  }
1594
- // Otherwise, perform a standard login
1607
+ // oauth / saml / bearer / unknown — let login() drive the handshake.
1595
1608
  try {
1596
1609
  const response = await login();
1597
1610
  if (response) {
1598
1611
  info("Response from login", response);
1599
1612
  return true;
1600
1613
  }
1601
- else {
1602
- warn("Response from login", response);
1614
+ // Not authenticated. For provider redirects (oauth/saml) the page is already navigating away,
1615
+ // so we don't touch the router. Otherwise (unknown resolved to credentials, or bearer failed)
1616
+ // show the login form.
1617
+ if (authMode?.kind !== "oauth" && authMode?.kind !== "saml") {
1618
+ router.navigate([loginPath], { queryParams: { returnUrl: lastUrlAfterNavigation } });
1603
1619
  }
1620
+ return false;
1604
1621
  }
1605
1622
  catch (err) {
1606
1623
  error("Error during login", err);
1607
- if (err.status === 401) {
1624
+ // A 401 is recoverable: the user just needs to authenticate → show the login form.
1625
+ if (err?.status === 401) {
1608
1626
  error("Unauthorized access - please check your credentials:", err?.errorMessage);
1609
- router.navigate([loginPath]);
1627
+ router.navigate([loginPath], { queryParams: { returnUrl: lastUrlAfterNavigation } });
1628
+ return false;
1610
1629
  }
1611
- throw err;
1630
+ // Any other error (e.g. a misconfigured OAuth/SAML provider → 5xx) is fatal and not something
1631
+ // the user can resolve from the login form. Stop here: navigate straight to the error page with
1632
+ // the reason. We return false (instead of rethrowing) so the application does NOT initialize and
1633
+ // fire further authenticated API calls (e.g. usersettings → 401).
1634
+ const message = (err?.errorMessage ?? err?.message) || undefined;
1635
+ router.navigate(["/error"], { queryParams: { message } });
1636
+ return false;
1612
1637
  }
1613
- return false; // prevent further execution
1614
1638
  }
1615
1639
 
1616
1640
  /**
1617
1641
  * Bootstraps the application by ensuring the user is authenticated and initializing the application.
1618
1642
  *
1619
- * This function first attempts to sign in the user using the provided `Router`. If authentication is successful,
1620
- * it proceeds to initialize the application and create routes using the `applicationService`. Any errors during
1621
- * authentication or initialization are logged to the console, but the returned promise always resolves.
1643
+ * This function first attempts to sign in the user via `signIn()`. If authentication is successful,
1644
+ * it initializes the application (stores and, optionally, dynamic routes) through `ApplicationService`
1645
+ * and waits for the initialization to complete before resolving. Any errors during authentication or
1646
+ * initialization are logged to the console, but the returned promise never rejects.
1647
+ *
1648
+ * Note: this function relies on Angular's injection context, so it must be called within one
1649
+ * (e.g. from `provideAppInitializer`).
1622
1650
  *
1623
- * @param Router - The application's router instance used for navigation and authentication.
1624
- * @param applicationService - The service responsible for initializing the application and creating routes.
1625
- * @returns A promise that resolves when the application is ready to be initialized, regardless of success or failure.
1651
+ * @param options - Configuration options for the bootstrap process.
1652
+ * @param options.createRoutes - Whether to create routes during initialization. Defaults to `true`.
1653
+ * @returns A promise that resolves to `true` when the application has been fully initialized,
1654
+ * or `false` when the user is not authenticated or an error occurred.
1626
1655
  */
1627
- async function withBootstrapApp(applicationService, { createRoutes = true }) {
1628
- // This function is used to test if the application is ready to be initialized
1629
- // set the createRoutes flag in the global config
1656
+ async function bootstrapApp({ createRoutes = true } = {}) {
1657
+ assertInInjectionContext(bootstrapApp);
1658
+ // Capture the injector synchronously: Angular's injection context does not survive across an
1659
+ // `await`, so after awaiting `initializeAppConfig()` we must re-enter it to inject services and
1660
+ // run `signIn()` (which use `inject()` internally).
1661
+ const injector = inject(EnvironmentInjector);
1662
+ // set the createRoutes flag early so it is defined even when sign-in fails
1663
+ // (ApplicationService.initialize() sets it again when it runs)
1630
1664
  setGlobalConfig({ createRoutes });
1631
- return new Promise(resolve => {
1632
- // Check if the user is authenticated
1633
- signIn()
1634
- .then(async (response) => {
1635
- if (response) {
1636
- info('User authenticated, initializing application...');
1637
- // Initialize the application.
1638
- // Awaited so the APP_INITIALIZER does not resolve (and bootstrap does not
1639
- // complete) until initialization is done. Otherwise routed components render
1640
- // and set their page title before `initialize()` runs `setGeneralApp()`, which
1641
- // would then overwrite the page title with the bare application name.
1642
- try {
1643
- await applicationService.initialize(createRoutes);
1644
- info(`Application initialized with routes: ${createRoutes} successfully.`);
1645
- }
1646
- catch (err) {
1647
- error(`Error initializing application with routes: ${createRoutes}:`, err);
1648
- }
1649
- }
1650
- else {
1651
- info('User not authenticated, skipping application initialization.');
1652
- }
1653
- ;
1654
- })
1655
- .catch(err => {
1656
- error('Error while signing in:', err);
1657
- })
1658
- .finally(() => resolve());
1659
- });
1665
+ // Resolve the authentication mode (queries the server pre-login when needed) BEFORE signing in.
1666
+ // Doing it here rather than as a separate, concurrent APP_INITIALIZER — guarantees that
1667
+ // signIn() reads a fully resolved `authMode` and removes the long-standing bootstrap race.
1668
+ // Crucially, this also sets `backendUrl` BEFORE any service/store is constructed, so those that
1669
+ // derive their API URL from `globalConfig.backendUrl` (e.g. PrincipalStore) don't capture
1670
+ // `undefined` (which produced requests to `/undefined/api/v1/...`).
1671
+ try {
1672
+ await initializeAppConfig();
1673
+ }
1674
+ catch (err) {
1675
+ error('Error while initializing application config:', err);
1676
+ // Pre-login failed (e.g. a 500 "app not found: 'mint_rnd-yoyo'" for a bad app name). This is
1677
+ // fatal — backendUrl/authMode can't be resolved — so route to the error page with the reason
1678
+ // instead of continuing to signIn() (which would otherwise fail with a less meaningful error).
1679
+ const message = err?.errorMessage
1680
+ ?? err?.message;
1681
+ runInInjectionContext(injector, () => inject(Router).navigate(['/error'], { queryParams: { message } }));
1682
+ return false;
1683
+ }
1684
+ // Inject ApplicationService AFTER initializeAppConfig (re-entering the injection context lost
1685
+ // across the await), so its dependent services/stores are constructed with `backendUrl` set.
1686
+ const applicationService = runInInjectionContext(injector, () => inject(ApplicationService));
1687
+ let authenticated = false;
1688
+ try {
1689
+ // Re-enter the injection context lost across the await above.
1690
+ authenticated = await runInInjectionContext(injector, () => signIn());
1691
+ }
1692
+ catch (err) {
1693
+ error('Error while signing in:', err);
1694
+ return false;
1695
+ }
1696
+ if (!authenticated) {
1697
+ info('User not authenticated, skipping application initialization.');
1698
+ return false;
1699
+ }
1700
+ info('User authenticated, initializing application...');
1701
+ try {
1702
+ await applicationService.initialize(createRoutes);
1703
+ info(`Application initialized successfully (createRoutes: ${createRoutes}).`);
1704
+ return true;
1705
+ }
1706
+ catch (err) {
1707
+ error(`Error initializing application (createRoutes: ${createRoutes}):`, err);
1708
+ // Authenticated, but the application failed to initialize (e.g. fetchApp/usersettings failed).
1709
+ // Route to the error page with the failure reason instead of leaving a half-initialized app.
1710
+ const message = err?.errorMessage
1711
+ ?? err?.message;
1712
+ runInInjectionContext(injector, () => inject(Router).navigate(['/error'], { queryParams: { message } }));
1713
+ return false;
1714
+ }
1715
+ }
1716
+ /**
1717
+ * Bootstraps the application by ensuring the user is authenticated and initializing the application.
1718
+ *
1719
+ * @deprecated Use {@link bootstrapApp} instead, and let it inject `ApplicationService` itself.
1720
+ *
1721
+ * Migration — in your `app.config.ts`, replace:
1722
+ * ```ts
1723
+ * // ❌ Deprecated: eagerly injecting ApplicationService in the factory constructs it (and its
1724
+ * // dependent stores/services) BEFORE bootstrapApp resolves the config, so services that build
1725
+ * // their API URL from `globalConfig.backendUrl` capture `undefined` (→ `/undefined/api/v1/...`).
1726
+ * provideAppInitializer(() => withBootstrapApp(inject(ApplicationService), { createRoutes: true })),
1727
+ * ```
1728
+ * with:
1729
+ * ```ts
1730
+ * // ✅ bootstrapApp injects ApplicationService internally, AFTER initializeAppConfig() has set
1731
+ * // `backendUrl` and resolved the auth mode.
1732
+ * provideAppInitializer(() => bootstrapApp({ createRoutes: true })),
1733
+ * ```
1734
+ * (Remove the now-unused `inject` / `ApplicationService` imports.)
1735
+ *
1736
+ * @param applicationService - Ignored; kept for backward compatibility. `ApplicationService` is
1737
+ * provided in root and injected internally by {@link bootstrapApp}, so the instance is the same.
1738
+ * Passing `inject(ApplicationService)` here is discouraged — see the migration note above.
1739
+ * @param options - Configuration options for the bootstrap process.
1740
+ * @param options.createRoutes - Whether to create routes during initialization. Defaults to `true`.
1741
+ * @returns A promise that resolves when the bootstrap process is complete, regardless of success or failure.
1742
+ */
1743
+ async function withBootstrapApp(_applicationService, { createRoutes = true } = {}) {
1744
+ await bootstrapApp({ createRoutes });
1660
1745
  }
1661
1746
 
1662
1747
  /**
@@ -3415,12 +3500,13 @@ function AuthGuard() {
3415
3500
  return async (_, state) => {
3416
3501
  const router = inject(Router);
3417
3502
  const principalStore = inject(PrincipalStore);
3418
- const { loginPath, useCredentials, useSSO } = globalConfig;
3503
+ const { loginPath, authMode } = globalConfig;
3419
3504
  if (state.url.startsWith("/login"))
3420
3505
  return true;
3421
3506
  // If the user is not authenticated, navigate to the login page.
3422
3507
  // The login page handles every authentication method (credentials, OAuth, SAML).
3423
- if (!isAuthenticated() && !useSSO) {
3508
+ // In SSO mode the browser/proxy carries the auth, so we let it through.
3509
+ if (!isAuthenticated() && authMode?.kind !== "sso") {
3424
3510
  router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
3425
3511
  return false;
3426
3512
  }
@@ -3446,7 +3532,7 @@ function AuthGuard() {
3446
3532
  // check if the password is expired and if the partition is editable
3447
3533
  // changing password is only possible when user use credentials to auhtenticate
3448
3534
  // only in credentials mode
3449
- if (useCredentials) {
3535
+ if (authMode?.kind === "credentials") {
3450
3536
  const exp = passwordExpirationDate;
3451
3537
  const editable = !!editablePartition;
3452
3538
  if (editable && exp && isExpired(exp)) {
@@ -5271,101 +5357,160 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
5271
5357
  }], 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 }] }] } });
5272
5358
 
5273
5359
  class ErrorComponent {
5360
+ route = inject(ActivatedRoute);
5274
5361
  router = inject(Router);
5275
- reload() {
5362
+ /**
5363
+ * Human-readable error detail shown on the page, taken from the `message` query param.
5364
+ * Callers navigating here can pass it, e.g. on an auth failure:
5365
+ * `router.navigate(["error"], { queryParams: { message: err.message } })`.
5366
+ */
5367
+ message = (() => {
5368
+ const value = this.route.snapshot.queryParams['message'];
5369
+ return typeof value === 'string' && value.trim().length ? value : undefined;
5370
+ })();
5371
+ /** Navigate home and re-bootstrap the application (fresh authentication attempt). */
5372
+ goHome() {
5276
5373
  this.router.navigate(['/']).then(() => window.location.reload());
5277
5374
  }
5278
5375
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5279
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ErrorComponent, isStandalone: true, selector: "error-component, ErrorComponent", ngImport: i0, template: `
5280
- <div class="bg-background text-foreground flex min-h-screen flex-col items-center justify-center">
5281
- <svg
5282
- class="mb-8 h-20 w-20 text-red-600"
5283
- xmlns="http://www.w3.org/2000/svg"
5284
- width="24"
5285
- height="24"
5286
- viewBox="0 0 24 24"
5287
- fill="none"
5288
- stroke="currentColor"
5289
- stroke-width="2"
5290
- stroke-linecap="round"
5291
- stroke-linejoin="round">
5292
- <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" />
5293
- <path d="M12 9v4" />
5294
- <path d="M12 17h.01" />
5295
- </svg>
5296
- <h1 class="mb-4 text-4xl font-bold">Oops! Something went wrong</h1>
5297
- <p class="text-muted-foreground mb-8 text-xl">We apologize for the inconvenience.</p>
5298
- <div class="flex space-x-4">
5299
- <button
5300
- (click)="reload()"
5301
- class="btn btn-outline rounded-lg border-neutral-300 text-neutral-700 transition hover:bg-neutral-200 hover:text-neutral-700">
5376
+ 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: `
5377
+ <Card
5378
+ hover="no"
5379
+ class="bg-background border-(--popover-border) w-full max-w-md rounded-3xl border shadow-2xl">
5380
+ <CardHeader class="flex flex-col items-center gap-4 pt-8 text-center">
5381
+ <span
5382
+ class="bg-destructive/10 text-destructive inline-flex h-16 w-16 items-center justify-center rounded-full">
5302
5383
  <svg
5303
- class="mr-2 h-4 w-4"
5304
5384
  xmlns="http://www.w3.org/2000/svg"
5305
- width="24"
5306
- height="24"
5385
+ class="h-8 w-8"
5307
5386
  viewBox="0 0 24 24"
5308
5387
  fill="none"
5309
5388
  stroke="currentColor"
5310
5389
  stroke-width="2"
5311
5390
  stroke-linecap="round"
5312
- stroke-linejoin="round">
5313
- <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
5314
- <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" />
5391
+ stroke-linejoin="round"
5392
+ aria-hidden="true">
5393
+ <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" />
5394
+ <path d="M12 9v4" />
5395
+ <path d="M12 17h.01" />
5315
5396
  </svg>
5316
- Go to Homepage
5317
- </button>
5318
- </div>
5319
- </div>
5320
- `, isInline: true });
5397
+ </span>
5398
+
5399
+ <div class="grid gap-1.5">
5400
+ <h1 class="text-2xl font-semibold tracking-tight">Something went wrong</h1>
5401
+ <p class="text-muted-foreground text-sm">
5402
+ We're sorry — the application ran into a problem and couldn't continue.
5403
+ </p>
5404
+ </div>
5405
+ </CardHeader>
5406
+
5407
+ <CardContent class="grid gap-5 pb-8">
5408
+ @if (message) {
5409
+ <div class="border-border bg-muted/40 grid gap-1 rounded-lg border p-3 text-left">
5410
+ <span class="text-muted-foreground text-[0.7rem] font-medium uppercase tracking-wider">
5411
+ Details
5412
+ </span>
5413
+ <p class="text-foreground text-sm break-words" role="alert">{{ message }}</p>
5414
+ </div>
5415
+ }
5416
+
5417
+ <div class="flex justify-center">
5418
+ <button variant="primary" (click)="goHome()">
5419
+ <svg
5420
+ xmlns="http://www.w3.org/2000/svg"
5421
+ class="mr-2 h-4 w-4"
5422
+ viewBox="0 0 24 24"
5423
+ fill="none"
5424
+ stroke="currentColor"
5425
+ stroke-width="2"
5426
+ stroke-linecap="round"
5427
+ stroke-linejoin="round"
5428
+ aria-hidden="true">
5429
+ <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
5430
+ <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" />
5431
+ </svg>
5432
+ Go to homepage
5433
+ </button>
5434
+ </div>
5435
+ </CardContent>
5436
+ </Card>
5437
+ `, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size"] }, { 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"] }] });
5321
5438
  }
5322
5439
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ErrorComponent, decorators: [{
5323
5440
  type: Component,
5324
5441
  args: [{
5325
5442
  selector: 'error-component, ErrorComponent',
5326
5443
  standalone: true,
5327
- imports: [],
5444
+ imports: [
5445
+ ButtonComponent,
5446
+ CardComponent,
5447
+ CardHeaderComponent,
5448
+ CardContentComponent
5449
+ ],
5450
+ host: {
5451
+ class: 'bg-background text-foreground grid min-h-dvh w-full place-content-center p-6'
5452
+ },
5328
5453
  template: `
5329
- <div class="bg-background text-foreground flex min-h-screen flex-col items-center justify-center">
5330
- <svg
5331
- class="mb-8 h-20 w-20 text-red-600"
5332
- xmlns="http://www.w3.org/2000/svg"
5333
- width="24"
5334
- height="24"
5335
- viewBox="0 0 24 24"
5336
- fill="none"
5337
- stroke="currentColor"
5338
- stroke-width="2"
5339
- stroke-linecap="round"
5340
- stroke-linejoin="round">
5341
- <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" />
5342
- <path d="M12 9v4" />
5343
- <path d="M12 17h.01" />
5344
- </svg>
5345
- <h1 class="mb-4 text-4xl font-bold">Oops! Something went wrong</h1>
5346
- <p class="text-muted-foreground mb-8 text-xl">We apologize for the inconvenience.</p>
5347
- <div class="flex space-x-4">
5348
- <button
5349
- (click)="reload()"
5350
- class="btn btn-outline rounded-lg border-neutral-300 text-neutral-700 transition hover:bg-neutral-200 hover:text-neutral-700">
5454
+ <Card
5455
+ hover="no"
5456
+ class="bg-background border-(--popover-border) w-full max-w-md rounded-3xl border shadow-2xl">
5457
+ <CardHeader class="flex flex-col items-center gap-4 pt-8 text-center">
5458
+ <span
5459
+ class="bg-destructive/10 text-destructive inline-flex h-16 w-16 items-center justify-center rounded-full">
5351
5460
  <svg
5352
- class="mr-2 h-4 w-4"
5353
5461
  xmlns="http://www.w3.org/2000/svg"
5354
- width="24"
5355
- height="24"
5462
+ class="h-8 w-8"
5356
5463
  viewBox="0 0 24 24"
5357
5464
  fill="none"
5358
5465
  stroke="currentColor"
5359
5466
  stroke-width="2"
5360
5467
  stroke-linecap="round"
5361
- stroke-linejoin="round">
5362
- <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
5363
- <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" />
5468
+ stroke-linejoin="round"
5469
+ aria-hidden="true">
5470
+ <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" />
5471
+ <path d="M12 9v4" />
5472
+ <path d="M12 17h.01" />
5364
5473
  </svg>
5365
- Go to Homepage
5366
- </button>
5367
- </div>
5368
- </div>
5474
+ </span>
5475
+
5476
+ <div class="grid gap-1.5">
5477
+ <h1 class="text-2xl font-semibold tracking-tight">Something went wrong</h1>
5478
+ <p class="text-muted-foreground text-sm">
5479
+ We're sorry — the application ran into a problem and couldn't continue.
5480
+ </p>
5481
+ </div>
5482
+ </CardHeader>
5483
+
5484
+ <CardContent class="grid gap-5 pb-8">
5485
+ @if (message) {
5486
+ <div class="border-border bg-muted/40 grid gap-1 rounded-lg border p-3 text-left">
5487
+ <span class="text-muted-foreground text-[0.7rem] font-medium uppercase tracking-wider">
5488
+ Details
5489
+ </span>
5490
+ <p class="text-foreground text-sm break-words" role="alert">{{ message }}</p>
5491
+ </div>
5492
+ }
5493
+
5494
+ <div class="flex justify-center">
5495
+ <button variant="primary" (click)="goHome()">
5496
+ <svg
5497
+ xmlns="http://www.w3.org/2000/svg"
5498
+ class="mr-2 h-4 w-4"
5499
+ viewBox="0 0 24 24"
5500
+ fill="none"
5501
+ stroke="currentColor"
5502
+ stroke-width="2"
5503
+ stroke-linecap="round"
5504
+ stroke-linejoin="round"
5505
+ aria-hidden="true">
5506
+ <path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
5507
+ <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" />
5508
+ </svg>
5509
+ Go to homepage
5510
+ </button>
5511
+ </div>
5512
+ </CardContent>
5513
+ </Card>
5369
5514
  `
5370
5515
  }]
5371
5516
  }] });
@@ -10800,13 +10945,18 @@ class SignInComponent {
10800
10945
  config = globalConfig;
10801
10946
  /**
10802
10947
  * True when authentication is handled outside the credentials form — i.e. by the
10803
- * browser/proxy (`useSSO`) or by an auto-configured OAuth/SAML provider. In those
10804
- * modes this screen shows a loader instead of a login form and initiates the
10805
- * handshake automatically by calling `handleLogin()`.
10948
+ * browser/proxy (`sso`) or by an auto-configured OAuth/SAML provider. In those modes
10949
+ * this screen shows a loader instead of a login form and initiates the handshake
10950
+ * automatically by calling `handleLogin()`.
10951
+ *
10952
+ * Note: the ambiguous `unknown` mode is intentionally excluded — it is resolved upstream
10953
+ * (in `login()`/`signIn()`) to either `sso` or `credentials` before this screen renders,
10954
+ * so reaching here in `unknown` should still show the form, never a dead-end loader.
10806
10955
  */
10807
- externalAuth = !!(globalConfig.useSSO ||
10808
- globalConfig.autoOAuthProvider ||
10809
- globalConfig.autoSAMLProvider);
10956
+ externalAuth = (() => {
10957
+ const kind = globalConfig.authMode?.kind;
10958
+ return kind === "sso" || kind === "oauth" || kind === "saml";
10959
+ })();
10810
10960
  class = input(...(ngDevMode ? [undefined, { debugName: "class" }] : []));
10811
10961
  forgotPassword = output();
10812
10962
  username = model("", ...(ngDevMode ? [{ debugName: "username" }] : []));
@@ -10902,10 +11052,13 @@ class SignInComponent {
10902
11052
  this.auditService.notifyLogin();
10903
11053
  }
10904
11054
  return result;
10905
- }).catch(error => {
10906
- warn("An error occurred while logging in", error);
11055
+ }).catch((err) => {
11056
+ warn("An error occurred while logging in", err);
10907
11057
  this.auditService.notify({ type: 'Login_Denied' });
10908
- this.router.navigate(["error"]);
11058
+ // Surface the failure reason on the error page (e.g. "OAuth provider not found: identity-dev")
11059
+ // so the user/admin knows what to fix, instead of a bare generic error screen.
11060
+ const message = (err?.errorMessage ?? err?.message) || undefined;
11061
+ this.router.navigate(["error"], { queryParams: { message } });
10909
11062
  return false;
10910
11063
  });
10911
11064
  }
@@ -10920,7 +11073,16 @@ class SignInComponent {
10920
11073
  }
10921
11074
  this.auditService.notifyLogin();
10922
11075
  const { createRoutes = false } = globalConfig;
10923
- await this.applicationService.initialize(createRoutes);
11076
+ try {
11077
+ await this.applicationService.initialize(createRoutes);
11078
+ }
11079
+ catch (initErr) {
11080
+ // Authenticated, but the application failed to initialize (e.g. fetchApp failed). Surface the
11081
+ // reason on the error page rather than leaving the user stuck on the login form.
11082
+ const { errorMessage, message } = (initErr ?? {});
11083
+ this.router.navigate(["/error"], { queryParams: { message: errorMessage ?? message } });
11084
+ return;
11085
+ }
10924
11086
  this.checkPasswordExpiresSoon();
10925
11087
  const url = this.route.snapshot.queryParams["returnUrl"] || "/";
10926
11088
  this.router.navigateByUrl(url);
@@ -11466,49 +11628,29 @@ class OverrideUserDialogComponent {
11466
11628
  }
11467
11629
  }
11468
11630
  handleOverrideUser(username, domain) {
11469
- const { useSSO, createRoutes } = globalConfig;
11631
+ const { createRoutes } = globalConfig;
11470
11632
  if (username === undefined || domain === undefined) {
11471
11633
  setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
11472
11634
  }
11473
11635
  else {
11474
11636
  setGlobalConfig({ userOverrideActive: true, userOverride: { username, domain } });
11475
11637
  }
11476
- // Login with the new user
11477
- if (useSSO) {
11478
- this.appService
11479
- .initialize(createRoutes)
11480
- .then(() => {
11481
- const fullName = this.principalStore.fullName();
11482
- notify.success(`Welcome back ${fullName}!`, { duration: 2000 });
11483
- })
11484
- .catch((err) => {
11485
- error("An error occured while overriding (SSO - initialize)", err);
11486
- notify.error("An error occurred while overriding (SSO - initialize)", { duration: 2000 });
11487
- setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
11488
- });
11489
- }
11490
- else {
11491
- login()
11492
- .then((value) => {
11493
- if (value) {
11494
- this.appService
11495
- .initialize(createRoutes)
11496
- .then(() => {
11497
- const fullName = this.principalStore.fullName();
11498
- notify.success(`Welcome back ${fullName}!`, { duration: 2000 });
11499
- })
11500
- .catch((err) => {
11501
- error("An error occured while overriding (initialize)", err);
11502
- setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
11503
- });
11504
- }
11505
- })
11506
- .catch((err) => {
11507
- error("An error occured while overriding (login)", err);
11508
- notify.error("An error occured while overriding (login)", { duration: 2000 });
11509
- setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
11510
- });
11511
- }
11638
+ // Impersonation is header-driven: `createHeaders` adds `sinequa-override-user`/`-domain` to every
11639
+ // request while `userOverrideActive`, on top of the current (admin) session. We therefore do NOT
11640
+ // need to re-authenticate — re-initializing the stores refetches the principal/usersettings as the
11641
+ // overridden user. This works in every auth mode, including `credentials` where `login()` (without
11642
+ // credentials) is intentionally a no-op in atomic 2.0 and would otherwise skip initialization.
11643
+ this.appService
11644
+ .initialize(createRoutes)
11645
+ .then(() => {
11646
+ const fullName = this.principalStore.fullName();
11647
+ notify.success(`Welcome back ${fullName}!`, { duration: 2000 });
11648
+ })
11649
+ .catch((err) => {
11650
+ error("An error occurred while overriding (initialize)", err);
11651
+ notify.error("An error occurred while overriding (initialize)", { duration: 2000 });
11652
+ setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
11653
+ });
11512
11654
  }
11513
11655
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: OverrideUserDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11514
11656
  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: `
@@ -16242,8 +16384,18 @@ const bodyInterceptorFn = (request, next) => {
16242
16384
  };
16243
16385
 
16244
16386
  /**
16245
- * Interceptor function that handles HTTP 401 errors by refreshing authentication
16246
- * and retrying the original request. For 403 errors, logs out the user.
16387
+ * Marks a request that has already been retried once after a re-authentication attempt.
16388
+ * Carried on the request's HttpContext (not sent to the server) so a second 401 on the retry
16389
+ * propagates instead of triggering another sign-in — which would loop forever.
16390
+ */
16391
+ const AUTH_RETRIED = new HttpContextToken(() => false);
16392
+ /**
16393
+ * Interceptor function that handles HTTP 401 errors by refreshing authentication and retrying the
16394
+ * original request once. For 403 errors, the error is propagated as a permanent auth failure.
16395
+ *
16396
+ * The retry happens ONLY when `signIn()` reports the user is authenticated, and AT MOST once per
16397
+ * request. Without these two guards a persistent 401 (credentials required, or an endpoint that
16398
+ * keeps rejecting even after a successful CSRF handshake) would retry endlessly.
16247
16399
  *
16248
16400
  * @param request - The HTTP request object.
16249
16401
  * @param next - The HTTP handler function.
@@ -16256,14 +16408,23 @@ const errorInterceptorFn = (request, next) => {
16256
16408
  }
16257
16409
  return next(request).pipe(catchError$1((err) => {
16258
16410
  if (err.status === 401) {
16259
- error("ErrorInterceptor: 401 error detected, attempting to refresh auth and retry");
16260
- // Handle 401 by refreshing auth and retrying the request
16261
- return runInInjectionContext(injector, () => from(signIn()).pipe(switchMap$1(() => {
16262
- error("Auth refreshed, retrying original request");
16263
- // Retry the original request with cloned copy
16264
- const headersObj = createHeaders();
16265
- const headers = new HttpHeaders(headersObj);
16266
- return next(request.clone({ headers }));
16411
+ // Already retried once after a re-auth — give up to avoid an infinite loop.
16412
+ if (request.context.get(AUTH_RETRIED)) {
16413
+ error("ErrorInterceptor: 401 again after re-auth retry, giving up");
16414
+ return throwError(() => err);
16415
+ }
16416
+ error("ErrorInterceptor: 401 detected, attempting to refresh auth");
16417
+ return runInInjectionContext(injector, () => from(signIn()).pipe(switchMap$1((authenticated) => {
16418
+ // signIn() could not authenticate (e.g. credentials required → redirected to login,
16419
+ // or a provider redirect is in progress). Do NOT retry — propagate the 401 instead,
16420
+ // otherwise every subsequent 401 re-triggers sign-in and loops.
16421
+ if (!authenticated) {
16422
+ error("ErrorInterceptor: re-auth did not authenticate, not retrying");
16423
+ return throwError(() => err);
16424
+ }
16425
+ error("Auth refreshed, retrying original request once");
16426
+ const headers = new HttpHeaders(createHeaders());
16427
+ return next(request.clone({ headers, context: request.context.set(AUTH_RETRIED, true) }));
16267
16428
  }), catchError$1((signInErr) => {
16268
16429
  error("Failed to refresh auth, redirecting to login", signInErr);
16269
16430
  return throwError(() => signInErr);
@@ -16346,5 +16507,5 @@ const queryNameResolver = () => {
16346
16507
  * Generated bundle index. Do not edit.
16347
16508
  */
16348
16509
 
16349
- export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationDateComponent, AggregationDateRangeDialogComponent, AggregationListComponent, 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, 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, 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 };
16510
+ export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationDateComponent, AggregationDateRangeDialogComponent, AggregationListComponent, 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, SignInComponent, 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, 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 };
16350
16511
  //# sourceMappingURL=sinequa-atomic-angular.mjs.map