@acorex-platform/framework-client 0.0.10

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 (112) hide show
  1. package/README.md +7 -0
  2. package/auth/README.md +3 -0
  3. package/common/README.md +3 -0
  4. package/core/README.md +4 -0
  5. package/fesm2022/acorex-platform-framework-client-auth.mjs +1383 -0
  6. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs +190 -0
  8. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-framework-client-common.mjs +9467 -0
  10. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-framework-client-core.mjs +4655 -0
  12. package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-framework-client-domain.mjs +3189 -0
  14. package/fesm2022/acorex-platform-framework-client-domain.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +3439 -0
  16. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs +134 -0
  18. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +9777 -0
  20. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +2799 -0
  22. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs +376 -0
  24. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs +100 -0
  26. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +26299 -0
  28. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-framework-client-layout-views.mjs +2960 -0
  30. package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +4711 -0
  32. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs +72 -0
  34. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs +29 -0
  36. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs +172 -0
  38. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs.map +1 -0
  39. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs +513 -0
  40. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs +274 -0
  42. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs.map +1 -0
  43. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs +64 -0
  44. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs.map +1 -0
  45. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs +34 -0
  46. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs.map +1 -0
  47. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +28553 -0
  48. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-framework-client-native.mjs +155 -0
  50. package/fesm2022/acorex-platform-framework-client-native.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-framework-client-runtime.mjs +911 -0
  52. package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs +160 -0
  54. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs +121 -0
  56. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs +236 -0
  58. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs +48 -0
  60. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs +42 -0
  62. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs.map +1 -0
  63. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  64. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +4160 -0
  66. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs +55 -0
  68. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs +57 -0
  70. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs +234 -0
  72. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs.map +1 -0
  73. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs +94 -0
  74. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs.map +1 -0
  75. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs +86 -0
  76. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs.map +1 -0
  77. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +2375 -0
  78. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -0
  79. package/fesm2022/acorex-platform-framework-client-workflow.mjs +2836 -0
  80. package/fesm2022/acorex-platform-framework-client-workflow.mjs.map +1 -0
  81. package/fesm2022/acorex-platform-framework-client.mjs +6 -0
  82. package/fesm2022/acorex-platform-framework-client.mjs.map +1 -0
  83. package/layout/builder/README.md +1578 -0
  84. package/layout/components/README.md +3 -0
  85. package/layout/designer/README.md +4 -0
  86. package/layout/entity/README.md +4 -0
  87. package/layout/views/README.md +3 -0
  88. package/layout/widget-core/README.md +4 -0
  89. package/layout/widgets/README.md +3 -0
  90. package/native/README.md +4 -0
  91. package/package.json +98 -0
  92. package/runtime/README.md +3 -0
  93. package/themes/default/README.md +3 -0
  94. package/themes/shared/README.md +3 -0
  95. package/types/acorex-platform-framework-client-auth.d.ts +690 -0
  96. package/types/acorex-platform-framework-client-common.d.ts +3287 -0
  97. package/types/acorex-platform-framework-client-core.d.ts +1849 -0
  98. package/types/acorex-platform-framework-client-domain.d.ts +1968 -0
  99. package/types/acorex-platform-framework-client-layout-builder.d.ts +1058 -0
  100. package/types/acorex-platform-framework-client-layout-components.d.ts +3362 -0
  101. package/types/acorex-platform-framework-client-layout-designer.d.ts +501 -0
  102. package/types/acorex-platform-framework-client-layout-entity.d.ts +4374 -0
  103. package/types/acorex-platform-framework-client-layout-views.d.ts +975 -0
  104. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +1080 -0
  105. package/types/acorex-platform-framework-client-layout-widgets.d.ts +5263 -0
  106. package/types/acorex-platform-framework-client-native.d.ts +28 -0
  107. package/types/acorex-platform-framework-client-runtime.d.ts +301 -0
  108. package/types/acorex-platform-framework-client-themes-default.d.ts +889 -0
  109. package/types/acorex-platform-framework-client-themes-shared.d.ts +202 -0
  110. package/types/acorex-platform-framework-client-workflow.d.ts +1345 -0
  111. package/types/acorex-platform-framework-client.d.ts +2 -0
  112. package/workflow/README.md +4 -0
@@ -0,0 +1,1383 @@
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 { AXPBroadcastEventService, AXP_SESSION_SERVICE } from '@acorex-platform/framework-client/core';
4
+ import { isEmpty } from 'lodash-es';
5
+ import { map, BehaviorSubject, shareReplay, defaultIfEmpty, switchMap, filter, from, first } from 'rxjs';
6
+ import { AXPWidgetsCatalog } from '@acorex-platform/framework-shared/core';
7
+ import { AXDataSource } from '@acorex/cdk/common';
8
+
9
+ const AXP_APPLICATION_LOADER = new InjectionToken('AXP_APPLICATION_LOADER', {
10
+ providedIn: 'root',
11
+ factory: () => {
12
+ return new AXPApplicationDefaultLoader();
13
+ },
14
+ });
15
+ class AXPApplicationDefaultLoader {
16
+ async getList(context) {
17
+ return [
18
+ {
19
+ id: '1',
20
+ name: 'default-app',
21
+ title: 'Default Application',
22
+ version: '1.0.0',
23
+ edition: {
24
+ id: 'default-edition-1',
25
+ title: 'Standard',
26
+ },
27
+ },
28
+ {
29
+ id: '2',
30
+ name: 'default-app',
31
+ title: 'Default Application',
32
+ version: '1.0.0',
33
+ edition: {
34
+ id: 'default-edition-2',
35
+ title: 'Standard',
36
+ },
37
+ },
38
+ ];
39
+ }
40
+ }
41
+
42
+ //#region ---- Datasource keys ----
43
+ /** Platform auth datasource registry names. */
44
+ const AXPPlatformAuthDataSourceKeys = {
45
+ PermissionDefinitions: 'platform-permission-definitions',
46
+ };
47
+ //#endregion
48
+
49
+ class AXPUnauthorizedError extends Error {
50
+ constructor(message, data) {
51
+ super(message);
52
+ this.data = data;
53
+ this.name = 'AXPUnauthorizedError';
54
+ }
55
+ }
56
+ class AXPUnauthenticatedError extends Error {
57
+ constructor(message, data) {
58
+ super(message);
59
+ this.data = data;
60
+ this.name = 'AXPUnauthenticatedError';
61
+ }
62
+ }
63
+
64
+ class AXPSessionContext {
65
+ get user() {
66
+ return this._user;
67
+ }
68
+ get tenant() {
69
+ return this._tenant;
70
+ }
71
+ get application() {
72
+ return this._application;
73
+ }
74
+ constructor(context) {
75
+ this._user = null;
76
+ this._tenant = null;
77
+ this._application = null;
78
+ this._user = context.user;
79
+ this._tenant = context.tenant;
80
+ this._application = context.application;
81
+ }
82
+ }
83
+ var AXPSessionStatus;
84
+ (function (AXPSessionStatus) {
85
+ AXPSessionStatus["Authenticated"] = "authenticated";
86
+ AXPSessionStatus["Unauthenticated"] = "unauthenticated";
87
+ AXPSessionStatus["Unauthorized"] = "unauthorized";
88
+ AXPSessionStatus["Authorized"] = "authorized";
89
+ AXPSessionStatus["Expired"] = "expired";
90
+ AXPSessionStatus["SignedOut"] = "signedOut";
91
+ })(AXPSessionStatus || (AXPSessionStatus = {}));
92
+
93
+ //#region ---- Contracts barrel ----
94
+ //#endregion
95
+
96
+ const AXP_TENANT_LOADER = new InjectionToken('AXP_TENANT_LOADER', {
97
+ providedIn: 'root',
98
+ factory: () => {
99
+ return new AXPTenantDefaultLoader();
100
+ },
101
+ });
102
+ class AXPTenantDefaultLoader {
103
+ async getList(context) {
104
+ return [
105
+ {
106
+ id: '1',
107
+ name: 'default-tenant',
108
+ title: 'Default Tenant',
109
+ },
110
+ ];
111
+ }
112
+ }
113
+
114
+ class AXPAuthStrategyRegistryService {
115
+ constructor(injector) {
116
+ this.strategies = new Map();
117
+ this.injector = injector;
118
+ }
119
+ register(...plugins) {
120
+ plugins.forEach(t => {
121
+ const childInjector = Injector.create({ providers: [{ provide: t, useClass: t, deps: [] }], parent: this.injector });
122
+ const strategy = childInjector.get(t);
123
+ if (strategy) {
124
+ this.strategies.set(strategy.name, strategy);
125
+ }
126
+ });
127
+ }
128
+ get(strategyKey) {
129
+ return this.strategies.get(strategyKey);
130
+ }
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' }); }
133
+ }
134
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthStrategyRegistryService, decorators: [{
135
+ type: Injectable,
136
+ args: [{
137
+ providedIn: 'root'
138
+ }]
139
+ }], ctorParameters: () => [{ type: i0.Injector }] });
140
+
141
+ const AXP_FEATURE_LOADER = new InjectionToken('AXP_FEATURE_LOADER', {
142
+ providedIn: 'root',
143
+ factory: () => {
144
+ return new AXPFeatureDefaultLoader();
145
+ },
146
+ });
147
+ class AXPFeatureDefaultLoader {
148
+ async getList(context) {
149
+ return [];
150
+ }
151
+ }
152
+
153
+ class AXPFeatureDirective {
154
+ constructor(templateRef, viewContainer) {
155
+ this.templateRef = templateRef;
156
+ this.viewContainer = viewContainer;
157
+ this.hasView = signal(false, ...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
158
+ this.sessionService = inject(AXPSessionService);
159
+ }
160
+ set feature(featureKeys) {
161
+ const keys = !featureKeys ? [] : (Array.isArray(featureKeys) ? featureKeys : [featureKeys]);
162
+ if (keys.length == 0) {
163
+ // If featureKey is null or empty, decide the default behavior here
164
+ this.viewContainer.createEmbeddedView(this.templateRef);
165
+ this.hasView.set(true);
166
+ return;
167
+ }
168
+ this.subscription = this.sessionService.features$
169
+ //.pipe(first())
170
+ .subscribe(() => {
171
+ if (this.sessionService.isFeatureEnabled(...keys)) {
172
+ if (!this.hasView()) {
173
+ this.viewContainer.createEmbeddedView(this.templateRef);
174
+ this.hasView.set(true);
175
+ }
176
+ }
177
+ else {
178
+ this.viewContainer.clear();
179
+ this.hasView.set(false);
180
+ }
181
+ });
182
+ }
183
+ set featureElse(elseTemplateRef) {
184
+ if (!this.hasView()) {
185
+ this.viewContainer.createEmbeddedView(elseTemplateRef);
186
+ }
187
+ }
188
+ ngOnDestroy() {
189
+ this.subscription?.unsubscribe();
190
+ }
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 }); }
193
+ }
194
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFeatureDirective, decorators: [{
195
+ type: Directive,
196
+ args: [{
197
+ selector: '[feature]',
198
+ standalone: false
199
+ }]
200
+ }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }], propDecorators: { feature: [{
201
+ type: Input
202
+ }], featureElse: [{
203
+ type: Input
204
+ }] } });
205
+
206
+ const AXPFeatureGuard = (route, state) => {
207
+ const sessionService = inject(AXPSessionService);
208
+ const requiredFeatures = route.data['requiredFeature'];
209
+ return sessionService.features$.pipe(map(() => {
210
+ const keys = !requiredFeatures ? [] : (Array.isArray(requiredFeatures) ? requiredFeatures : [requiredFeatures]);
211
+ const hasFeature = keys.length == 0 || sessionService.isFeatureEnabled(...keys);
212
+ if (!hasFeature) {
213
+ throw new AXPUnauthorizedError(`Access Denied. You do not have access to this feature. Required feature(s): ${keys.join(', ')}. Please contact support if you need access.`, {
214
+ redirectUrl: state.url
215
+ });
216
+ }
217
+ return true;
218
+ }));
219
+ };
220
+
221
+ /**
222
+ * Optional injection token for feature checker.
223
+ * If provided, the checker will be called to potentially override
224
+ * feature enablement results based on custom logic.
225
+ */
226
+ const AXP_FEATURE_CHECKER = new InjectionToken('AXP_FEATURE_CHECKER');
227
+
228
+ const AXP_PERMISSION_LOADER = new InjectionToken('AXP_PERMISSION_LOADER', {
229
+ providedIn: 'root',
230
+ factory: () => {
231
+ return new AXPPermissionDefaultLoader();
232
+ }
233
+ });
234
+ class AXPPermissionDefaultLoader {
235
+ async getList(context) {
236
+ return [];
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Optional injection token for permission checker.
242
+ * If provided, the checker will be called to potentially override
243
+ * authorization results based on custom logic.
244
+ */
245
+ const AXP_PERMISSION_CHECKER = new InjectionToken('AXP_PERMISSION_CHECKER');
246
+
247
+ class AXPSessionService {
248
+ constructor() {
249
+ this.eventService = inject(AXPBroadcastEventService);
250
+ this.authStrategyRegistry = inject(AXPAuthStrategyRegistryService);
251
+ this.injector = inject(Injector);
252
+ this.permissionLoader = inject(AXP_PERMISSION_LOADER);
253
+ this.featureLoader = inject(AXP_FEATURE_LOADER);
254
+ this.tenantLoader = inject(AXP_TENANT_LOADER);
255
+ this.applicationLoader = inject(AXP_APPLICATION_LOADER);
256
+ this.permissionChecker = inject(AXP_PERMISSION_CHECKER, { optional: true });
257
+ this.featureChecker = inject(AXP_FEATURE_CHECKER, { optional: true });
258
+ this.status = new BehaviorSubject(AXPSessionStatus.Unauthenticated);
259
+ this.status$ = this.status.asObservable().pipe(shareReplay(1));
260
+ // Add loading state to prevent premature redirects
261
+ this.isLoading = new BehaviorSubject(true);
262
+ this.isLoading$ = this.isLoading.asObservable().pipe(shareReplay(1));
263
+ this.currentUserSubject = new BehaviorSubject(null);
264
+ this.user$ = this.currentUserSubject.asObservable().pipe(shareReplay(1));
265
+ this.currentTenantSubject = new BehaviorSubject(null);
266
+ this.tenant$ = this.currentTenantSubject.asObservable().pipe(shareReplay(1));
267
+ this.currentApplicationSubject = new BehaviorSubject(null);
268
+ this.application$ = this.currentApplicationSubject.asObservable().pipe(shareReplay(1));
269
+ this.permissionsSubject = new BehaviorSubject([]);
270
+ this.permissions$ = this.permissionsSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
271
+ this.featuresSubject = new BehaviorSubject([]);
272
+ this.features$ = this.featuresSubject.asObservable().pipe(shareReplay(1), defaultIfEmpty([]));
273
+ this.isAuthenticated$ = this.status$.pipe(map((status) => {
274
+ const isAuth = status === AXPSessionStatus.Authenticated || status === AXPSessionStatus.Authorized;
275
+ return isAuth;
276
+ }), shareReplay(1));
277
+ // Add a new observable that considers loading state
278
+ this.isAuthenticatedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
279
+ if (loading) {
280
+ // Wait for loading to complete, then return authentication status
281
+ return this.isLoading$.pipe(filter((isLoading) => !isLoading), switchMap(() => this.isAuthenticated$));
282
+ }
283
+ // If not loading, return current authentication status
284
+ return this.isAuthenticated$;
285
+ }), shareReplay(1));
286
+ this.isAuthorized$ = this.status$.pipe(map((status) => status === AXPSessionStatus.Authorized), shareReplay(1));
287
+ /** Waits for session restore to finish, then emits whether the session is fully authorized. */
288
+ this.isAuthorizedWithLoading$ = this.isLoading$.pipe(switchMap((loading) => {
289
+ if (loading) {
290
+ return this.isLoading$.pipe(filter((isLoading) => !isLoading), switchMap(() => this.isAuthorized$));
291
+ }
292
+ return this.isAuthorized$;
293
+ }), shareReplay(1));
294
+ }
295
+ static { this.SESSION_KEY = 'AXP_SESSION'; }
296
+ get user() {
297
+ const session = this.getSessionData();
298
+ if (session?.user && !this.currentUserSubject.value) {
299
+ this.currentUserSubject.next(session.user);
300
+ }
301
+ return this.currentUserSubject.value;
302
+ }
303
+ get tenant() {
304
+ const session = this.getSessionData();
305
+ if (session?.tenant && !this.currentTenantSubject.value) {
306
+ this.currentTenantSubject.next(session.tenant);
307
+ }
308
+ return this.currentTenantSubject.value;
309
+ }
310
+ get tenants$() {
311
+ return from(this.tenantLoader.getList(this.getContext()));
312
+ }
313
+ get application() {
314
+ const session = this.getSessionData();
315
+ if (session?.application && !this.currentApplicationSubject.value) {
316
+ this.currentApplicationSubject.next(session.application);
317
+ }
318
+ return this.currentApplicationSubject.value;
319
+ }
320
+ get applications$() {
321
+ return from(this.applicationLoader.getList(this.getContext()));
322
+ }
323
+ get permissions() {
324
+ return this.permissionsSubject.value ?? [];
325
+ }
326
+ get features() {
327
+ return this.featuresSubject.value ?? [];
328
+ }
329
+ async restoreSession() {
330
+ this.isLoading.next(true);
331
+ try {
332
+ const sessionData = this.getSessionData();
333
+ if (sessionData) {
334
+ if (sessionData.user) {
335
+ this.currentUserSubject.next(sessionData.user);
336
+ // Restore tenant and application from session data
337
+ if (sessionData.tenant) {
338
+ this.currentTenantSubject.next(sessionData.tenant);
339
+ }
340
+ if (sessionData.application) {
341
+ this.currentApplicationSubject.next(sessionData.application);
342
+ }
343
+ this.status.next(AXPSessionStatus.Authenticated);
344
+ await this.loadPermissions();
345
+ await this.loadFeatures();
346
+ await this.signInComplete();
347
+ }
348
+ }
349
+ else {
350
+ this.status.next(AXPSessionStatus.Unauthorized);
351
+ }
352
+ }
353
+ catch (error) {
354
+ this.status.next(AXPSessionStatus.Unauthorized);
355
+ }
356
+ finally {
357
+ this.isLoading.next(false);
358
+ }
359
+ }
360
+ async signin(credentials) {
361
+ this.isLoading.next(true);
362
+ try {
363
+ //
364
+ // this.clearSession();
365
+ //
366
+ const strategy = this.authStrategyRegistry.get(credentials.strategy);
367
+ if (!strategy) {
368
+ throw new Error(`Authentication strategy '${credentials.strategy}' is not supported`);
369
+ }
370
+ const result = await strategy.signin(credentials);
371
+ if (!result)
372
+ return;
373
+ if (result?.succeed) {
374
+ this.permissionsSubject.next([]);
375
+ this.featuresSubject.next([]);
376
+ this.currentUserSubject.next(result.data.user);
377
+ this.setSession({
378
+ accessToken: result.data.accessToken,
379
+ refreshToken: result.data.refreshToken,
380
+ strategy: credentials.strategy,
381
+ user: result.data.user,
382
+ application: result.data?.application,
383
+ tenant: result.data?.tenant,
384
+ expiresIn: result.data?.expiresIn,
385
+ idToken: result.data?.idToken ?? null,
386
+ locked: false,
387
+ });
388
+ await this.loadPermissions();
389
+ await this.loadFeatures();
390
+ }
391
+ else {
392
+ this.status.next(AXPSessionStatus.Unauthenticated);
393
+ throw new Error(`Invalid Username or Password`);
394
+ }
395
+ }
396
+ catch (error) {
397
+ console.error('Signin error:', error);
398
+ this.status.next(AXPSessionStatus.Unauthenticated);
399
+ throw error;
400
+ }
401
+ finally {
402
+ this.isLoading.next(false);
403
+ console.log('Signin process completed');
404
+ }
405
+ }
406
+ async updateToken(params) {
407
+ this.isLoading.next(true);
408
+ try {
409
+ const strategyName = this.getSessionData()?.strategy;
410
+ if (!strategyName) {
411
+ throw new Error('Strategy not found');
412
+ }
413
+ const strategy = this.authStrategyRegistry.get(strategyName);
414
+ if (!strategy) {
415
+ throw new Error(`Authentication strategy '${this.getSessionData()?.strategy}' is not supported`);
416
+ }
417
+ const result = await strategy.updateToken(params);
418
+ if (result?.succeed) {
419
+ this.currentUserSubject.next(result.data.user);
420
+ this.setSession({
421
+ accessToken: result.data.accessToken,
422
+ refreshToken: result.data.refreshToken,
423
+ strategy: strategyName,
424
+ user: result.data.user,
425
+ application: result.data?.application,
426
+ tenant: result.data?.tenant,
427
+ expiresIn: result.data?.expiresIn,
428
+ idToken: result.data?.idToken ?? null,
429
+ });
430
+ this.status.next(AXPSessionStatus.Authenticated);
431
+ // If we have both tenant and application, complete the sign-in
432
+ if (this.application && this.tenant) {
433
+ await this.restoreSession();
434
+ }
435
+ }
436
+ else {
437
+ this.status.next(AXPSessionStatus.Unauthenticated);
438
+ throw new Error(`Invalid Username or Password`);
439
+ }
440
+ }
441
+ catch (error) {
442
+ console.error('Update token error:', error);
443
+ throw error;
444
+ }
445
+ finally {
446
+ this.isLoading.next(false);
447
+ console.log('Update token process completed');
448
+ }
449
+ }
450
+ async signout() {
451
+ console.log('Signing out...');
452
+ this.isLoading.next(true);
453
+ try {
454
+ const sessionData = this.getSessionData();
455
+ if (sessionData?.strategy) {
456
+ const strategy = this.authStrategyRegistry.get(sessionData?.strategy);
457
+ if (strategy) {
458
+ try {
459
+ await strategy.signout();
460
+ }
461
+ catch (error) { }
462
+ }
463
+ }
464
+ //
465
+ const userId = this.user?.id;
466
+ this.clearSession();
467
+ this.eventService.publish(AXPSessionStatus.SignedOut, { id: userId });
468
+ this.isLoading.next(false);
469
+ this.status.next(AXPSessionStatus.SignedOut);
470
+ }
471
+ finally {
472
+ this.isLoading.next(false);
473
+ }
474
+ }
475
+ async refreshToken() {
476
+ console.log('Refreshing token...');
477
+ return new Promise(async (resolve, reject) => {
478
+ const sessionData = this.getSessionData();
479
+ if (!sessionData || !sessionData?.refreshToken) {
480
+ console.log('No session data or refresh token found');
481
+ reject(new Error('No refresh token available'));
482
+ return;
483
+ }
484
+ const strategy = this.authStrategyRegistry.get(sessionData.strategy);
485
+ if (!strategy) {
486
+ console.error('Authentication strategy not found:', sessionData.strategy);
487
+ reject(new Error(`Authentication strategy '${sessionData.strategy}' is not found`));
488
+ return;
489
+ }
490
+ try {
491
+ const result = await strategy.refreshToken(this.getContext());
492
+ if (result.succeed) {
493
+ console.log('Token refresh successful');
494
+ this.setSession(result.data);
495
+ resolve(result.data?.accessToken);
496
+ }
497
+ else {
498
+ console.error('Token refresh failed');
499
+ this.clearSession();
500
+ this.status.next(AXPSessionStatus.Expired);
501
+ reject(new Error('Token refresh failed'));
502
+ }
503
+ }
504
+ catch (error) {
505
+ console.error('Error during token refresh:', error);
506
+ this.clearSession();
507
+ this.status.next(AXPSessionStatus.Expired);
508
+ reject(error);
509
+ }
510
+ });
511
+ }
512
+ async loadPermissions() {
513
+ try {
514
+ const permissions = await this.permissionLoader.getList(this.getContext());
515
+ this.permissionsSubject.next(permissions ?? []);
516
+ }
517
+ catch (error) {
518
+ console.error('Error loading permissions:', error);
519
+ this.permissionsSubject.next([]);
520
+ }
521
+ }
522
+ async loadFeatures() {
523
+ try {
524
+ const features = await this.featureLoader.getList(this.getContext());
525
+ this.featuresSubject.next(features ?? []);
526
+ }
527
+ catch (error) {
528
+ console.error('Error loading features:', error);
529
+ this.featuresSubject.next([]);
530
+ }
531
+ }
532
+ async signInComplete() {
533
+ // Ensure we have the required data
534
+ if (!this.user) {
535
+ this.status.next(AXPSessionStatus.Unauthenticated);
536
+ return;
537
+ }
538
+ // Set status to Authorized
539
+ this.status.next(AXPSessionStatus.Authorized);
540
+ }
541
+ setSession(tokens) {
542
+ const existing = this.getSessionData() ?? {};
543
+ const sessionData = {
544
+ ...existing,
545
+ ...tokens,
546
+ };
547
+ // Update subjects
548
+ if (tokens.user) {
549
+ this.currentUserSubject.next(tokens.user);
550
+ }
551
+ if (tokens.tenant) {
552
+ this.currentTenantSubject.next(tokens.tenant);
553
+ }
554
+ if (tokens.application) {
555
+ this.currentApplicationSubject.next(tokens.application);
556
+ }
557
+ localStorage.setItem(AXPSessionService.SESSION_KEY, JSON.stringify(sessionData));
558
+ }
559
+ /** Whether the client lock overlay should remain active after reload. */
560
+ isLocked() {
561
+ return this.getSessionData()?.locked === true;
562
+ }
563
+ /** Persists client lock state inside {@link AXPSessionService.SESSION_KEY}. */
564
+ setLocked(locked) {
565
+ const existing = this.getSessionData();
566
+ if (!existing) {
567
+ return;
568
+ }
569
+ this.setSession({ locked });
570
+ }
571
+ setStrategy(strategy) {
572
+ const sessionData = this.getSessionData();
573
+ const newSessionData = { ...sessionData, strategy };
574
+ this.setSession(newSessionData);
575
+ }
576
+ getSessionData() {
577
+ try {
578
+ const sessionDataString = localStorage.getItem(AXPSessionService.SESSION_KEY);
579
+ if (sessionDataString) {
580
+ const sessionData = JSON.parse(sessionDataString);
581
+ return sessionData;
582
+ }
583
+ else {
584
+ return null;
585
+ }
586
+ }
587
+ catch (error) {
588
+ localStorage.removeItem(AXPSessionService.SESSION_KEY);
589
+ return null;
590
+ }
591
+ }
592
+ clearSession() {
593
+ //
594
+ this.currentUserSubject.next(null);
595
+ //
596
+ this.currentTenantSubject.next(null);
597
+ //
598
+ this.currentApplicationSubject.next(null);
599
+ //
600
+ this.permissionsSubject.next([]);
601
+ //
602
+ this.featuresSubject.next([]);
603
+ //
604
+ localStorage.removeItem(AXPSessionService.SESSION_KEY);
605
+ }
606
+ authorize(...keys) {
607
+ // Calculate base result
608
+ const baseResult = keys.every((k) => {
609
+ if (isEmpty(k))
610
+ return true;
611
+ // Check if user has the permission
612
+ const hasPermission = this.permissions.indexOf(k) > -1;
613
+ if (!hasPermission)
614
+ return false;
615
+ // Check if permission has required features (if permission definition service is available)
616
+ // Note: This is a lightweight check. Full feature validation happens in permission definition service.
617
+ return true;
618
+ });
619
+ // If permission checker is provided, use it to potentially override the result
620
+ if (this.permissionChecker) {
621
+ const context = this.getContext();
622
+ return this.permissionChecker.check(keys, context, baseResult);
623
+ }
624
+ return baseResult;
625
+ }
626
+ isFeatureEnabled(...keys) {
627
+ // Calculate base result
628
+ const baseResult = keys.every((k) => isEmpty(k) || this.features.some((c) => c.name == k && c.value == true));
629
+ // If feature checker is provided, use it to potentially override the result
630
+ if (this.featureChecker) {
631
+ const context = this.getContext();
632
+ return this.featureChecker.check(keys, context, baseResult);
633
+ }
634
+ return baseResult;
635
+ }
636
+ /**
637
+ * Checks if a module is enabled for the current tenant/application.
638
+ * Module names are stored as features with value: true when enabled.
639
+ * Module names can be provided in PascalCase (e.g., 'SecurityManagement') or kebab-case (e.g., 'security-management').
640
+ */
641
+ isModuleEnabled(moduleName) {
642
+ if (!moduleName) {
643
+ return false;
644
+ }
645
+ // Normalize module name: convert kebab-case to PascalCase if needed
646
+ const normalizedModuleName = moduleName.includes('-')
647
+ ? moduleName.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('')
648
+ : moduleName;
649
+ // Module names are stored as features with value: true when enabled
650
+ return this.features.some(f => f.name === normalizedModuleName && f.value === true);
651
+ }
652
+ getToken() {
653
+ const sessionData = this.getSessionData();
654
+ return sessionData?.accessToken;
655
+ }
656
+ getContext() {
657
+ return new AXPSessionContext({
658
+ user: this.user,
659
+ tenant: this.tenant,
660
+ application: this.application,
661
+ });
662
+ }
663
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPSessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
664
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPSessionService, providedIn: 'root' }); }
665
+ }
666
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPSessionService, decorators: [{
667
+ type: Injectable,
668
+ args: [{
669
+ providedIn: 'root',
670
+ }]
671
+ }] });
672
+
673
+ class AXPPermissionDirective {
674
+ constructor(templateRef, viewContainer) {
675
+ this.templateRef = templateRef;
676
+ this.viewContainer = viewContainer;
677
+ this.hasView = signal(false, ...(ngDevMode ? [{ debugName: "hasView" }] : /* istanbul ignore next */ []));
678
+ this.sessionService = inject(AXPSessionService);
679
+ }
680
+ set permission(permissionKeys) {
681
+ const keys = !permissionKeys ? [] : (Array.isArray(permissionKeys) ? permissionKeys : [permissionKeys]);
682
+ if (keys.length == 0) {
683
+ // If permissionKey is null or empty, decide the default behavior here
684
+ this.viewContainer.createEmbeddedView(this.templateRef);
685
+ this.hasView.set(true);
686
+ return;
687
+ }
688
+ this.subscription = this.sessionService.isAuthorized$
689
+ .subscribe((isAuthorized) => {
690
+ if (isAuthorized && this.sessionService.authorize(...keys)) {
691
+ if (!this.hasView()) {
692
+ this.viewContainer.createEmbeddedView(this.templateRef);
693
+ this.hasView.set(true);
694
+ }
695
+ }
696
+ else {
697
+ this.viewContainer.clear();
698
+ this.hasView.set(false);
699
+ }
700
+ });
701
+ }
702
+ set permissionElse(elseTemplateRef) {
703
+ if (!this.hasView()) {
704
+ this.viewContainer.createEmbeddedView(elseTemplateRef);
705
+ }
706
+ }
707
+ ngOnDestroy() {
708
+ this.subscription?.unsubscribe();
709
+ }
710
+ 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 }); }
711
+ 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 }); }
712
+ }
713
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDirective, decorators: [{
714
+ type: Directive,
715
+ args: [{
716
+ selector: '[permission]',
717
+ standalone: false
718
+ }]
719
+ }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }], propDecorators: { permission: [{
720
+ type: Input
721
+ }], permissionElse: [{
722
+ type: Input
723
+ }] } });
724
+
725
+ const AXPPermissionGuard = (route, state) => {
726
+ const sessionService = inject(AXPSessionService);
727
+ const permissionKeys = route.data['requiredPermission'];
728
+ return sessionService.isAuthorizedWithLoading$.pipe(first(), map((value) => {
729
+ const keys = !permissionKeys ? [] : Array.isArray(permissionKeys) ? permissionKeys : [permissionKeys];
730
+ const hasPermission = keys.length == 0 || sessionService.authorize(...keys);
731
+ if (!hasPermission) {
732
+ throw new AXPUnauthorizedError(`Access denied. Required permissions: ${keys.join(', ')}. Please contact your administrator if you believe this is an error.`, {
733
+ redirectUrl: state.url,
734
+ });
735
+ }
736
+ return true;
737
+ }));
738
+ };
739
+
740
+ class AXPPermissionDefinitionProviderContext {
741
+ constructor() {
742
+ this.builders = new Map();
743
+ }
744
+ addGroup(name, title, description) {
745
+ if (this.isGroupDefined(name)) {
746
+ return this.findGroup(name);
747
+ }
748
+ const group = {
749
+ name,
750
+ title,
751
+ description,
752
+ permissions: []
753
+ };
754
+ const builder = new AXPPermissionDefinitionGroupBuilder(this, group);
755
+ this.builders.set(name, builder);
756
+ return builder;
757
+ }
758
+ getGroupDefinitions() {
759
+ return Array.from(this.builders.values()).map(b => b.group);
760
+ }
761
+ findGroup(name) {
762
+ return this.builders.get(name);
763
+ }
764
+ isGroupDefined(name) {
765
+ return this.builders.has(name);
766
+ }
767
+ }
768
+ class AXPPermissionDefinitionGroupBuilder {
769
+ get group() {
770
+ return this._group;
771
+ }
772
+ constructor(context, group) {
773
+ this.context = context;
774
+ this._group = group;
775
+ }
776
+ addPermission(name, title, description, requiredFeatures) {
777
+ const permission = {
778
+ name,
779
+ title,
780
+ description,
781
+ children: [],
782
+ requiredFeatures
783
+ };
784
+ this._group.permissions.push(permission);
785
+ return new AXPPermissionDefinitionBuilder(this, permission);
786
+ }
787
+ endGroup() {
788
+ return this.context;
789
+ }
790
+ findPermission(path) {
791
+ return undefined;
792
+ }
793
+ findGroup(name) {
794
+ return undefined;
795
+ }
796
+ }
797
+ class AXPPermissionDefinitionBuilder {
798
+ constructor(groupBuilder, permission) {
799
+ this.groupBuilder = groupBuilder;
800
+ this.permission = permission;
801
+ }
802
+ addChild(name, title, description, requiredFeatures) {
803
+ const permission = {
804
+ name,
805
+ title,
806
+ description,
807
+ children: [],
808
+ requiredFeatures
809
+ };
810
+ this.permission.children.push(permission);
811
+ return this;
812
+ }
813
+ /**
814
+ * Set required features for this permission.
815
+ * @param features - Array of feature names (e.g., ['PlatformManagement.menu-customization'])
816
+ */
817
+ requireFeatures(...features) {
818
+ this.permission.requiredFeatures = features;
819
+ return this;
820
+ }
821
+ endPermission() {
822
+ return this.groupBuilder;
823
+ }
824
+ }
825
+
826
+ const AXP_PERMISSION_DEFINITION_PROVIDER = new InjectionToken('AXP_PERMISSION_DEFINITION_PROVIDER', {
827
+ providedIn: 'root',
828
+ factory: () => {
829
+ return [];
830
+ }
831
+ });
832
+ class AXPPermissionDefinitionService {
833
+ constructor() {
834
+ this.providers = inject(AXP_PERMISSION_DEFINITION_PROVIDER, { optional: true });
835
+ this.sessionService = inject(AXPSessionService);
836
+ this.cache = null;
837
+ }
838
+ async load() {
839
+ if (this.cache) {
840
+ return;
841
+ }
842
+ const context = new AXPPermissionDefinitionProviderContext();
843
+ // Load providers from DI tokens
844
+ if (Array.isArray(this.providers)) {
845
+ for (const provider of this.providers) {
846
+ if (provider instanceof Promise) {
847
+ // If provider is a promise, resolve it
848
+ const resolvedProvider = await provider;
849
+ await resolvedProvider.define(context);
850
+ }
851
+ else {
852
+ // If provider is a direct instance, use it directly
853
+ await provider.define(context);
854
+ }
855
+ }
856
+ }
857
+ // Filter permissions based on required features
858
+ const allGroups = context.getGroupDefinitions();
859
+ this.cache = this.filterByFeatures(allGroups);
860
+ }
861
+ /**
862
+ * Filter permissions based on required features.
863
+ * Removes permissions that have required features that are not enabled.
864
+ */
865
+ filterByFeatures(groups) {
866
+ return groups.map(group => ({
867
+ ...group,
868
+ permissions: this.filterPermissions(group.permissions)
869
+ })).filter(group => group.permissions.length > 0);
870
+ }
871
+ /**
872
+ * Recursively filter permissions and their children based on required features.
873
+ */
874
+ filterPermissions(permissions) {
875
+ return permissions
876
+ .filter(permission => {
877
+ // Check if required features are enabled
878
+ if (permission.requiredFeatures && permission.requiredFeatures.length > 0) {
879
+ const allFeaturesEnabled = permission.requiredFeatures.every(featureName => this.sessionService.isFeatureEnabled(featureName));
880
+ if (!allFeaturesEnabled) {
881
+ return false; // Filter out this permission
882
+ }
883
+ }
884
+ return true;
885
+ })
886
+ .map(permission => ({
887
+ ...permission,
888
+ children: this.filterPermissions(permission.children)
889
+ }))
890
+ .filter(permission => {
891
+ // Remove permissions that have no children if they were only containers
892
+ // (This is optional - you might want to keep parent permissions even without children)
893
+ return true;
894
+ });
895
+ }
896
+ async reload() {
897
+ this.cache = null;
898
+ await this.load();
899
+ }
900
+ async getGroups() {
901
+ await this.load();
902
+ return this.cache ?? [];
903
+ }
904
+ async getPermissions() {
905
+ await this.load();
906
+ return this.cache?.flatMap(g => g.permissions) ?? [];
907
+ }
908
+ async getGroup(name) {
909
+ await this.load();
910
+ return this.cache?.find(g => g.name === name) ?? null;
911
+ }
912
+ async getPermission(name) {
913
+ await this.load();
914
+ return this.cache?.find(g => g.permissions.find(p => p.name === name))?.permissions.find(p => p.name === name) ?? null;
915
+ }
916
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDefinitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
917
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDefinitionService, providedIn: 'root' }); }
918
+ }
919
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPermissionDefinitionService, decorators: [{
920
+ type: Injectable,
921
+ args: [{ providedIn: 'root' }]
922
+ }] });
923
+
924
+ //#region ---- Imports ----
925
+ //#endregion
926
+ //#region ---- Helpers ----
927
+ function flattenPermissionDefinitions(nodes, groupPrefix) {
928
+ const rows = [];
929
+ for (const node of nodes) {
930
+ const displayTitle = groupPrefix ? `${groupPrefix} / ${node.title}` : node.title;
931
+ if (node.children?.length) {
932
+ rows.push(...flattenPermissionDefinitions(node.children, displayTitle));
933
+ }
934
+ else {
935
+ rows.push({
936
+ id: String(node.name),
937
+ title: displayTitle,
938
+ });
939
+ }
940
+ }
941
+ return rows;
942
+ }
943
+ //#endregion
944
+ //#region ---- Permission definitions data source ----
945
+ /**
946
+ * Data source definition for leaf permissions from {@link AXPPermissionDefinitionService#getGroups}.
947
+ */
948
+ class AXPPermissionDefinitionsDataSourceDefinition {
949
+ constructor() {
950
+ //#region ---- Services & Dependencies ----
951
+ this.permissionDefinitionService = inject(AXPPermissionDefinitionService);
952
+ //#endregion
953
+ }
954
+ //#endregion
955
+ //#region ---- Public API ----
956
+ async items() {
957
+ return [
958
+ {
959
+ name: AXPPlatformAuthDataSourceKeys.PermissionDefinitions,
960
+ title: 'Permissions',
961
+ source: () => new AXDataSource({
962
+ key: 'id',
963
+ load: async () => {
964
+ const groups = await this.permissionDefinitionService.getGroups();
965
+ const list = [];
966
+ for (const g of groups) {
967
+ list.push(...flattenPermissionDefinitions(g.permissions, g.title));
968
+ }
969
+ return { items: list, total: list.length };
970
+ },
971
+ byKey: async (key) => {
972
+ const groups = await this.permissionDefinitionService.getGroups();
973
+ const list = [];
974
+ for (const g of groups) {
975
+ list.push(...flattenPermissionDefinitions(g.permissions, g.title));
976
+ }
977
+ return list.find((item) => item.id === key);
978
+ },
979
+ pageSize: 1000,
980
+ }),
981
+ columns: [
982
+ {
983
+ name: 'id',
984
+ title: 'ID',
985
+ datatype: 'string',
986
+ type: AXPWidgetsCatalog.text,
987
+ },
988
+ {
989
+ name: 'title',
990
+ title: 'Title',
991
+ datatype: 'string',
992
+ type: AXPWidgetsCatalog.text,
993
+ },
994
+ ],
995
+ filters: [
996
+ {
997
+ field: 'title',
998
+ title: 'Title',
999
+ operator: { type: 'equal' },
1000
+ widget: { type: AXPWidgetsCatalog.text },
1001
+ filterType: { advance: true, inline: true },
1002
+ },
1003
+ ],
1004
+ textField: { name: 'title', title: 'Title' },
1005
+ valueField: { name: 'id', title: 'ID' },
1006
+ },
1007
+ ];
1008
+ }
1009
+ }
1010
+ //#endregion
1011
+
1012
+ class AXPPermissionEvaluatorScopeProvider {
1013
+ constructor() {
1014
+ this.sessionService = inject(AXPSessionService);
1015
+ }
1016
+ async provide(context) {
1017
+ context.addScope('permission', {
1018
+ /**
1019
+ * Check if the current user has all specified permissions
1020
+ * @param permissions - Single permission string or array of permission strings
1021
+ * @returns boolean - true if user has all permissions, false otherwise
1022
+ */
1023
+ check: (...permissions) => {
1024
+ return this.sessionService.authorize(...permissions);
1025
+ },
1026
+ /**
1027
+ * Check if the current user has any of the specified permissions
1028
+ * @param permissions - Array of permission strings
1029
+ * @returns boolean - true if user has any of the permissions, false otherwise
1030
+ */
1031
+ checkAny: (...permissions) => {
1032
+ return permissions.some(p => this.sessionService.authorize(p));
1033
+ },
1034
+ });
1035
+ }
1036
+ }
1037
+
1038
+ const AXPAuthGuard = (route, state) => {
1039
+ const sessionService = inject(AXPSessionService);
1040
+ return sessionService.isAuthenticatedWithLoading$.pipe(first(), map((value) => {
1041
+ if (value) {
1042
+ return true;
1043
+ }
1044
+ throw new AXPUnauthenticatedError(`Access denied. You are not currently logged in. Please log in to access this page. If you continue to see this message after logging in, please contact support.`, {
1045
+ redirectUrl: state.url,
1046
+ });
1047
+ }));
1048
+ };
1049
+
1050
+ function initializeAppState(service) {
1051
+ return async () => {
1052
+ try {
1053
+ await service.restoreSession();
1054
+ }
1055
+ catch (error) {
1056
+ console.error(error);
1057
+ }
1058
+ };
1059
+ }
1060
+ class AXPAuthModule {
1061
+ static forRoot(configs) {
1062
+ return {
1063
+ ngModule: AXPAuthModule,
1064
+ providers: [
1065
+ ...(configs?.strategies || []),
1066
+ {
1067
+ provide: 'AXPAuthModuleFactory',
1068
+ useFactory: (registry) => () => {
1069
+ registry.register(...(configs?.strategies || []));
1070
+ },
1071
+ deps: [AXPAuthStrategyRegistryService],
1072
+ multi: true,
1073
+ },
1074
+ ],
1075
+ };
1076
+ }
1077
+ static forChild(configs) {
1078
+ return {
1079
+ ngModule: AXPAuthModule,
1080
+ providers: [
1081
+ ...(configs?.strategies || []),
1082
+ {
1083
+ provide: 'AXPAuthModuleFactory',
1084
+ useFactory: (registry) => () => {
1085
+ registry.register(...(configs?.strategies || []));
1086
+ },
1087
+ deps: [AXPAuthStrategyRegistryService],
1088
+ multi: true,
1089
+ },
1090
+ ],
1091
+ };
1092
+ }
1093
+ /**
1094
+ * @ignore
1095
+ */
1096
+ constructor(instances) {
1097
+ instances?.forEach((f) => {
1098
+ f();
1099
+ });
1100
+ }
1101
+ 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 }); }
1102
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthModule, declarations: [AXPPermissionDirective, AXPFeatureDirective], exports: [AXPPermissionDirective, AXPFeatureDirective] }); }
1103
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthModule, providers: [
1104
+ provideAppInitializer(() => {
1105
+ const initializerFn = initializeAppState(inject(AXPSessionService));
1106
+ return initializerFn();
1107
+ }),
1108
+ {
1109
+ provide: AXP_SESSION_SERVICE,
1110
+ useExisting: AXPSessionService,
1111
+ },
1112
+ ] }); }
1113
+ }
1114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAuthModule, decorators: [{
1115
+ type: NgModule,
1116
+ args: [{
1117
+ imports: [],
1118
+ exports: [AXPPermissionDirective, AXPFeatureDirective],
1119
+ declarations: [AXPPermissionDirective, AXPFeatureDirective],
1120
+ providers: [
1121
+ provideAppInitializer(() => {
1122
+ const initializerFn = initializeAppState(inject(AXPSessionService));
1123
+ return initializerFn();
1124
+ }),
1125
+ {
1126
+ provide: AXP_SESSION_SERVICE,
1127
+ useExisting: AXPSessionService,
1128
+ },
1129
+ ],
1130
+ }]
1131
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1132
+ type: Optional
1133
+ }, {
1134
+ type: Inject,
1135
+ args: ['AXPAuthModuleFactory']
1136
+ }] }] });
1137
+
1138
+ class AXPAuthStrategy {
1139
+ }
1140
+
1141
+ //#region ---- JWT Utility ----
1142
+ /**
1143
+ * Utility class for JWT token operations
1144
+ */
1145
+ class JwtUtil {
1146
+ /**
1147
+ * Parses a JWT token and returns the payload
1148
+ */
1149
+ static parseJwt(token) {
1150
+ try {
1151
+ const base64Url = token.split('.')[1];
1152
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
1153
+ const jsonPayload = decodeURIComponent(atob(base64)
1154
+ .split('')
1155
+ .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
1156
+ .join(''));
1157
+ return JSON.parse(jsonPayload);
1158
+ }
1159
+ catch (error) {
1160
+ throw new Error('Invalid JWT token');
1161
+ }
1162
+ }
1163
+ }
1164
+ //#endregion
1165
+ //#region ---- PKCE Utility ----
1166
+ /**
1167
+ * Utility class for PKCE (Proof Key for Code Exchange) operations
1168
+ */
1169
+ class PkceUtil {
1170
+ /**
1171
+ * Generates a random string for PKCE code verifier
1172
+ */
1173
+ static generateRandomString(length) {
1174
+ const array = new Uint8Array(length);
1175
+ crypto.getRandomValues(array);
1176
+ return this.base64UrlEncode(array);
1177
+ }
1178
+ /**
1179
+ * Generates PKCE code challenge from verifier
1180
+ */
1181
+ static async generateCodeChallenge(codeVerifier) {
1182
+ const encoder = new TextEncoder();
1183
+ const data = encoder.encode(codeVerifier);
1184
+ const digest = await crypto.subtle.digest('SHA-256', data);
1185
+ return this.base64UrlEncode(new Uint8Array(digest));
1186
+ }
1187
+ /**
1188
+ * Base64 URL encoding for PKCE
1189
+ */
1190
+ static base64UrlEncode(array) {
1191
+ return btoa(String.fromCharCode(...array))
1192
+ .replace(/\+/g, '-')
1193
+ .replace(/\//g, '_')
1194
+ .replace(/=/g, '');
1195
+ }
1196
+ }
1197
+ //#endregion
1198
+ //#region ---- Time Utility ----
1199
+ /**
1200
+ * Utility class for time and date operations
1201
+ */
1202
+ class TimeUtil {
1203
+ /**
1204
+ * Calculates the time difference in milliseconds between a future date and now
1205
+ */
1206
+ static expiresInMilliseconds(expiresInDate) {
1207
+ return new Date(expiresInDate).getTime() - new Date().getTime();
1208
+ }
1209
+ /**
1210
+ * Calculates expiration date from seconds
1211
+ */
1212
+ static calculateExpireInDate(expireInSeconds) {
1213
+ return new Date(Date.now() + expireInSeconds * 1000).toISOString();
1214
+ }
1215
+ }
1216
+ //#endregion
1217
+
1218
+ //#region ---- Challenge Data Types ----
1219
+ //#endregion
1220
+
1221
+ //#region ---- Abstract Challenge Provider ----
1222
+ /**
1223
+ * Abstract base class for login challenge providers
1224
+ *
1225
+ * Implement this class to create custom challenge mechanisms like:
1226
+ * - Image CAPTCHA
1227
+ * - reCAPTCHA
1228
+ * - SMS verification
1229
+ * - Email verification
1230
+ *
1231
+ * @example
1232
+ * ```typescript
1233
+ * @Injectable()
1234
+ * export class MyImageCaptchaProvider extends AXPLoginChallengeProvider {
1235
+ * readonly name = 'image-captcha';
1236
+ *
1237
+ * checkResponse(error: unknown): AXPChallengeCheckResult | null {
1238
+ * if (error instanceof HttpErrorResponse) {
1239
+ * if (error.error?.requiresCaptcha) {
1240
+ * return { required: true };
1241
+ * }
1242
+ * }
1243
+ * return null;
1244
+ * }
1245
+ *
1246
+ * async getChallenge(): Promise<AXPLoginChallengeData> {
1247
+ * const response = await this.http.get('/api/captcha').toPromise();
1248
+ * return {
1249
+ * id: response.id,
1250
+ * content: response.image,
1251
+ * contentType: 'image-base64'
1252
+ * };
1253
+ * }
1254
+ *
1255
+ * async refreshChallenge(): Promise<AXPLoginChallengeData> {
1256
+ * return this.getChallenge();
1257
+ * }
1258
+ *
1259
+ * getChallengeComponent(): Type<AXPLoginChallengeComponentBase> {
1260
+ * return MyCaptchaChallengeComponent;
1261
+ * }
1262
+ * }
1263
+ * ```
1264
+ */
1265
+ class AXPLoginChallengeProvider {
1266
+ /**
1267
+ * Returns the component type for rendering the challenge UI
1268
+ *
1269
+ * Override this method to provide a custom challenge UI component.
1270
+ * If not overridden (returns null), the login component will use
1271
+ * a default built-in UI.
1272
+ *
1273
+ * @returns Component type extending AXPLoginChallengeComponentBase, or null for default UI
1274
+ */
1275
+ getChallengeComponent() {
1276
+ return null;
1277
+ }
1278
+ }
1279
+ //#endregion
1280
+
1281
+ //#region ---- Injection Token ----
1282
+ /**
1283
+ * Injection token for the login challenge provider
1284
+ *
1285
+ * This token is optional - if not provided, no challenge mechanism will be used.
1286
+ *
1287
+ * @example
1288
+ * ```typescript
1289
+ * // In your app module or provider configuration:
1290
+ * providers: [
1291
+ * {
1292
+ * provide: AXP_LOGIN_CHALLENGE_PROVIDER,
1293
+ * useClass: MyImageCaptchaProvider
1294
+ * }
1295
+ * ]
1296
+ *
1297
+ * // In a component:
1298
+ * private challengeProvider = inject(AXP_LOGIN_CHALLENGE_PROVIDER, { optional: true });
1299
+ * ```
1300
+ */
1301
+ const AXP_LOGIN_CHALLENGE_PROVIDER = new InjectionToken('AXP_LOGIN_CHALLENGE_PROVIDER');
1302
+ //#endregion
1303
+
1304
+ //#region ---- Base Challenge Component ----
1305
+ /**
1306
+ * Base class for login challenge UI components
1307
+ *
1308
+ * Providers can extend this class to create custom challenge UIs.
1309
+ * The login component will render this component and listen to its outputs.
1310
+ *
1311
+ * @example
1312
+ * ```typescript
1313
+ * @Component({
1314
+ * selector: 'my-captcha-challenge',
1315
+ * template: `
1316
+ * <div class="captcha-container">
1317
+ * <img [src]="'data:image/png;base64,' + challengeData().content" />
1318
+ * <input
1319
+ * type="text"
1320
+ * [value]="response()"
1321
+ * (input)="onResponseChange($event)"
1322
+ * />
1323
+ * <button (click)="onRefreshClick()">Refresh</button>
1324
+ * </div>
1325
+ * `
1326
+ * })
1327
+ * export class MyCaptchaChallengeComponent extends AXPLoginChallengeComponentBase {
1328
+ * response = signal('');
1329
+ *
1330
+ * onResponseChange(event: Event) {
1331
+ * const value = (event.target as HTMLInputElement).value;
1332
+ * this.response.set(value);
1333
+ * this.responseChange.emit(value);
1334
+ * }
1335
+ *
1336
+ * onRefreshClick() {
1337
+ * this.refreshRequest.emit();
1338
+ * }
1339
+ * }
1340
+ * ```
1341
+ */
1342
+ class AXPLoginChallengeComponentBase {
1343
+ constructor() {
1344
+ //#region ---- Inputs ----
1345
+ /**
1346
+ * Challenge data to display
1347
+ * Contains the image/content and metadata from the server
1348
+ */
1349
+ this.challengeData = input.required(...(ngDevMode ? [{ debugName: "challengeData" }] : /* istanbul ignore next */ []));
1350
+ /**
1351
+ * Whether the challenge is currently loading (e.g., refreshing)
1352
+ */
1353
+ this.isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
1354
+ //#endregion
1355
+ //#region ---- Outputs ----
1356
+ /**
1357
+ * Emits when the user enters or changes their response
1358
+ * The login component will capture this value and include it in credentials
1359
+ */
1360
+ this.responseChange = output();
1361
+ /**
1362
+ * Emits when the user requests a new challenge (e.g., clicks refresh button)
1363
+ * The login component will call provider.refreshChallenge()
1364
+ */
1365
+ this.refreshRequest = output();
1366
+ }
1367
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLoginChallengeComponentBase, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1368
+ 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 }); }
1369
+ }
1370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLoginChallengeComponentBase, decorators: [{
1371
+ type: Component,
1372
+ args: [{
1373
+ template: '',
1374
+ standalone: true,
1375
+ }]
1376
+ }], 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"] }] } });
1377
+
1378
+ /**
1379
+ * Generated bundle index. Do not edit.
1380
+ */
1381
+
1382
+ export { AXPAuthGuard, AXPAuthModule, AXPAuthStrategy, AXPAuthStrategyRegistryService, AXPFeatureDirective, AXPFeatureGuard, AXPLoginChallengeComponentBase, AXPLoginChallengeProvider, AXPPermissionDefinitionBuilder, AXPPermissionDefinitionGroupBuilder, AXPPermissionDefinitionProviderContext, AXPPermissionDefinitionService, AXPPermissionDefinitionsDataSourceDefinition, AXPPermissionDirective, AXPPermissionEvaluatorScopeProvider, AXPPermissionGuard, AXPPlatformAuthDataSourceKeys, AXPSessionContext, AXPSessionService, AXPSessionStatus, AXPUnauthenticatedError, AXPUnauthorizedError, 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, JwtUtil, PkceUtil, TimeUtil, initializeAppState };
1383
+ //# sourceMappingURL=acorex-platform-framework-client-auth.mjs.map