@acorex-platform/framework-client 0.0.18 → 0.0.21

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.
Files changed (108) hide show
  1. package/fesm2022/acorex-platform-framework-client-auth.mjs +872 -314
  2. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-framework-client-common-common-settings.provider-Euotv9l0.mjs → acorex-platform-framework-client-common-common-settings.provider-CLm2BVk-.mjs} +88 -5
  4. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CLm2BVk-.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-framework-client-common.mjs +2666 -693
  6. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-framework-client-core.mjs +685 -721
  8. package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-framework-client-domain.mjs +16 -16
  10. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +124 -72
  11. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -1
  12. package/fesm2022/{acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CjnQ9hAJ.mjs → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-oE8CeLE8.mjs} +8 -6
  13. package/fesm2022/{acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CjnQ9hAJ.mjs.map → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-oE8CeLE8.mjs.map} +1 -1
  14. package/fesm2022/{acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-fFen8w8M.mjs → acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs} +4 -4
  15. package/fesm2022/{acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-fFen8w8M.mjs.map → acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs.map} +1 -1
  16. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +2988 -857
  17. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
  18. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +277 -185
  19. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -1
  20. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BXOMs6oD.mjs +29184 -0
  21. package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-BXOMs6oD.mjs.map +1 -0
  22. package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-BzUM-JMC.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-DCK-qb-F.mjs} +34 -20
  23. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-DCK-qb-F.mjs.map +1 -0
  24. package/fesm2022/acorex-platform-framework-client-layout-entity-bulk-entity.command-Cs3agnpF.mjs +162 -0
  25. package/fesm2022/acorex-platform-framework-client-layout-entity-bulk-entity.command-Cs3agnpF.mjs.map +1 -0
  26. package/fesm2022/{acorex-platform-framework-client-layout-entity-file-list-popup.component-Ci_CTFtq.mjs → acorex-platform-framework-client-layout-entity-file-list-popup.component-p0hxDb0W.mjs} +31 -19
  27. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-p0hxDb0W.mjs.map +1 -0
  28. package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-DmiB__JR.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-DYV9YPyw.mjs} +30 -18
  29. package/fesm2022/acorex-platform-framework-client-layout-entity-gallery-page.component-DYV9YPyw.mjs.map +1 -0
  30. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +2 -27090
  31. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs.map +1 -1
  32. package/fesm2022/acorex-platform-framework-client-layout-views.mjs +382 -154
  33. package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -1
  34. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +646 -280
  35. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
  36. package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CIwpAXbL.mjs → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BiWerYcu.mjs} +16 -10
  37. package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CIwpAXbL.mjs.map → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BiWerYcu.mjs.map} +1 -1
  38. package/fesm2022/{acorex-platform-framework-client-layout-widgets-image-preview.popup-CazpERbX.mjs → acorex-platform-framework-client-layout-widgets-image-preview.popup-BOrrMm3U.mjs} +9 -8
  39. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-BOrrMm3U.mjs.map +1 -0
  40. package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-DKkJi40s.mjs → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs} +20 -12
  41. package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-DKkJi40s.mjs.map → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs.map} +1 -1
  42. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DtYZfA79.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-3CO4xJB3.mjs} +32 -18
  43. package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DtYZfA79.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-3CO4xJB3.mjs.map} +1 -1
  44. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-nMUtxAyy.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-CRST84ou.mjs} +8 -7
  45. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-nMUtxAyy.mjs.map → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-CRST84ou.mjs.map} +1 -1
  46. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-DvUSZ4hJ.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-FjtSRxIX.mjs} +4 -4
  47. package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-DvUSZ4hJ.mjs.map → acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-FjtSRxIX.mjs.map} +1 -1
  48. package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BihIfkZW.mjs → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs} +10 -8
  49. package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-BihIfkZW.mjs.map → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs.map} +1 -1
  50. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +4868 -2548
  51. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -1
  52. package/fesm2022/acorex-platform-framework-client-native.mjs +7 -7
  53. package/fesm2022/acorex-platform-framework-client-realtime-set-presence-availability.command-CetlUjnM.mjs +29 -0
  54. package/fesm2022/acorex-platform-framework-client-realtime-set-presence-availability.command-CetlUjnM.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-framework-client-realtime.mjs +463 -26
  56. package/fesm2022/acorex-platform-framework-client-realtime.mjs.map +1 -1
  57. package/fesm2022/acorex-platform-framework-client-runtime.mjs +300 -137
  58. package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -1
  59. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-create-view.component-QIFX11dh.mjs → acorex-platform-framework-client-themes-default-entity-master-create-view.component-yeftgJ_K.mjs} +10 -7
  60. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-create-view.component-QIFX11dh.mjs.map → acorex-platform-framework-client-themes-default-entity-master-create-view.component-yeftgJ_K.mjs.map} +1 -1
  61. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BBdellyL.mjs → acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DWE3OWGm.mjs} +10 -7
  62. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BBdellyL.mjs.map → acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DWE3OWGm.mjs.map} +1 -1
  63. package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-single-view.component-CIf1VWNW.mjs → acorex-platform-framework-client-themes-default-entity-master-single-view.component-C2Abf7Lb.mjs} +21 -19
  64. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-C2Abf7Lb.mjs.map +1 -0
  65. package/fesm2022/{acorex-platform-framework-client-themes-default-error-401.component-C_czT_JC.mjs → acorex-platform-framework-client-themes-default-error-401.component-BrTotLx1.mjs} +5 -5
  66. package/fesm2022/{acorex-platform-framework-client-themes-default-error-401.component-C_czT_JC.mjs.map → acorex-platform-framework-client-themes-default-error-401.component-BrTotLx1.mjs.map} +1 -1
  67. package/fesm2022/{acorex-platform-framework-client-themes-default-error-404.component-C2Wu0YtF.mjs → acorex-platform-framework-client-themes-default-error-404.component-DsC7_s59.mjs} +5 -5
  68. package/fesm2022/{acorex-platform-framework-client-themes-default-error-404.component-C2Wu0YtF.mjs.map → acorex-platform-framework-client-themes-default-error-404.component-DsC7_s59.mjs.map} +1 -1
  69. package/fesm2022/{acorex-platform-framework-client-themes-default-error-offline.component-DRz-kVsT.mjs → acorex-platform-framework-client-themes-default-error-offline.component-CngiQycG.mjs} +6 -6
  70. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-CngiQycG.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +421 -265
  72. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
  73. package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-column.component-Ci_oH7NE.mjs → acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CbZALjB5.mjs} +8 -6
  74. package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-column.component-Ci_oH7NE.mjs.map → acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CbZALjB5.mjs.map} +1 -1
  75. package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-view.component-Lb1G2Xz7.mjs → acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BRrNT6x2.mjs} +8 -6
  76. package/fesm2022/{acorex-platform-framework-client-themes-shared-icon-chooser-view.component-Lb1G2Xz7.mjs.map → acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BRrNT6x2.mjs.map} +1 -1
  77. package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-D4ubvFYf.mjs → acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BIsQTr6B.mjs} +4 -4
  78. package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-D4ubvFYf.mjs.map → acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BIsQTr6B.mjs.map} +1 -1
  79. package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-Br8j_BF_.mjs → acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-ZclmSbt9.mjs} +16 -10
  80. package/fesm2022/{acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-Br8j_BF_.mjs.map → acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-ZclmSbt9.mjs.map} +1 -1
  81. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +538 -133
  82. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -1
  83. package/fesm2022/acorex-platform-framework-client-workflow.mjs +26 -66
  84. package/fesm2022/acorex-platform-framework-client-workflow.mjs.map +1 -1
  85. package/package.json +10 -10
  86. package/realtime/README.md +16 -0
  87. package/runtime/README.md +14 -0
  88. package/types/acorex-platform-framework-client-auth.d.ts +291 -52
  89. package/types/acorex-platform-framework-client-common.d.ts +613 -87
  90. package/types/acorex-platform-framework-client-core.d.ts +213 -206
  91. package/types/acorex-platform-framework-client-layout-builder.d.ts +14 -8
  92. package/types/acorex-platform-framework-client-layout-components.d.ts +392 -28
  93. package/types/acorex-platform-framework-client-layout-entity.d.ts +667 -417
  94. package/types/acorex-platform-framework-client-layout-views.d.ts +31 -11
  95. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +66 -17
  96. package/types/acorex-platform-framework-client-layout-widgets.d.ts +294 -67
  97. package/types/acorex-platform-framework-client-realtime.d.ts +183 -6
  98. package/types/acorex-platform-framework-client-runtime.d.ts +134 -28
  99. package/types/acorex-platform-framework-client-themes-default.d.ts +9 -1
  100. package/types/acorex-platform-framework-client-themes-shared.d.ts +8 -2
  101. package/types/acorex-platform-framework-client-workflow.d.ts +11 -35
  102. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-Euotv9l0.mjs.map +0 -1
  103. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-BzUM-JMC.mjs.map +0 -1
  104. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-Ci_CTFtq.mjs.map +0 -1
  105. package/fesm2022/acorex-platform-framework-client-layout-entity-gallery-page.component-DmiB__JR.mjs.map +0 -1
  106. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-CazpERbX.mjs.map +0 -1
  107. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-CIf1VWNW.mjs.map +0 -1
  108. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DRz-kVsT.mjs.map +0 -1
@@ -1,10 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, provideAppInitializer, Optional, Inject, NgModule, input, output, Component } from '@angular/core';
3
- import { map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, from, combineLatest, first } from 'rxjs';
4
- import { AXPBroadcastEventService, AXP_SESSION_SERVICE } from '@acorex-platform/framework-client/core';
2
+ import { InjectionToken, Injector, Injectable, signal, inject, Input, Directive, Optional, Inject, NgModule, input, output, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, distinctUntilChanged, of, from, combineLatest, first } from 'rxjs';
4
+ import { AXPDebugService, AXPBroadcastEventService, AXP_SESSION_SERVICE, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, provideAppStartUpTask, AXP_APP_STARTUP_PRIORITY } from '@acorex-platform/framework-client/core';
5
5
  import { isEmpty } from 'lodash-es';
6
- import { AXPWidgetsCatalog } from '@acorex-platform/framework-shared/core';
7
- import { AXDataSource } from '@acorex/cdk/common';
6
+ import { AXPCommandService, AXPQueryService } from '@acorex-platform/framework-client/runtime';
7
+ import { AUTH_LOGOUT_DEFAULT, AUTH_REFRESH_DEFAULT, AUTH_UPDATE_SESSION_DEFAULT, AUTH_LIST_PROVIDERS_DEFAULT } from '@acorex-platform/framework-shared/runtime';
8
+ import { runtimeQueryPayloadData, runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';
8
9
 
9
10
  const AXP_APPLICATION_LOADER = new InjectionToken('AXP_APPLICATION_LOADER', {
10
11
  providedIn: 'root',
@@ -39,13 +40,6 @@ class AXPApplicationDefaultLoader {
39
40
  }
40
41
  }
41
42
 
42
- //#region ---- Datasource keys ----
43
- /** Platform auth datasource registry names. */
44
- const AXPPlatformAuthDataSourceKeys = {
45
- PermissionDefinitions: 'platform-permission-definitions',
46
- };
47
- //#endregion
48
-
49
43
  class AXPUnauthorizedError extends Error {
50
44
  constructor(message, data) {
51
45
  super(message);
@@ -111,6 +105,59 @@ class AXPTenantDefaultLoader {
111
105
  }
112
106
  }
113
107
 
108
+ /** Debug namespace for auth session diagnostics. Filter console with `[AXP:AUTH-SESSION]`. */
109
+ const AUTH_SESSION_TRACE_NS = 'AUTH-SESSION';
110
+ //#region ----- Trace area titles -----
111
+ const AUTH_SESSION_TRACE_TITLE = {
112
+ /** Sign-in / register / sign-out lifecycle. */
113
+ Lifecycle: 'Lifecycle',
114
+ /** Cookie session restore + refresh. */
115
+ Restore: 'Restore',
116
+ /** Tenant / application bind via UpdateSession. */
117
+ Workspace: 'Workspace',
118
+ /** Auth:GetAccessPolicy load (permissions + features). */
119
+ AccessPolicy: 'AccessPolicy',
120
+ /** ListTenants / ListApplications. */
121
+ Catalog: 'Catalog',
122
+ /** Route guards (login, tenant-chooser, app-chooser). */
123
+ Guard: 'Guard',
124
+ };
125
+ //#endregion
126
+
127
+ //#region ----- Lifecycle -----
128
+ function traceAuthLifecycle(message, data) {
129
+ AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Lifecycle, message, data);
130
+ }
131
+ function traceAuthLifecycleError(message, data) {
132
+ AXPDebugService.instance?.error(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Lifecycle, message, data);
133
+ }
134
+ //#endregion
135
+ //#region ----- Restore -----
136
+ function traceAuthRestore(message, data) {
137
+ AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Restore, message, data);
138
+ }
139
+ //#endregion
140
+ //#region ----- Workspace -----
141
+ function traceAuthWorkspace(message, data) {
142
+ AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Workspace, message, data);
143
+ }
144
+ //#endregion
145
+ //#region ----- Access policy -----
146
+ function traceAuthAccessPolicy(message, data) {
147
+ AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.AccessPolicy, message, data);
148
+ }
149
+ //#endregion
150
+ //#region ----- Catalog -----
151
+ function traceAuthCatalog(message, data) {
152
+ AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Catalog, message, data);
153
+ }
154
+ //#endregion
155
+ //#region ----- Guards -----
156
+ function traceAuthGuard(message, data) {
157
+ AXPDebugService.instance?.trace(AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE.Guard, message, data);
158
+ }
159
+ //#endregion
160
+
114
161
  class AXPAuthStrategyRegistryService {
115
162
  constructor(injector) {
116
163
  this.strategies = new Map();
@@ -128,10 +175,10 @@ class AXPAuthStrategyRegistryService {
128
175
  get(strategyKey) {
129
176
  return this.strategies.get(strategyKey);
130
177
  }
131
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthStrategyRegistryService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
132
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthStrategyRegistryService, providedIn: 'root' }); }
178
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthStrategyRegistryService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
179
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthStrategyRegistryService, providedIn: 'root' }); }
133
180
  }
134
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthStrategyRegistryService, decorators: [{
181
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthStrategyRegistryService, decorators: [{
135
182
  type: Injectable,
136
183
  args: [{
137
184
  providedIn: 'root'
@@ -154,7 +201,8 @@ class AXPFeatureDirective {
154
201
  constructor(templateRef, viewContainer) {
155
202
  this.templateRef = templateRef;
156
203
  this.viewContainer = viewContainer;
157
- this.hasView = signal(false, ...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
204
+ this.hasView = signal(false, /* @ts-ignore */
205
+ ...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
158
206
  this.sessionService = inject(AXPSessionService);
159
207
  }
160
208
  set feature(featureKeys) {
@@ -188,10 +236,10 @@ class AXPFeatureDirective {
188
236
  ngOnDestroy() {
189
237
  this.subscription?.unsubscribe();
190
238
  }
191
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFeatureDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
192
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AXPFeatureDirective, isStandalone: false, selector: "[feature]", inputs: { feature: "feature", featureElse: "featureElse" }, ngImport: i0 }); }
239
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPFeatureDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
240
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: AXPFeatureDirective, isStandalone: false, selector: "[feature]", inputs: { feature: "feature", featureElse: "featureElse" }, ngImport: i0 }); }
193
241
  }
194
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFeatureDirective, decorators: [{
242
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPFeatureDirective, decorators: [{
195
243
  type: Directive,
196
244
  args: [{
197
245
  selector: '[feature]',
@@ -225,6 +273,36 @@ const AXPFeatureGuard = (route, state) => {
225
273
  */
226
274
  const AXP_FEATURE_CHECKER = new InjectionToken('AXP_FEATURE_CHECKER');
227
275
 
276
+ //#region ----- Imports -----
277
+ //#endregion
278
+ //#region ----- Feature Evaluator Scope Provider -----
279
+ /**
280
+ * Exposes `feature.check(...)` / `feature.checkAny(...)` for entity expressions
281
+ * (e.g. `schema.visible: '{{ feature.check("Module:Feature:X") }}'`).
282
+ */
283
+ class AXPFeatureEvaluatorScopeProvider {
284
+ constructor() {
285
+ this.sessionService = inject(AXPSessionService);
286
+ }
287
+ async provide(context) {
288
+ context.addScope('feature', {
289
+ /**
290
+ * True when every listed feature key is enabled for the session.
291
+ */
292
+ check: (...features) => {
293
+ return this.sessionService.isFeatureEnabled(...features);
294
+ },
295
+ /**
296
+ * True when any listed feature key is enabled for the session.
297
+ */
298
+ checkAny: (...features) => {
299
+ return features.some((f) => this.sessionService.isFeatureEnabled(f));
300
+ },
301
+ });
302
+ }
303
+ }
304
+ //#endregion
305
+
228
306
  const AXP_PERMISSION_LOADER = new InjectionToken('AXP_PERMISSION_LOADER', {
229
307
  providedIn: 'root',
230
308
  factory: () => {
@@ -247,6 +325,33 @@ const AXP_PERMISSION_CHECKER = new InjectionToken('AXP_PERMISSION_CHECKER');
247
325
  const AXP_ACCESS_POLICY_CACHE_PORT = new InjectionToken('AXP_ACCESS_POLICY_CACHE_PORT');
248
326
  //#endregion
249
327
 
328
+ //#region ---- OAuth pending strategy (survives restoreSession) ----
329
+ /**
330
+ * Persisted while an authorization-code redirect is in flight.
331
+ * `restoreSession` clears partial `AXP_SESSION` (no user / no cookies) — this key
332
+ * must survive so `/auth/oauth-callback` can resolve the strategy.
333
+ */
334
+ const AXP_OAUTH_PENDING_STRATEGY_KEY = 'axp_oauth_pending_strategy';
335
+ /** Set by platform OAuth strategies before redirect (PKCE). */
336
+ const AXP_OAUTH_PKCE_VERIFIER_KEY = 'pkce_code_verifier';
337
+ function setOAuthPendingStrategy(strategy) {
338
+ const trimmed = strategy?.trim();
339
+ if (!trimmed) {
340
+ return;
341
+ }
342
+ localStorage.setItem(AXP_OAUTH_PENDING_STRATEGY_KEY, trimmed);
343
+ }
344
+ function getOAuthPendingStrategy() {
345
+ return localStorage.getItem(AXP_OAUTH_PENDING_STRATEGY_KEY)?.trim() || null;
346
+ }
347
+ function clearOAuthPendingStrategy() {
348
+ localStorage.removeItem(AXP_OAUTH_PENDING_STRATEGY_KEY);
349
+ }
350
+ function isOAuthRedirectInFlight() {
351
+ return !!localStorage.getItem(AXP_OAUTH_PKCE_VERIFIER_KEY) || !!getOAuthPendingStrategy();
352
+ }
353
+ //#endregion
354
+
250
355
  class AXPSessionService {
251
356
  constructor() {
252
357
  this.eventService = inject(AXPBroadcastEventService);
@@ -259,6 +364,9 @@ class AXPSessionService {
259
364
  this.permissionChecker = inject(AXP_PERMISSION_CHECKER, { optional: true });
260
365
  this.featureChecker = inject(AXP_FEATURE_CHECKER, { optional: true });
261
366
  this.accessPolicyCache = inject(AXP_ACCESS_POLICY_CACHE_PORT, { optional: true });
367
+ /** In-memory catalog caches — shared by guards and chooser UI (one List* per bind). */
368
+ this.tenantsListCache = null;
369
+ this.applicationsListCache = null;
262
370
  this.status = new BehaviorSubject(AXPSessionStatus.Unauthenticated);
263
371
  this.status$ = this.status.asObservable().pipe(shareReplay(1));
264
372
  // Add loading state to prevent premature redirects
@@ -274,10 +382,7 @@ class AXPSessionService {
274
382
  this.permissions$ = this.permissionsSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
275
383
  this.featuresSubject = new BehaviorSubject([]);
276
384
  this.features$ = this.featuresSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
277
- this.isAuthenticated$ = this.status$.pipe(map((status) => {
278
- const isAuth = status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized;
279
- return isAuth;
280
- }), shareReplay(1));
385
+ this.isAuthenticated$ = this.status$.pipe(map((status) => this.isAuthenticatedStatus(status)), shareReplay(1));
281
386
  // Add a new observable that considers loading state
282
387
  this.isAuthenticatedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
283
388
  if (loading) {
@@ -288,6 +393,24 @@ class AXPSessionService {
288
393
  return this.isAuthenticated$;
289
394
  }), shareReplay(1));
290
395
  this.isAuthorized$ = this.status$.pipe(map((status) => status === AXPSessionStatus.Authorized), shareReplay(1));
396
+ this.isReadyForAppScopedApi$ = this.isAuthorized$.pipe(map((authorized) => authorized && this.hasSessionCredentials()), distinctUntilChanged(), shareReplay(1));
397
+ /** Tenant list for the signed-in user; skips the API when there is no cookie session. */
398
+ this.tenants$ = this.isAuthenticated$.pipe(distinctUntilChanged(), switchMap((authenticated) => {
399
+ if (!authenticated || !this.hasSessionCredentials()) {
400
+ return of([]);
401
+ }
402
+ return from(this.getTenantsList());
403
+ }), shareReplay({ bufferSize: 1, refCount: true }));
404
+ /** Application list for the current tenant; skips the API when there is no cookie session. */
405
+ this.applications$ = combineLatest([
406
+ this.isAuthenticated$,
407
+ this.tenant$,
408
+ ]).pipe(distinctUntilChanged(([authenticated, tenant], [previousAuthenticated, previousTenant]) => authenticated === previousAuthenticated && tenant?.id === previousTenant?.id), switchMap(([authenticated, tenant]) => {
409
+ if (!authenticated || !this.hasSessionCredentials() || !tenant?.id) {
410
+ return of([]);
411
+ }
412
+ return from(this.getApplicationsList());
413
+ }), shareReplay({ bufferSize: 1, refCount: true }));
291
414
  /** Waits for session restore to finish, then emits whether the session is fully authorized. */
292
415
  this.isAuthorizedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
293
416
  if (loading) {
@@ -305,48 +428,109 @@ class AXPSessionService {
305
428
  this.syncContextSubjectsFromStorage();
306
429
  return this.currentTenantSubject.value;
307
430
  }
308
- get tenants$() {
309
- return from(this.tenantLoader.getList(this.getContext()));
310
- }
311
431
  get application() {
312
432
  this.syncContextSubjectsFromStorage();
313
433
  return this.currentApplicationSubject.value;
314
434
  }
315
- get applications$() {
316
- return from(this.applicationLoader.getList(this.getContext()));
317
- }
318
435
  get permissions() {
319
436
  return this.permissionsSubject.value ?? [];
320
437
  }
321
438
  get features() {
322
439
  return this.featuresSubject.value ?? [];
323
440
  }
441
+ /** Synchronous counterpart to {@link isAuthenticated$}. */
442
+ get isAuthenticated() {
443
+ return this.isAuthenticatedStatus(this.status.value);
444
+ }
445
+ /** Synchronous counterpart to {@link isAuthorized$}. */
446
+ get isAuthorized() {
447
+ return this.status.value === AXPSessionStatus.Authorized;
448
+ }
449
+ /**
450
+ * Tenant and application are selected and permissions are loaded.
451
+ * Gate app-scoped runtime work (entity queries, inbox, activity).
452
+ */
453
+ get isReadyForAppScopedApi() {
454
+ return this.isAuthorized && this.hasSessionCredentials();
455
+ }
324
456
  async restoreSession() {
325
457
  this.isLoading.next(true);
458
+ const startedAt = Date.now();
326
459
  try {
327
460
  const sessionData = this.getSessionData();
328
- if (sessionData) {
329
- if (sessionData.user) {
330
- this.currentUserSubject.next(sessionData.user);
331
- // Restore tenant and application from session data
332
- if (sessionData.tenant) {
333
- this.currentTenantSubject.next(sessionData.tenant);
334
- }
335
- if (sessionData.application) {
336
- this.currentApplicationSubject.next(sessionData.application);
461
+ if (!sessionData?.user || !sessionData.cookieSession) {
462
+ // OAuth code exchange needs `strategy` after redirect. Do not wipe the
463
+ // pre-login strategy / pending key while PKCE (or pending strategy) exists.
464
+ if (isOAuthRedirectInFlight()) {
465
+ const strategy = sessionData?.strategy || getOAuthPendingStrategy();
466
+ if (strategy && sessionData?.strategy !== strategy) {
467
+ this.setSession({
468
+ strategy,
469
+ user: null,
470
+ tenant: null,
471
+ application: null,
472
+ cookieSession: false,
473
+ });
337
474
  }
338
- this.status.next(AXPSessionStatus.Authenticated);
339
- await this.loadPermissions();
340
- await this.loadFeatures();
341
- await this.syncSessionAuthorization();
475
+ this.status.next(AXPSessionStatus.Unauthorized);
476
+ traceAuthRestore('OAuth redirect in flight — kept strategy', () => ({ strategy }));
477
+ return;
342
478
  }
479
+ this.clearSession();
480
+ this.status.next(AXPSessionStatus.Unauthorized);
481
+ traceAuthRestore('No cookie session metadata — cleared');
482
+ return;
343
483
  }
344
- else {
484
+ // Validate HttpOnly cookies and rotate tokens before trusting local metadata.
485
+ const strategyName = sessionData.strategy;
486
+ const strategy = strategyName ? this.authStrategyRegistry.get(strategyName) : null;
487
+ if (!strategy) {
488
+ this.clearSession();
489
+ this.status.next(AXPSessionStatus.Unauthorized);
490
+ traceAuthRestore('Strategy missing — cleared', () => ({ strategyName }));
491
+ return;
492
+ }
493
+ // Keep subjects warm for refresh context, but do NOT mark Authenticated yet —
494
+ // that would trigger settings/chat side-effects while isLoading is still true.
495
+ this.currentUserSubject.next(sessionData.user);
496
+ this.currentTenantSubject.next(sessionData.tenant ?? null);
497
+ this.currentApplicationSubject.next(sessionData.application ?? null);
498
+ traceAuthRestore('Refreshing cookie session', () => ({
499
+ userId: sessionData.user?.id,
500
+ tenantId: sessionData.tenant?.id,
501
+ applicationId: sessionData.application?.id,
502
+ strategy: strategyName,
503
+ }));
504
+ const refreshResult = await strategy.refreshToken(this.getContext());
505
+ if (!refreshResult?.succeed || !refreshResult.data?.user) {
506
+ this.clearSession();
345
507
  this.status.next(AXPSessionStatus.Unauthorized);
508
+ traceAuthRestore('Refresh failed — cleared');
509
+ return;
346
510
  }
511
+ // Preserve workspace binding from local metadata when refresh omits it.
512
+ const merged = {
513
+ succeed: true,
514
+ data: {
515
+ ...refreshResult.data,
516
+ tenant: refreshResult.data.tenant ?? sessionData.tenant ?? null,
517
+ application: refreshResult.data.application ?? sessionData.application ?? null,
518
+ },
519
+ };
520
+ await this.applySignInResult(strategyName, merged);
521
+ traceAuthRestore('Restore complete', () => ({
522
+ ms: Date.now() - startedAt,
523
+ status: this.status.value,
524
+ tenantId: this.tenant?.id,
525
+ applicationId: this.application?.id,
526
+ }));
347
527
  }
348
528
  catch (error) {
529
+ this.clearSession();
349
530
  this.status.next(AXPSessionStatus.Unauthorized);
531
+ traceAuthRestore('Restore error — cleared', () => ({
532
+ error: error instanceof Error ? error.message : String(error),
533
+ }));
350
534
  }
351
535
  finally {
352
536
  this.isLoading.next(false);
@@ -354,33 +538,45 @@ class AXPSessionService {
354
538
  }
355
539
  async signin(credentials) {
356
540
  this.isLoading.next(true);
541
+ const startedAt = Date.now();
542
+ traceAuthLifecycle('Sign-in started', () => ({ strategy: credentials.strategy }));
357
543
  try {
358
- //
359
- // this.clearSession();
360
- //
361
544
  const strategy = this.authStrategyRegistry.get(credentials.strategy);
362
545
  if (!strategy) {
363
546
  throw new Error(`Authentication strategy '${credentials.strategy}' is not supported`);
364
547
  }
365
548
  const result = await strategy.signin(credentials);
366
- if (!result)
549
+ if (!result) {
550
+ // Redirect / multi-step strategies return void — keep strategy + PKCE metadata.
551
+ this.status.next(AXPSessionStatus.Unauthenticated);
367
552
  return;
553
+ }
368
554
  if (result?.succeed) {
369
555
  await this.applySignInResult(credentials.strategy, result);
556
+ traceAuthLifecycle('Sign-in succeeded', () => ({
557
+ ms: Date.now() - startedAt,
558
+ userId: this.user?.id,
559
+ tenantId: this.tenant?.id,
560
+ applicationId: this.application?.id,
561
+ status: this.status.value,
562
+ }));
370
563
  }
371
564
  else {
565
+ this.clearSession();
372
566
  this.status.next(AXPSessionStatus.Unauthenticated);
373
567
  throw new Error(`Invalid Username or Password`);
374
568
  }
375
569
  }
376
570
  catch (error) {
377
- console.error('Signin error:', error);
571
+ traceAuthLifecycleError('Sign-in failed', () => ({
572
+ error: error instanceof Error ? error.message : String(error),
573
+ }));
574
+ this.clearSession();
378
575
  this.status.next(AXPSessionStatus.Unauthenticated);
379
576
  throw error;
380
577
  }
381
578
  finally {
382
579
  this.isLoading.next(false);
383
- console.log('Signin process completed');
384
580
  }
385
581
  }
386
582
  async register(credentials) {
@@ -399,6 +595,7 @@ class AXPSessionService {
399
595
  }
400
596
  catch (error) {
401
597
  console.error('Register error:', error);
598
+ this.clearSession();
402
599
  this.status.next(AXPSessionStatus.Unauthenticated);
403
600
  throw error;
404
601
  }
@@ -407,38 +604,72 @@ class AXPSessionService {
407
604
  }
408
605
  }
409
606
  async applySignInResult(strategy, result) {
410
- this.clearSession();
607
+ // Reset client metadata only — do not call clearSession() (that flips status to
608
+ // Unauthenticated and can trigger logout listeners mid-flow).
609
+ this.invalidateAccessPolicyCache();
610
+ this.clearCatalogCaches();
611
+ this.permissionsSubject.next([]);
612
+ this.featuresSubject.next([]);
411
613
  this.currentUserSubject.next(result.data.user);
412
614
  this.setSession({
413
- accessToken: result.data.accessToken,
414
- refreshToken: result.data.refreshToken,
415
615
  strategy,
416
616
  user: result.data.user,
417
617
  tenant: result.data?.tenant ?? null,
618
+ // Fresh sign-in tokens omit application; refresh/restore may include it.
418
619
  application: result.data?.application ?? null,
419
620
  expiresIn: result.data?.expiresIn,
420
- idToken: result.data?.idToken ?? null,
421
621
  locked: false,
622
+ cookieSession: true,
422
623
  });
423
- await this.loadPermissions();
424
- await this.loadFeatures();
624
+ await this.reloadAccessPolicyIfReady('applySignInResult');
425
625
  await this.syncSessionAuthorization();
426
626
  }
627
+ /**
628
+ * Clears the selected application so the user must pick one (app chooser).
629
+ * Keeps tenant/user/token intact and demotes status to Authenticated.
630
+ */
631
+ clearApplicationSelection() {
632
+ if (!this.application?.id) {
633
+ return;
634
+ }
635
+ this.setSession({ application: null });
636
+ if (this.user) {
637
+ this.status.next(AXPSessionStatus.Authenticated);
638
+ }
639
+ }
427
640
  async updateToken(params) {
428
641
  this.isLoading.next(true);
642
+ const startedAt = Date.now();
643
+ const isApplicationUpdate = this.isApplicationContextUpdate(params);
644
+ const isTenantUpdate = this.isTenantContextUpdate(params);
645
+ traceAuthWorkspace('UpdateSession started', () => ({
646
+ isTenantUpdate,
647
+ isApplicationUpdate,
648
+ tenantId: params?.['tenant']?.id ?? params?.['tenantId'],
649
+ applicationId: params?.['application']?.id ?? params?.['applicationId'],
650
+ }));
651
+ // Pause app-scoped API work while token/context is rebound (avoids 401/403 races).
652
+ if (this.isAuthorized) {
653
+ this.status.next(AXPSessionStatus.Authenticated);
654
+ }
655
+ if (isTenantUpdate && !isApplicationUpdate) {
656
+ this.currentApplicationSubject.next(null);
657
+ this.applicationsListCache = null;
658
+ }
429
659
  try {
430
- const strategyName = this.getSessionData()?.strategy;
660
+ const strategyName = this.resolveStrategyName();
431
661
  if (!strategyName) {
432
662
  throw new Error('Strategy not found');
433
663
  }
434
664
  const strategy = this.authStrategyRegistry.get(strategyName);
435
665
  if (!strategy) {
436
- throw new Error(`Authentication strategy '${this.getSessionData()?.strategy}' is not supported`);
666
+ throw new Error(`Authentication strategy '${strategyName}' is not supported`);
437
667
  }
438
668
  const result = await strategy.updateToken(params);
439
669
  if (result?.succeed) {
440
670
  if (params?.['code']) {
441
671
  this.clearSession();
672
+ clearOAuthPendingStrategy();
442
673
  }
443
674
  else {
444
675
  this.permissionsSubject.next([]);
@@ -447,19 +678,22 @@ class AXPSessionService {
447
678
  }
448
679
  this.currentUserSubject.next(result.data.user);
449
680
  this.setSession({
450
- accessToken: result.data.accessToken,
451
- refreshToken: result.data.refreshToken,
452
681
  strategy: strategyName,
453
682
  user: result.data.user,
454
683
  tenant: result.data?.tenant ?? null,
455
- application: result.data?.application ?? null,
684
+ application: this.resolveApplicationAfterUpdate(result.data?.application ?? null, isApplicationUpdate, isTenantUpdate),
456
685
  expiresIn: result.data?.expiresIn,
457
- idToken: result.data?.idToken ?? null,
458
686
  locked: false,
687
+ cookieSession: true,
459
688
  });
460
- await this.loadPermissions();
461
- await this.loadFeatures();
689
+ await this.reloadAccessPolicyIfReady('updateToken');
462
690
  await this.syncSessionAuthorization();
691
+ traceAuthWorkspace('UpdateSession complete', () => ({
692
+ ms: Date.now() - startedAt,
693
+ status: this.status.value,
694
+ tenantId: this.tenant?.id,
695
+ applicationId: this.application?.id,
696
+ }));
463
697
  }
464
698
  else {
465
699
  this.status.next(AXPSessionStatus.Unauthenticated);
@@ -467,16 +701,23 @@ class AXPSessionService {
467
701
  }
468
702
  }
469
703
  catch (error) {
470
- console.error('Update token error:', error);
704
+ traceAuthLifecycleError('UpdateSession failed', () => ({
705
+ error: error instanceof Error ? error.message : String(error),
706
+ }));
707
+ // Failed workspace bind must not leave a half-authenticated cookie session
708
+ // (that causes splash restore loops after refresh).
709
+ if (params?.['code']) {
710
+ clearOAuthPendingStrategy();
711
+ }
712
+ this.clearSession();
471
713
  throw error;
472
714
  }
473
715
  finally {
474
716
  this.isLoading.next(false);
475
- console.log('Update token process completed');
476
717
  }
477
718
  }
478
719
  async signout() {
479
- console.log('Signing out...');
720
+ traceAuthLifecycle('Sign-out started', () => ({ userId: this.user?.id }));
480
721
  this.isLoading.next(true);
481
722
  try {
482
723
  const sessionData = this.getSessionData();
@@ -489,53 +730,76 @@ class AXPSessionService {
489
730
  catch (error) { }
490
731
  }
491
732
  }
492
- //
493
733
  const userId = this.user?.id;
494
734
  this.clearSession();
495
735
  this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
496
736
  this.isLoading.next(false);
497
737
  this.status.next(AXPSessionStatus.SignedOut);
738
+ traceAuthLifecycle('Sign-out complete', () => ({ userId }));
498
739
  }
499
740
  finally {
500
741
  this.isLoading.next(false);
501
742
  }
502
743
  }
503
744
  async refreshToken() {
504
- console.log('Refreshing token...');
505
- return new Promise(async (resolve, reject) => {
506
- const sessionData = this.getSessionData();
507
- if (!sessionData || !sessionData?.refreshToken) {
508
- console.log('No session data or refresh token found');
509
- reject(new Error('No refresh token available'));
510
- return;
511
- }
512
- const strategy = this.authStrategyRegistry.get(sessionData.strategy);
513
- if (!strategy) {
514
- console.error('Authentication strategy not found:', sessionData.strategy);
515
- reject(new Error(`Authentication strategy '${sessionData.strategy}' is not found`));
516
- return;
517
- }
518
- try {
519
- const result = await strategy.refreshToken(this.getContext());
520
- if (result.succeed) {
521
- console.log('Token refresh successful');
522
- this.setSession(result.data);
523
- resolve(result.data?.accessToken);
524
- }
525
- else {
526
- console.error('Token refresh failed');
527
- this.clearSession();
528
- this.status.next(AXPSessionStatus.Expired);
529
- reject(new Error('Token refresh failed'));
530
- }
531
- }
532
- catch (error) {
533
- console.error('Error during token refresh:', error);
745
+ const sessionData = this.getSessionData();
746
+ if (!sessionData?.strategy || !sessionData.cookieSession) {
747
+ throw new Error('No cookie session available');
748
+ }
749
+ const strategy = this.authStrategyRegistry.get(sessionData.strategy);
750
+ if (!strategy) {
751
+ throw new Error(`Authentication strategy '${sessionData.strategy}' is not found`);
752
+ }
753
+ try {
754
+ const result = await strategy.refreshToken(this.getContext());
755
+ if (!result.succeed || !result.data?.user) {
534
756
  this.clearSession();
535
757
  this.status.next(AXPSessionStatus.Expired);
536
- reject(error);
758
+ throw new Error('Token refresh failed');
537
759
  }
538
- });
760
+ this.setSession({
761
+ strategy: sessionData.strategy,
762
+ user: result.data.user,
763
+ tenant: result.data.tenant ?? sessionData.tenant ?? null,
764
+ application: result.data.application ?? sessionData.application ?? null,
765
+ expiresIn: result.data.expiresIn,
766
+ cookieSession: true,
767
+ locked: sessionData.locked === true,
768
+ });
769
+ return true;
770
+ }
771
+ catch (error) {
772
+ this.clearSession();
773
+ this.status.next(AXPSessionStatus.Expired);
774
+ throw error;
775
+ }
776
+ }
777
+ /**
778
+ * Auth:GetAccessPolicy is only meaningful with tenant + application bound.
779
+ * Skipping incomplete contexts cuts 2 redundant calls on the common login path.
780
+ */
781
+ async reloadAccessPolicyIfReady(reason) {
782
+ if (!this.hasCompleteSessionContext()) {
783
+ this.permissionsSubject.next([]);
784
+ this.featuresSubject.next([]);
785
+ traceAuthAccessPolicy('Skipped GetAccessPolicy (incomplete workspace)', () => ({
786
+ reason,
787
+ tenantId: this.tenant?.id ?? null,
788
+ applicationId: this.application?.id ?? null,
789
+ }));
790
+ return;
791
+ }
792
+ const startedAt = Date.now();
793
+ await this.loadPermissions();
794
+ await this.loadFeatures();
795
+ traceAuthAccessPolicy('GetAccessPolicy loaded', () => ({
796
+ reason,
797
+ ms: Date.now() - startedAt,
798
+ permissionCount: this.permissions.length,
799
+ featureCount: this.features.length,
800
+ tenantId: this.tenant?.id,
801
+ applicationId: this.application?.id,
802
+ }));
539
803
  }
540
804
  async loadPermissions() {
541
805
  try {
@@ -543,7 +807,9 @@ class AXPSessionService {
543
807
  this.permissionsSubject.next(permissions ?? []);
544
808
  }
545
809
  catch (error) {
546
- console.error('Error loading permissions:', error);
810
+ traceAuthLifecycleError('Load permissions failed', () => ({
811
+ error: error instanceof Error ? error.message : String(error),
812
+ }));
547
813
  this.permissionsSubject.next([]);
548
814
  }
549
815
  }
@@ -553,10 +819,62 @@ class AXPSessionService {
553
819
  this.featuresSubject.next(features ?? []);
554
820
  }
555
821
  catch (error) {
556
- console.error('Error loading features:', error);
822
+ traceAuthLifecycleError('Load features failed', () => ({
823
+ error: error instanceof Error ? error.message : String(error),
824
+ }));
557
825
  this.featuresSubject.next([]);
558
826
  }
559
827
  }
828
+ /** Shared ListTenants — used by guards and {@link tenants$}. */
829
+ async getTenantsList() {
830
+ if (!this.hasSessionCredentials()) {
831
+ return [];
832
+ }
833
+ const userId = this.user?.id ?? '';
834
+ if (this.tenantsListCache?.userId === userId) {
835
+ traceAuthCatalog('Tenants cache hit', () => ({
836
+ userId,
837
+ count: this.tenantsListCache.items.length,
838
+ }));
839
+ return this.tenantsListCache.items;
840
+ }
841
+ const startedAt = Date.now();
842
+ const items = await this.tenantLoader.getList(this.getContext());
843
+ this.tenantsListCache = { userId, items };
844
+ traceAuthCatalog('Tenants fetched', () => ({
845
+ userId,
846
+ count: items.length,
847
+ ms: Date.now() - startedAt,
848
+ }));
849
+ return items;
850
+ }
851
+ /** Shared ListApplications — used by guards and {@link applications$}. */
852
+ async getApplicationsList() {
853
+ if (!this.hasSessionCredentials() || !this.tenant?.id) {
854
+ return [];
855
+ }
856
+ const tenantId = this.tenant.id;
857
+ if (this.applicationsListCache?.tenantId === tenantId) {
858
+ traceAuthCatalog('Applications cache hit', () => ({
859
+ tenantId,
860
+ count: this.applicationsListCache.items.length,
861
+ }));
862
+ return this.applicationsListCache.items;
863
+ }
864
+ const startedAt = Date.now();
865
+ const items = await this.applicationLoader.getList(this.getContext());
866
+ this.applicationsListCache = { tenantId, items };
867
+ traceAuthCatalog('Applications fetched', () => ({
868
+ tenantId,
869
+ count: items.length,
870
+ ms: Date.now() - startedAt,
871
+ }));
872
+ return items;
873
+ }
874
+ clearCatalogCaches() {
875
+ this.tenantsListCache = null;
876
+ this.applicationsListCache = null;
877
+ }
560
878
  async signInComplete() {
561
879
  // Ensure we have the required data
562
880
  if (!this.user) {
@@ -585,21 +903,25 @@ class AXPSessionService {
585
903
  }
586
904
  this.status.next(AXPSessionStatus.Authenticated);
587
905
  }
588
- setSession(tokens) {
589
- const existing = this.getSessionData() ?? {};
906
+ setSession(partial) {
907
+ const existing = this.getSessionData() ?? { strategy: partial.strategy ?? 'user-pass' };
590
908
  const sessionData = {
591
909
  ...existing,
592
- ...tokens,
910
+ ...partial,
911
+ strategy: partial.strategy ?? existing.strategy ?? 'user-pass',
593
912
  };
594
- // Update subjects
595
- if (tokens.user) {
596
- this.currentUserSubject.next(tokens.user);
913
+ // Never persist tokens in browser storage — HttpOnly cookies are authoritative.
914
+ delete sessionData.accessToken;
915
+ delete sessionData.refreshToken;
916
+ delete sessionData.idToken;
917
+ if (partial.user) {
918
+ this.currentUserSubject.next(partial.user);
597
919
  }
598
- if ('tenant' in tokens) {
599
- this.currentTenantSubject.next(tokens.tenant ?? null);
920
+ if ('tenant' in partial) {
921
+ this.currentTenantSubject.next(partial.tenant ?? null);
600
922
  }
601
- if ('application' in tokens) {
602
- this.currentApplicationSubject.next(tokens.application ?? null);
923
+ if ('application' in partial) {
924
+ this.currentApplicationSubject.next(partial.application ?? null);
603
925
  }
604
926
  localStorage.setItem(AXPSessionService.SESSION_KEY, JSON.stringify(sessionData));
605
927
  }
@@ -616,9 +938,19 @@ class AXPSessionService {
616
938
  this.setSession({ locked });
617
939
  }
618
940
  setStrategy(strategy) {
941
+ setOAuthPendingStrategy(strategy);
619
942
  const sessionData = this.getSessionData();
620
- const newSessionData = { ...sessionData, strategy };
621
- this.setSession(newSessionData);
943
+ // Do not invent a persisted session from strategy alone (pre-login).
944
+ // That leaves a partial AXP_SESSION and can break splash restore after a failed sign-in.
945
+ if (!sessionData?.user && !sessionData?.cookieSession) {
946
+ this.setSession({ strategy, user: null, tenant: null, application: null, cookieSession: false });
947
+ return;
948
+ }
949
+ this.setSession({ ...sessionData, strategy });
950
+ }
951
+ /** Session strategy, or pending OAuth strategy left for the callback page. */
952
+ resolveStrategyName() {
953
+ return this.getSessionData()?.strategy?.trim() || getOAuthPendingStrategy();
622
954
  }
623
955
  getSessionData() {
624
956
  try {
@@ -638,13 +970,15 @@ class AXPSessionService {
638
970
  }
639
971
  clearSession() {
640
972
  this.invalidateAccessPolicyCache();
973
+ this.clearCatalogCaches();
974
+ this.status.next(AXPSessionStatus.Unauthenticated);
641
975
  this.currentUserSubject.next(null);
642
976
  this.currentTenantSubject.next(null);
643
977
  this.currentApplicationSubject.next(null);
644
978
  this.permissionsSubject.next([]);
645
979
  this.featuresSubject.next([]);
646
980
  localStorage.removeItem(AXPSessionService.SESSION_KEY);
647
- this.status.next(AXPSessionStatus.Unauthenticated);
981
+ clearOAuthPendingStrategy();
648
982
  }
649
983
  /** Keeps in-memory subjects aligned with persisted session (e.g. after user switch). */
650
984
  syncContextSubjectsFromStorage() {
@@ -711,9 +1045,16 @@ class AXPSessionService {
711
1045
  // Module names are stored as features with value: true when enabled
712
1046
  return this.features.some(f => f.name === normalizedModuleName && f.value === true);
713
1047
  }
1048
+ /**
1049
+ * Access tokens are HttpOnly and not readable from JS.
1050
+ * Use {@link hasSessionCredentials} for auth gating.
1051
+ */
714
1052
  getToken() {
715
- const sessionData = this.getSessionData();
716
- return sessionData?.accessToken;
1053
+ return undefined;
1054
+ }
1055
+ /** True when the SPA has an active cookie-backed session. */
1056
+ hasSessionCredentials() {
1057
+ return this.getSessionData()?.cookieSession === true && this.isAuthenticated;
717
1058
  }
718
1059
  getContext() {
719
1060
  return new AXPSessionContext({
@@ -725,10 +1066,28 @@ class AXPSessionService {
725
1066
  invalidateAccessPolicyCache() {
726
1067
  this.accessPolicyCache?.invalidate();
727
1068
  }
728
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPSessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
729
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPSessionService, providedIn: 'root' }); }
1069
+ isApplicationContextUpdate(params) {
1070
+ return params?.['application'] != null || params?.['applicationId'] != null;
1071
+ }
1072
+ isTenantContextUpdate(params) {
1073
+ return params?.['tenant'] != null || params?.['tenantId'] != null;
1074
+ }
1075
+ resolveApplicationAfterUpdate(fromToken, isApplicationUpdate, isTenantUpdate) {
1076
+ if (isApplicationUpdate) {
1077
+ return fromToken;
1078
+ }
1079
+ if (isTenantUpdate) {
1080
+ return null;
1081
+ }
1082
+ return fromToken;
1083
+ }
1084
+ isAuthenticatedStatus(status) {
1085
+ return status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized;
1086
+ }
1087
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1088
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSessionService, providedIn: 'root' }); }
730
1089
  }
731
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPSessionService, decorators: [{
1090
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSessionService, decorators: [{
732
1091
  type: Injectable,
733
1092
  args: [{
734
1093
  providedIn: 'root',
@@ -739,7 +1098,8 @@ class AXPPermissionDirective {
739
1098
  constructor(templateRef, viewContainer) {
740
1099
  this.templateRef = templateRef;
741
1100
  this.viewContainer = viewContainer;
742
- this.hasView = signal(false, ...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
1101
+ this.hasView = signal(false, /* @ts-ignore */
1102
+ ...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
743
1103
  this.sessionService = inject(AXPSessionService);
744
1104
  }
745
1105
  set permission(permissionKeys) {
@@ -774,10 +1134,10 @@ class AXPPermissionDirective {
774
1134
  ngOnDestroy() {
775
1135
  this.subscription?.unsubscribe();
776
1136
  }
777
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
778
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AXPPermissionDirective, isStandalone: false, selector: "[permission]", inputs: { permission: "permission", permissionElse: "permissionElse" }, ngImport: i0 }); }
1137
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1138
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: AXPPermissionDirective, isStandalone: false, selector: "[permission]", inputs: { permission: "permission", permissionElse: "permissionElse" }, ngImport: i0 }); }
779
1139
  }
780
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDirective, decorators: [{
1140
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDirective, decorators: [{
781
1141
  type: Directive,
782
1142
  args: [{
783
1143
  selector: '[permission]',
@@ -804,26 +1164,37 @@ const AXPPermissionGuard = (route, state) => {
804
1164
  }));
805
1165
  };
806
1166
 
1167
+ //#endregion
1168
+ //#region ----- Context -----
807
1169
  class AXPPermissionDefinitionProviderContext {
808
1170
  constructor() {
809
1171
  this.builders = new Map();
810
1172
  }
811
- addGroup(name, title, description) {
1173
+ addGroup(name, title, description, options) {
812
1174
  if (this.isGroupDefined(name)) {
813
- return this.findGroup(name);
1175
+ const existing = this.findGroup(name);
1176
+ if (options?.parent && !existing.group.parent) {
1177
+ existing.group.parent = options.parent;
1178
+ }
1179
+ if (options?.order != null && existing.group.order == null) {
1180
+ existing.group.order = options.order;
1181
+ }
1182
+ return existing;
814
1183
  }
815
1184
  const group = {
816
1185
  name,
817
1186
  title,
818
1187
  description,
819
- permissions: []
1188
+ parent: options?.parent,
1189
+ order: options?.order,
1190
+ permissions: [],
820
1191
  };
821
1192
  const builder = new AXPPermissionDefinitionGroupBuilder(this, group);
822
1193
  this.builders.set(name, builder);
823
1194
  return builder;
824
1195
  }
825
1196
  getGroupDefinitions() {
826
- return Array.from(this.builders.values()).map(b => b.group);
1197
+ return Array.from(this.builders.values()).map((b) => b.group);
827
1198
  }
828
1199
  findGroup(name) {
829
1200
  return this.builders.get(name);
@@ -832,6 +1203,8 @@ class AXPPermissionDefinitionProviderContext {
832
1203
  return this.builders.has(name);
833
1204
  }
834
1205
  }
1206
+ //#endregion
1207
+ //#region ----- Group Builder -----
835
1208
  class AXPPermissionDefinitionGroupBuilder {
836
1209
  get group() {
837
1210
  return this._group;
@@ -840,13 +1213,20 @@ class AXPPermissionDefinitionGroupBuilder {
840
1213
  this.context = context;
841
1214
  this._group = group;
842
1215
  }
843
- addPermission(name, title, description, requiredFeatures) {
1216
+ addPermission(name, title, description, options) {
1217
+ const normalized = normalizePermissionAddOptions(options);
1218
+ const existing = this._group.permissions.find((p) => p.name === name);
1219
+ if (existing) {
1220
+ mergePermissionOptions(existing, normalized);
1221
+ return new AXPPermissionDefinitionBuilder(this, existing);
1222
+ }
844
1223
  const permission = {
845
1224
  name,
846
1225
  title,
847
1226
  description,
848
1227
  children: [],
849
- requiredFeatures
1228
+ requiredFeatures: normalized.requiredFeatures,
1229
+ modules: normalized.modules,
850
1230
  };
851
1231
  this._group.permissions.push(permission);
852
1232
  return new AXPPermissionDefinitionBuilder(this, permission);
@@ -854,25 +1234,34 @@ class AXPPermissionDefinitionGroupBuilder {
854
1234
  endGroup() {
855
1235
  return this.context;
856
1236
  }
857
- findPermission(path) {
1237
+ findPermission(_path) {
858
1238
  return undefined;
859
1239
  }
860
- findGroup(name) {
1240
+ findGroup(_name) {
861
1241
  return undefined;
862
1242
  }
863
1243
  }
1244
+ //#endregion
1245
+ //#region ----- Permission Builder -----
864
1246
  class AXPPermissionDefinitionBuilder {
865
1247
  constructor(groupBuilder, permission) {
866
1248
  this.groupBuilder = groupBuilder;
867
1249
  this.permission = permission;
868
1250
  }
869
- addChild(name, title, description, requiredFeatures) {
1251
+ addChild(name, title, description, options) {
1252
+ const normalized = normalizePermissionAddOptions(options);
1253
+ const existing = this.permission.children.find((p) => p.name === name);
1254
+ if (existing) {
1255
+ mergePermissionOptions(existing, normalized);
1256
+ return this;
1257
+ }
870
1258
  const permission = {
871
1259
  name,
872
1260
  title,
873
1261
  description,
874
1262
  children: [],
875
- requiredFeatures
1263
+ requiredFeatures: normalized.requiredFeatures,
1264
+ modules: normalized.modules,
876
1265
  };
877
1266
  this.permission.children.push(permission);
878
1267
  return this;
@@ -885,83 +1274,142 @@ class AXPPermissionDefinitionBuilder {
885
1274
  this.permission.requiredFeatures = features;
886
1275
  return this;
887
1276
  }
1277
+ /**
1278
+ * Set edition module ownership for this permission (ANY-match for availability).
1279
+ */
1280
+ modules(...moduleNames) {
1281
+ this.permission.modules = moduleNames;
1282
+ return this;
1283
+ }
888
1284
  endPermission() {
889
1285
  return this.groupBuilder;
890
1286
  }
891
1287
  }
1288
+ //#endregion
1289
+ //#region ----- Helpers -----
1290
+ function normalizePermissionAddOptions(options) {
1291
+ if (!options) {
1292
+ return {};
1293
+ }
1294
+ // Legacy: 4th argument was `requiredFeatures: string[]`
1295
+ if (Array.isArray(options)) {
1296
+ return { requiredFeatures: options };
1297
+ }
1298
+ return options;
1299
+ }
1300
+ function mergePermissionOptions(target, options) {
1301
+ if (options.requiredFeatures?.length) {
1302
+ const set = new Set([...(target.requiredFeatures ?? []), ...options.requiredFeatures]);
1303
+ target.requiredFeatures = Array.from(set);
1304
+ }
1305
+ if (options.modules?.length) {
1306
+ const set = new Set([...(target.modules ?? []), ...options.modules]);
1307
+ target.modules = Array.from(set);
1308
+ }
1309
+ }
1310
+ //#endregion
892
1311
 
893
1312
  const AXP_PERMISSION_DEFINITION_PROVIDER = new InjectionToken('AXP_PERMISSION_DEFINITION_PROVIDER', {
894
1313
  providedIn: 'root',
895
1314
  factory: () => {
896
1315
  return [];
897
- }
1316
+ },
898
1317
  });
1318
+ const AXP_PERMISSION_MODULES_RESOLVER = new InjectionToken('AXP_PERMISSION_MODULES_RESOLVER');
899
1319
  class AXPPermissionDefinitionService {
900
1320
  constructor() {
901
1321
  this.providers = inject(AXP_PERMISSION_DEFINITION_PROVIDER, { optional: true });
1322
+ this.modulesResolver = inject(AXP_PERMISSION_MODULES_RESOLVER, { optional: true });
902
1323
  this.sessionService = inject(AXPSessionService);
903
1324
  this.cache = null;
1325
+ /** Invalidates cache when the session edition modules/features change (e.g. app switch). */
1326
+ this.cacheAvailabilityKey = null;
1327
+ }
1328
+ buildAvailabilityKey() {
1329
+ const enabled = this.sessionService.features
1330
+ .filter((feature) => feature.value === true && !!feature.name)
1331
+ .map((feature) => feature.name)
1332
+ .sort();
1333
+ const applicationId = this.sessionService.application?.id ?? '';
1334
+ const tenantId = this.sessionService.tenant?.id ?? '';
1335
+ return `${tenantId}|${applicationId}|${enabled.join(',')}`;
904
1336
  }
905
1337
  async load() {
906
- if (this.cache) {
1338
+ const availabilityKey = this.buildAvailabilityKey();
1339
+ if (this.cache && this.cacheAvailabilityKey === availabilityKey) {
907
1340
  return;
908
1341
  }
1342
+ this.cacheAvailabilityKey = availabilityKey;
909
1343
  const context = new AXPPermissionDefinitionProviderContext();
910
- // Load providers from DI tokens
911
1344
  if (Array.isArray(this.providers)) {
912
1345
  for (const provider of this.providers) {
913
1346
  if (provider instanceof Promise) {
914
- // If provider is a promise, resolve it
915
1347
  const resolvedProvider = await provider;
916
1348
  await resolvedProvider.define(context);
917
1349
  }
918
1350
  else {
919
- // If provider is a direct instance, use it directly
920
1351
  await provider.define(context);
921
1352
  }
922
1353
  }
923
1354
  }
924
- // Filter permissions based on required features
925
- const allGroups = context.getGroupDefinitions();
926
- this.cache = this.filterByFeatures(allGroups);
1355
+ const allGroups = context.getGroupDefinitions().map((group) => this.enrichGroupModules(group));
1356
+ this.cache = this.filterUnavailablePermissions(allGroups);
927
1357
  }
928
- /**
929
- * Filter permissions based on required features.
930
- * Removes permissions that have required features that are not enabled.
931
- */
932
- filterByFeatures(groups) {
933
- return groups.map(group => ({
1358
+ /** Attach modules[] from the product resolver when providers omitted them. */
1359
+ enrichGroupModules(group) {
1360
+ return {
934
1361
  ...group,
935
- permissions: this.filterPermissions(group.permissions)
936
- })).filter(group => group.permissions.length > 0);
1362
+ permissions: group.permissions.map((permission) => this.enrichPermissionModules(permission)),
1363
+ };
1364
+ }
1365
+ enrichPermissionModules(permission) {
1366
+ const resolved = permission.modules?.length || !this.modulesResolver
1367
+ ? permission.modules
1368
+ : [...this.modulesResolver(permission.name)];
1369
+ return {
1370
+ ...permission,
1371
+ modules: resolved,
1372
+ children: (permission.children ?? []).map((child) => this.enrichPermissionModules(child)),
1373
+ };
937
1374
  }
938
1375
  /**
939
- * Recursively filter permissions and their children based on required features.
1376
+ * Filter permissions by edition modules (ANY-match) and required features.
1377
+ * Keeps parent groups that still have at least one child section with permissions.
940
1378
  */
1379
+ filterUnavailablePermissions(groups) {
1380
+ const filtered = groups.map((group) => ({
1381
+ ...group,
1382
+ permissions: this.filterPermissions(group.permissions),
1383
+ }));
1384
+ const withPermissions = new Set(filtered.filter((g) => g.permissions.length > 0).map((g) => g.name));
1385
+ const parentsOfKept = new Set(filtered.filter((g) => withPermissions.has(g.name) && g.parent).map((g) => g.parent));
1386
+ return filtered.filter((g) => withPermissions.has(g.name) || parentsOfKept.has(g.name));
1387
+ }
941
1388
  filterPermissions(permissions) {
942
1389
  return permissions
943
- .filter(permission => {
944
- // Check if required features are enabled
1390
+ .filter((permission) => {
1391
+ if (permission.modules?.length) {
1392
+ const anyModuleEnabled = permission.modules.some((moduleName) => this.sessionService.isModuleEnabled(moduleName));
1393
+ if (!anyModuleEnabled) {
1394
+ return false;
1395
+ }
1396
+ }
945
1397
  if (permission.requiredFeatures && permission.requiredFeatures.length > 0) {
946
- const allFeaturesEnabled = permission.requiredFeatures.every(featureName => this.sessionService.isFeatureEnabled(featureName));
1398
+ const allFeaturesEnabled = permission.requiredFeatures.every((featureName) => this.sessionService.isFeatureEnabled(featureName));
947
1399
  if (!allFeaturesEnabled) {
948
- return false; // Filter out this permission
1400
+ return false;
949
1401
  }
950
1402
  }
951
1403
  return true;
952
1404
  })
953
- .map(permission => ({
1405
+ .map((permission) => ({
954
1406
  ...permission,
955
- children: this.filterPermissions(permission.children)
956
- }))
957
- .filter(permission => {
958
- // Remove permissions that have no children if they were only containers
959
- // (This is optional - you might want to keep parent permissions even without children)
960
- return true;
961
- });
1407
+ children: this.filterPermissions(permission.children),
1408
+ }));
962
1409
  }
963
1410
  async reload() {
964
1411
  this.cache = null;
1412
+ this.cacheAvailabilityKey = null;
965
1413
  await this.load();
966
1414
  }
967
1415
  async getGroups() {
@@ -970,112 +1418,42 @@ class AXPPermissionDefinitionService {
970
1418
  }
971
1419
  async getPermissions() {
972
1420
  await this.load();
973
- return this.cache?.flatMap(g => g.permissions) ?? [];
1421
+ return this.cache?.flatMap((g) => g.permissions) ?? [];
974
1422
  }
975
1423
  async getGroup(name) {
976
1424
  await this.load();
977
- return this.cache?.find(g => g.name === name) ?? null;
1425
+ return this.cache?.find((g) => g.name === name) ?? null;
978
1426
  }
979
1427
  async getPermission(name) {
980
1428
  await this.load();
981
- return this.cache?.find(g => g.permissions.find(p => p.name === name))?.permissions.find(p => p.name === name) ?? null;
1429
+ for (const group of this.cache ?? []) {
1430
+ const found = this.findPermissionRecursive(group.permissions, name);
1431
+ if (found) {
1432
+ return found;
1433
+ }
1434
+ }
1435
+ return null;
982
1436
  }
983
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDefinitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
984
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDefinitionService, providedIn: 'root' }); }
1437
+ findPermissionRecursive(permissions, name) {
1438
+ for (const permission of permissions) {
1439
+ if (permission.name === name) {
1440
+ return permission;
1441
+ }
1442
+ const child = this.findPermissionRecursive(permission.children ?? [], name);
1443
+ if (child) {
1444
+ return child;
1445
+ }
1446
+ }
1447
+ return null;
1448
+ }
1449
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDefinitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1450
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDefinitionService, providedIn: 'root' }); }
985
1451
  }
986
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDefinitionService, decorators: [{
1452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPermissionDefinitionService, decorators: [{
987
1453
  type: Injectable,
988
1454
  args: [{ providedIn: 'root' }]
989
1455
  }] });
990
1456
 
991
- //#region ---- Imports ----
992
- //#endregion
993
- //#region ---- Helpers ----
994
- function flattenPermissionDefinitions(nodes, groupPrefix) {
995
- const rows = [];
996
- for (const node of nodes) {
997
- const displayTitle = groupPrefix ? `${groupPrefix} / ${node.title}` : node.title;
998
- if (node.children?.length) {
999
- rows.push(...flattenPermissionDefinitions(node.children, displayTitle));
1000
- }
1001
- else {
1002
- rows.push({
1003
- id: String(node.name),
1004
- title: displayTitle,
1005
- });
1006
- }
1007
- }
1008
- return rows;
1009
- }
1010
- //#endregion
1011
- //#region ---- Permission definitions data source ----
1012
- /**
1013
- * Data source definition for leaf permissions from {@link AXPPermissionDefinitionService#getGroups}.
1014
- */
1015
- class AXPPermissionDefinitionsDataSourceDefinition {
1016
- constructor() {
1017
- //#region ---- Services & Dependencies ----
1018
- this.permissionDefinitionService = inject(AXPPermissionDefinitionService);
1019
- //#endregion
1020
- }
1021
- //#endregion
1022
- //#region ---- Public API ----
1023
- async items() {
1024
- return [
1025
- {
1026
- name: AXPPlatformAuthDataSourceKeys.PermissionDefinitions,
1027
- title: 'Permissions',
1028
- source: () => new AXDataSource({
1029
- key: 'id',
1030
- load: async () => {
1031
- const groups = await this.permissionDefinitionService.getGroups();
1032
- const list = [];
1033
- for (const g of groups) {
1034
- list.push(...flattenPermissionDefinitions(g.permissions, g.title));
1035
- }
1036
- return { items: list, total: list.length };
1037
- },
1038
- byKey: async (key) => {
1039
- const groups = await this.permissionDefinitionService.getGroups();
1040
- const list = [];
1041
- for (const g of groups) {
1042
- list.push(...flattenPermissionDefinitions(g.permissions, g.title));
1043
- }
1044
- return list.find((item) => item.id === key);
1045
- },
1046
- pageSize: 1000,
1047
- }),
1048
- columns: [
1049
- {
1050
- name: 'id',
1051
- title: 'ID',
1052
- datatype: 'string',
1053
- type: AXPWidgetsCatalog.text,
1054
- },
1055
- {
1056
- name: 'title',
1057
- title: 'Title',
1058
- datatype: 'string',
1059
- type: AXPWidgetsCatalog.text,
1060
- },
1061
- ],
1062
- filters: [
1063
- {
1064
- field: 'title',
1065
- title: 'Title',
1066
- operator: { type: 'equal' },
1067
- widget: { type: AXPWidgetsCatalog.text },
1068
- filterType: { advance: true, inline: true },
1069
- },
1070
- ],
1071
- textField: { name: 'title', title: 'Title' },
1072
- valueField: { name: 'id', title: 'ID' },
1073
- },
1074
- ];
1075
- }
1076
- }
1077
- //#endregion
1078
-
1079
1457
  class AXPPermissionEvaluatorScopeProvider {
1080
1458
  constructor() {
1081
1459
  this.sessionService = inject(AXPSessionService);
@@ -1114,16 +1492,6 @@ const AXPAuthGuard = (route, state) => {
1114
1492
  }));
1115
1493
  };
1116
1494
 
1117
- function initializeAppState(service) {
1118
- return async () => {
1119
- try {
1120
- await service.restoreSession();
1121
- }
1122
- catch (error) {
1123
- console.error(error);
1124
- }
1125
- };
1126
- }
1127
1495
  class AXPAuthModule {
1128
1496
  static forRoot(configs) {
1129
1497
  return {
@@ -1165,34 +1533,76 @@ class AXPAuthModule {
1165
1533
  f();
1166
1534
  });
1167
1535
  }
1168
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthModule, deps: [{ token: 'AXPAuthModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
1169
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthModule, declarations: [AXPPermissionDirective, AXPFeatureDirective], exports: [AXPPermissionDirective, AXPFeatureDirective] }); }
1170
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthModule, providers: [
1171
- provideAppInitializer(() => {
1172
- const initializerFn = initializeAppState(inject(AXPSessionService));
1173
- return initializerFn();
1536
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, deps: [{ token: 'AXPAuthModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
1537
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, declarations: [AXPPermissionDirective, AXPFeatureDirective], exports: [AXPPermissionDirective, AXPFeatureDirective] }); }
1538
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, providers: [
1539
+ provideAppStartUpTask(() => {
1540
+ const service = inject(AXPSessionService);
1541
+ return {
1542
+ name: 'session-restore',
1543
+ priority: AXP_APP_STARTUP_PRIORITY.sessionRestore,
1544
+ run: async () => {
1545
+ try {
1546
+ await service.restoreSession();
1547
+ }
1548
+ catch (error) {
1549
+ console.error(error);
1550
+ }
1551
+ },
1552
+ };
1174
1553
  }),
1175
1554
  {
1176
1555
  provide: AXP_SESSION_SERVICE,
1177
1556
  useExisting: AXPSessionService,
1178
1557
  },
1558
+ {
1559
+ provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
1560
+ useClass: AXPFeatureEvaluatorScopeProvider,
1561
+ multi: true,
1562
+ },
1563
+ {
1564
+ provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
1565
+ useClass: AXPPermissionEvaluatorScopeProvider,
1566
+ multi: true,
1567
+ },
1179
1568
  ] }); }
1180
1569
  }
1181
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthModule, decorators: [{
1570
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPAuthModule, decorators: [{
1182
1571
  type: NgModule,
1183
1572
  args: [{
1184
1573
  imports: [],
1185
1574
  exports: [AXPPermissionDirective, AXPFeatureDirective],
1186
1575
  declarations: [AXPPermissionDirective, AXPFeatureDirective],
1187
1576
  providers: [
1188
- provideAppInitializer(() => {
1189
- const initializerFn = initializeAppState(inject(AXPSessionService));
1190
- return initializerFn();
1577
+ provideAppStartUpTask(() => {
1578
+ const service = inject(AXPSessionService);
1579
+ return {
1580
+ name: 'session-restore',
1581
+ priority: AXP_APP_STARTUP_PRIORITY.sessionRestore,
1582
+ run: async () => {
1583
+ try {
1584
+ await service.restoreSession();
1585
+ }
1586
+ catch (error) {
1587
+ console.error(error);
1588
+ }
1589
+ },
1590
+ };
1191
1591
  }),
1192
1592
  {
1193
1593
  provide: AXP_SESSION_SERVICE,
1194
1594
  useExisting: AXPSessionService,
1195
1595
  },
1596
+ {
1597
+ provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
1598
+ useClass: AXPFeatureEvaluatorScopeProvider,
1599
+ multi: true,
1600
+ },
1601
+ {
1602
+ provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
1603
+ useClass: AXPPermissionEvaluatorScopeProvider,
1604
+ multi: true,
1605
+ },
1196
1606
  ],
1197
1607
  }]
1198
1608
  }], ctorParameters: () => [{ type: undefined, decorators: [{
@@ -1204,6 +1614,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1204
1614
 
1205
1615
  class AXPAuthStrategy {
1206
1616
  }
1617
+ //#endregion
1207
1618
 
1208
1619
  //#region ---- JWT Utility ----
1209
1620
  /**
@@ -1292,40 +1703,13 @@ function platformOAuthSessionUpdateParams(params) {
1292
1703
  applicationId: params['applicationId'] ?? application?.id,
1293
1704
  };
1294
1705
  }
1295
- function shouldPlatformOAuthBindSession(accessToken, params) {
1296
- if (!accessToken) {
1706
+ function shouldPlatformOAuthBindSession(hasCredentials, params) {
1707
+ if (!hasCredentials) {
1297
1708
  return false;
1298
1709
  }
1299
1710
  const { tenant, application, tenantId, applicationId } = platformOAuthSessionUpdateParams(params);
1300
1711
  return Boolean(tenantId || applicationId || tenant || application);
1301
1712
  }
1302
- async function postPlatformOAuthSessionUpdate(baseUrl, accessToken, params) {
1303
- const { tenant, application, tenantId, applicationId } = platformOAuthSessionUpdateParams(params);
1304
- const response = await fetch(`${baseUrl.replace(/\/$/, '')}/v1/auth/session`, {
1305
- method: 'POST',
1306
- headers: {
1307
- 'Content-Type': 'application/json',
1308
- Authorization: `Bearer ${accessToken}`,
1309
- },
1310
- credentials: 'include',
1311
- body: JSON.stringify({
1312
- tenantId,
1313
- applicationId,
1314
- tenant,
1315
- application,
1316
- }),
1317
- });
1318
- if (!response.ok) {
1319
- const detail = await response.text().catch(() => '');
1320
- throw new Error(`Session update failed: ${response.status} ${response.statusText}${detail ? ` — ${detail}` : ''}`);
1321
- }
1322
- const result = (await response.json());
1323
- return toPlatformOAuthSignInResult(result, {
1324
- user: params['user'] ?? null,
1325
- tenant: tenant ?? null,
1326
- application: application ?? null,
1327
- });
1328
- }
1329
1713
  function toPlatformOAuthSignInResult(result, context) {
1330
1714
  const tenant = mergeTenant(mapPlatformTenant(result.tenant), context?.tenant ?? null);
1331
1715
  const application = mergeApplication(mapPlatformApplication(result.application), context?.application ?? null);
@@ -1408,6 +1792,137 @@ function parsePlatformExpiresIn(expiresIn) {
1408
1792
  }
1409
1793
  //#endregion
1410
1794
 
1795
+ //#region ---- Platform OAuth helpers (PKCE + /connect/*) ----
1796
+ const AXP_DEFAULT_PLATFORM_OAUTH_CLIENT_ID = 'acorex';
1797
+ const AXP_DEFAULT_PLATFORM_OAUTH_SCOPE = 'openid profile email offline_access';
1798
+ function resolvePlatformOAuthIssuer(baseUrl) {
1799
+ const issuer = baseUrl?.replace(/\/$/, '');
1800
+ if (!issuer) {
1801
+ throw new Error('API baseUrl is not configured');
1802
+ }
1803
+ return issuer;
1804
+ }
1805
+ function resolvePlatformOAuthRedirectUri() {
1806
+ return `${window.location.origin}/auth/oauth-callback`;
1807
+ }
1808
+ function resolvePlatformOAuthClientConfig(authConfig) {
1809
+ return {
1810
+ clientId: authConfig?.clientId ?? authConfig?.client_id ?? AXP_DEFAULT_PLATFORM_OAUTH_CLIENT_ID,
1811
+ redirectUri: authConfig?.redirectUri ?? resolvePlatformOAuthRedirectUri(),
1812
+ scope: authConfig?.scope ?? AXP_DEFAULT_PLATFORM_OAUTH_SCOPE,
1813
+ };
1814
+ }
1815
+ async function fetchPlatformOAuthDiscoveryDocument(issuer) {
1816
+ const response = await fetch(`${issuer}/.well-known/openid-configuration`);
1817
+ if (!response.ok) {
1818
+ throw new Error(`Failed to load OpenID discovery document: ${response.status} ${response.statusText}`);
1819
+ }
1820
+ return (await response.json());
1821
+ }
1822
+ function resolvePlatformOAuthAuthorizeEndpoint(issuer, discovery) {
1823
+ return discovery?.authorization_endpoint ?? `${issuer}/connect/authorize`;
1824
+ }
1825
+ function resolvePlatformOAuthTokenEndpoint(issuer, discovery) {
1826
+ return discovery?.token_endpoint ?? `${issuer}/connect/token`;
1827
+ }
1828
+ //#endregion
1829
+
1830
+ //#region ---- Auth:ListProviders public DTOs ----
1831
+ function asAuthListProvidersResponse(payload) {
1832
+ const data = (payload ?? {});
1833
+ return {
1834
+ localMethods: Array.isArray(data.localMethods) ? data.localMethods : [],
1835
+ identityProviders: Array.isArray(data.identityProviders) ? data.identityProviders : [],
1836
+ };
1837
+ }
1838
+ function findIdentityProvider(payload, id) {
1839
+ const { identityProviders } = asAuthListProvidersResponse(payload);
1840
+ return identityProviders.find((p) => p.id === id);
1841
+ }
1842
+ //#endregion
1843
+
1844
+ /**
1845
+ * Best-effort plain string for thrown {@link Error} messages from command results (no i18n lookup).
1846
+ */
1847
+ function commandMessageTextForError(value) {
1848
+ if (value == null) {
1849
+ return '';
1850
+ }
1851
+ if (typeof value === 'string') {
1852
+ return value;
1853
+ }
1854
+ if (typeof value === 'object' && !Array.isArray(value)) {
1855
+ const values = Object.values(value);
1856
+ const first = values.find((v) => typeof v === 'string' && v.length > 0);
1857
+ return first ?? '';
1858
+ }
1859
+ return String(value);
1860
+ }
1861
+
1862
+ class AXPPlatformAuthRuntimeService {
1863
+ constructor() {
1864
+ this.commandService = inject(AXPCommandService);
1865
+ this.queryService = inject(AXPQueryService);
1866
+ this.sessionService = inject(AXPSessionService);
1867
+ this.listProvidersCache = null;
1868
+ this.listProvidersInFlight = null;
1869
+ }
1870
+ async logout(_refreshToken) {
1871
+ try {
1872
+ // Refresh token is read from HttpOnly cookie on the server.
1873
+ await this.commandService.execute(AUTH_LOGOUT_DEFAULT, {});
1874
+ }
1875
+ catch {
1876
+ // Ignore logout network errors; client session is cleared regardless.
1877
+ }
1878
+ finally {
1879
+ this.clearListProvidersCache();
1880
+ }
1881
+ }
1882
+ async refresh(_refreshToken) {
1883
+ return this.executeCommand(AUTH_REFRESH_DEFAULT, {});
1884
+ }
1885
+ async updateSession(input) {
1886
+ return this.executeCommand(AUTH_UPDATE_SESSION_DEFAULT, input);
1887
+ }
1888
+ async listProviders() {
1889
+ if (this.listProvidersCache) {
1890
+ return this.listProvidersCache;
1891
+ }
1892
+ if (this.listProvidersInFlight) {
1893
+ return this.listProvidersInFlight;
1894
+ }
1895
+ this.listProvidersInFlight = this.queryService
1896
+ .fetch(AUTH_LIST_PROVIDERS_DEFAULT, {})
1897
+ .then((result) => {
1898
+ this.listProvidersCache = asAuthListProvidersResponse(runtimeQueryPayloadData(result));
1899
+ return this.listProvidersCache;
1900
+ })
1901
+ .finally(() => {
1902
+ this.listProvidersInFlight = null;
1903
+ });
1904
+ return this.listProvidersInFlight;
1905
+ }
1906
+ clearListProvidersCache() {
1907
+ this.listProvidersCache = null;
1908
+ this.listProvidersInFlight = null;
1909
+ }
1910
+ async executeCommand(key, input) {
1911
+ const result = await this.commandService.execute(key, input);
1912
+ const data = runtimeCommandPayloadData(result);
1913
+ if (data == null) {
1914
+ throw new Error(commandMessageTextForError(result?.message?.text) || `Auth command failed: ${key}`);
1915
+ }
1916
+ return data;
1917
+ }
1918
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPlatformAuthRuntimeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1919
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPlatformAuthRuntimeService, providedIn: 'root' }); }
1920
+ }
1921
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPlatformAuthRuntimeService, decorators: [{
1922
+ type: Injectable,
1923
+ args: [{ providedIn: 'root' }]
1924
+ }] });
1925
+
1411
1926
  const AXP_USER_AVATAR_CACHE_PORT = new InjectionToken('AXP_USER_AVATAR_CACHE_PORT');
1412
1927
 
1413
1928
  //#region ---- User profile identity service ----
@@ -1442,10 +1957,10 @@ class AXPUserProfileIdentityService {
1442
1957
  }
1443
1958
  await this.avatarCachePort?.refreshUserInfo(id);
1444
1959
  }
1445
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPUserProfileIdentityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1446
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPUserProfileIdentityService, providedIn: 'root' }); }
1960
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPUserProfileIdentityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1961
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPUserProfileIdentityService, providedIn: 'root' }); }
1447
1962
  }
1448
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPUserProfileIdentityService, decorators: [{
1963
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPUserProfileIdentityService, decorators: [{
1449
1964
  type: Injectable,
1450
1965
  args: [{ providedIn: 'root' }]
1451
1966
  }] });
@@ -1581,11 +2096,13 @@ class AXPLoginChallengeComponentBase {
1581
2096
  * Challenge data to display
1582
2097
  * Contains the image/content and metadata from the server
1583
2098
  */
1584
- this.challengeData = input.required(...(ngDevMode ? [{ debugName: "challengeData" }] : /* istanbul ignore next */ []));
2099
+ this.challengeData = input.required(/* @ts-ignore */
2100
+ ...(ngDevMode ? [{ debugName: "challengeData" }] : /* istanbul ignore next */ []));
1585
2101
  /**
1586
2102
  * Whether the challenge is currently loading (e.g., refreshing)
1587
2103
  */
1588
- this.isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
2104
+ this.isLoading = input(false, /* @ts-ignore */
2105
+ ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
1589
2106
  //#endregion
1590
2107
  //#region ---- Outputs ----
1591
2108
  /**
@@ -1599,20 +2116,61 @@ class AXPLoginChallengeComponentBase {
1599
2116
  */
1600
2117
  this.refreshRequest = output();
1601
2118
  }
1602
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLoginChallengeComponentBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1603
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: AXPLoginChallengeComponentBase, isStandalone: true, selector: "ng-component", inputs: { challengeData: { classPropertyName: "challengeData", publicName: "challengeData", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { responseChange: "responseChange", refreshRequest: "refreshRequest" }, ngImport: i0, template: '', isInline: true }); }
2119
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLoginChallengeComponentBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2120
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXPLoginChallengeComponentBase, isStandalone: true, selector: "ng-component", inputs: { challengeData: { classPropertyName: "challengeData", publicName: "challengeData", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { responseChange: "responseChange", refreshRequest: "refreshRequest" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.Eager }); }
1604
2121
  }
1605
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLoginChallengeComponentBase, decorators: [{
2122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPLoginChallengeComponentBase, decorators: [{
1606
2123
  type: Component,
1607
2124
  args: [{
1608
2125
  template: '',
2126
+ changeDetection: ChangeDetectionStrategy.Eager,
1609
2127
  standalone: true,
1610
2128
  }]
1611
2129
  }], propDecorators: { challengeData: [{ type: i0.Input, args: [{ isSignal: true, alias: "challengeData", required: true }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], responseChange: [{ type: i0.Output, args: ["responseChange"] }], refreshRequest: [{ type: i0.Output, args: ["refreshRequest"] }] } });
1612
2130
 
2131
+ //#region ----- Sign-in method types -----
2132
+ /**
2133
+ * Pluggable sign-in method. Multiple methods can be registered and offered at once.
2134
+ * Session remember/refresh live in {@link AXPSessionService}; methods only prove identity.
2135
+ */
2136
+ class AXPSignInMethod {
2137
+ }
2138
+ //#endregion
2139
+
2140
+ //#region ----- Sign-in method registry -----
2141
+ /**
2142
+ * Registry of enabled sign-in methods for the login UI.
2143
+ * Methods are independent of session refresh/remember (owned by session service).
2144
+ */
2145
+ class AXPSignInMethodRegistry {
2146
+ constructor() {
2147
+ this.methods = new Map();
2148
+ }
2149
+ register(...methods) {
2150
+ for (const method of methods) {
2151
+ this.methods.set(method.descriptor.id, method);
2152
+ }
2153
+ }
2154
+ get(id) {
2155
+ return this.methods.get(id);
2156
+ }
2157
+ listEnabled() {
2158
+ return [...this.methods.values()]
2159
+ .map((m) => m.descriptor)
2160
+ .filter((d) => d.enabled)
2161
+ .sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
2162
+ }
2163
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSignInMethodRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2164
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSignInMethodRegistry, providedIn: 'root' }); }
2165
+ }
2166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPSignInMethodRegistry, decorators: [{
2167
+ type: Injectable,
2168
+ args: [{ providedIn: 'root' }]
2169
+ }] });
2170
+
1613
2171
  /**
1614
2172
  * Generated bundle index. Do not edit.
1615
2173
  */
1616
2174
 
1617
- export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPLoginChallengeComponentBase, AXPLoginChallengeProvider, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDefinitionsDataSourceDefinition, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPPlatformAuthDataSourceKeys, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, AXPUserProfileIdentityService, AXP_ACCESS_POLICY_CACHE_PORT, AXP_APPLICATION_LOADER, AXP_FEATURE_CHECKER, AXP_FEATURE_LOADER, AXP_LOGIN_CHALLENGE_PROVIDER, AXP_PERMISSION_CHECKER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_TENANT_LOADER, AXP_USER_AVATAR_CACHE_PORT, JwtUtil, PkceUtil, TimeUtil, initializeAppState, platformOAuthSessionUpdateParams, postPlatformOAuthSessionUpdate, shouldPlatformOAuthBindSession, toPlatformOAuthSignInResult };
2175
+ export { AUTH_SESSION_TRACE_NS, AUTH_SESSION_TRACE_TITLE, AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureEvaluatorScopeProvider, AXPFeatureGuard, AXPLoginChallengeComponentBase, AXPLoginChallengeProvider, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPPlatformAuthRuntimeService, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPSignInMethod, AXPSignInMethodRegistry, AXPUnauthenticatedError, AXPUnauthorizedError, AXPUserProfileIdentityService, AXP_ACCESS_POLICY_CACHE_PORT, AXP_APPLICATION_LOADER, AXP_DEFAULT_PLATFORM_OAUTH_CLIENT_ID, AXP_DEFAULT_PLATFORM_OAUTH_SCOPE, AXP_FEATURE_CHECKER, AXP_FEATURE_LOADER, AXP_LOGIN_CHALLENGE_PROVIDER, AXP_OAUTH_PENDING_STRATEGY_KEY, AXP_OAUTH_PKCE_VERIFIER_KEY, AXP_PERMISSION_CHECKER, AXP_PERMISSION_DEFINITION_PROVIDER, AXP_PERMISSION_LOADER, AXP_PERMISSION_MODULES_RESOLVER, AXP_TENANT_LOADER, AXP_USER_AVATAR_CACHE_PORT, JwtUtil, PkceUtil, TimeUtil, asAuthListProvidersResponse, clearOAuthPendingStrategy, commandMessageTextForError, fetchPlatformOAuthDiscoveryDocument, findIdentityProvider, getOAuthPendingStrategy, isOAuthRedirectInFlight, platformOAuthSessionUpdateParams, resolvePlatformOAuthAuthorizeEndpoint, resolvePlatformOAuthClientConfig, resolvePlatformOAuthIssuer, resolvePlatformOAuthRedirectUri, resolvePlatformOAuthTokenEndpoint, setOAuthPendingStrategy, shouldPlatformOAuthBindSession, toPlatformOAuthSignInResult, traceAuthAccessPolicy, traceAuthCatalog, traceAuthGuard, traceAuthLifecycle, traceAuthLifecycleError, traceAuthRestore, traceAuthWorkspace };
1618
2176
  //# sourceMappingURL=acorex-platform-framework-client-auth.mjs.map