@acorex/platform 0.0.0-ACOREX

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