@strivacity/sdk-angular 3.0.3 → 4.0.0-beta.0

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 (56) hide show
  1. package/README.md +1991 -609
  2. package/dist/README.md +1991 -609
  3. package/dist/fesm2022/strivacity-sdk-angular-src-server.mjs +221 -0
  4. package/dist/fesm2022/strivacity-sdk-angular-src-server.mjs.map +1 -0
  5. package/dist/fesm2022/strivacity-sdk-angular-src-types.mjs +6 -0
  6. package/dist/fesm2022/strivacity-sdk-angular-src-types.mjs.map +1 -0
  7. package/dist/fesm2022/strivacity-sdk-angular.mjs +284 -498
  8. package/dist/fesm2022/strivacity-sdk-angular.mjs.map +1 -1
  9. package/dist/types/strivacity-sdk-angular-src-server.d.ts +82 -0
  10. package/dist/types/strivacity-sdk-angular-src-types.d.ts +41 -0
  11. package/dist/types/strivacity-sdk-angular.d.ts +147 -0
  12. package/eslint.config.mjs +31 -0
  13. package/ng-package.json +3 -3
  14. package/package.json +29 -11
  15. package/project.json +33 -0
  16. package/src/index.ts +8 -0
  17. package/src/lib/services/auth.service.ts +131 -0
  18. package/src/lib/services/index.ts +2 -0
  19. package/src/lib/services/native-login.service.ts +172 -0
  20. package/src/lib/storages.ts +12 -0
  21. package/src/lib/utils.ts +39 -0
  22. package/src/server/errors.ts +1 -0
  23. package/src/server/index.ts +6 -0
  24. package/src/server/ng-package.json +6 -0
  25. package/src/server/sdk.ts +113 -0
  26. package/src/server/session.ts +30 -0
  27. package/src/server/storages.ts +25 -0
  28. package/src/server/types.ts +32 -0
  29. package/src/server/utils.ts +74 -0
  30. package/src/types/index.ts +47 -0
  31. package/src/types/ng-package.json +6 -0
  32. package/testing/setup.ts +10 -0
  33. package/testing/tests/auth.service.spec.ts +236 -0
  34. package/testing/tests/index.spec.ts +193 -0
  35. package/testing/tests/native-login.service.spec.ts +311 -0
  36. package/testing/tests/server/errors.spec.ts +14 -0
  37. package/testing/tests/server/sdk.spec.ts +197 -0
  38. package/testing/tests/server/session.spec.ts +52 -0
  39. package/testing/tests/server/storages.spec.ts +58 -0
  40. package/testing/tests/server/utils.spec.ts +112 -0
  41. package/testing/tests/storages.spec.ts +31 -0
  42. package/testing/tests/utils.spec.ts +24 -0
  43. package/testing/utils/testbed.ts +26 -0
  44. package/tsconfig.lib.json +13 -0
  45. package/tsconfig.lib.prod.json +9 -0
  46. package/tsconfig.spec.json +8 -0
  47. package/vite.config.mts +11 -0
  48. package/dist/index.d.ts +0 -5
  49. package/dist/lib/components/login-renderer.component.d.ts +0 -38
  50. package/dist/lib/components/widget-renderer.component.d.ts +0 -16
  51. package/dist/lib/services/auth.service.d.ts +0 -93
  52. package/dist/lib/services/widget.service.d.ts +0 -25
  53. package/dist/lib/strivacity-auth.module.d.ts +0 -10
  54. package/dist/lib/utils/helpers.d.ts +0 -16
  55. package/dist/lib/utils/types.d.ts +0 -41
  56. package/dist/public-api.d.ts +0 -16
@@ -1,561 +1,347 @@
1
- import { initFlow, FallbackError } from '@strivacity/sdk-core';
2
- export * from '@strivacity/sdk-core';
3
- export { SDKStorage } from '@strivacity/sdk-core';
4
- export { HttpClient } from '@strivacity/sdk-core/utils/HttpClient';
5
- export { DefaultLogging } from '@strivacity/sdk-core/utils/Logging';
6
- export { LocalStorage } from '@strivacity/sdk-core/storages/LocalStorage';
7
- export { SessionStorage } from '@strivacity/sdk-core/storages/SessionStorage';
8
- export { createCredential, getCredential } from '@strivacity/sdk-core/utils/credentials';
1
+ export * from '@strivacity/sdk-core/flows';
2
+ export * from '@strivacity/sdk-core/handlers';
3
+ import { unflattenObject } from '@strivacity/sdk-core/utils';
4
+ export * from '@strivacity/sdk-core/utils';
9
5
  import * as i0 from '@angular/core';
10
- import { Injectable, ViewContainerRef, ViewChild, Input, Component, InjectionToken, Inject, EventEmitter, Output, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
11
- import { BehaviorSubject, from, Subscription } from 'rxjs';
12
- import { unflattenObject } from '@strivacity/sdk-core/utils/object';
6
+ import { InjectionToken, CUSTOM_ELEMENTS_SCHEMA, NgModule, makeStateKey, provideAppInitializer, inject, REQUEST, TransferState, signal, PLATFORM_ID, DestroyRef, Injectable } from '@angular/core';
7
+ import { isPlatformBrowser } from '@angular/common';
8
+ import { initFlow } from '@strivacity/sdk-core';
9
+ import { FallbackError } from '@strivacity/sdk-core/utils/errors';
10
+ export { COOKIE_CHUNK_SIZE, COOKIE_CONTEXT, createCacheAPIStorage, createIndexedDBStorage, createLocalStorage, createMemoryStorage, createServerMemoryStorage, createSessionStorage, createWorkerStorage, handleWorkerStorageRequests } from '@strivacity/sdk-core/storages';
13
11
 
12
+ const STRIVACITY_SDK = new InjectionToken('strivacity-sdk');
14
13
  /**
15
- * Service that manages Strivacity native widgets.
14
+ * Provides the Strivacity SDK configuration and the `StrivacityAuthService` as dependency injection providers.
15
+ *
16
+ * This function is used to supply the Strivacity SDK configuration to the application
17
+ * by binding it to the `STRIVACITY_SDK` token, and to register `StrivacityAuthService` as an injectable.
18
+ *
19
+ * @param {AngularSDKInitConfig} config The SDK configuration options.
20
+ * @returns {Provider[]} The providers to add to the application (or component) injector.
16
21
  */
17
- class StrivacityWidgetService {
18
- loading$ = new BehaviorSubject(false);
19
- forms$ = new BehaviorSubject({});
20
- messages$ = new BehaviorSubject({});
21
- state$ = new BehaviorSubject({});
22
- triggerFallback;
23
- triggerClose;
24
- submitForm;
25
- setFormValue(formId, widgetId, value) {
26
- const forms = { ...this.forms$.value };
27
- forms[formId] = { ...(forms[formId] || {}), [widgetId]: value === '' ? null : value };
28
- this.forms$.next(forms);
29
- }
30
- setMessage(formId, widgetId, value) {
31
- const messages = { ...this.messages$.value };
32
- messages[formId] = { ...(messages[formId] || {}), [widgetId]: value };
33
- this.messages$.next(messages);
22
+ function provideStrivacity(config) {
23
+ return [{ provide: STRIVACITY_SDK, useValue: config }, StrivacityAuthService];
24
+ }
25
+ class StrivacityAuthModule {
26
+ /**
27
+ * Registers the Strivacity SDK configuration and the `StrivacityAuthService` as dependency injection providers.
28
+ *
29
+ * This is the `NgModule`-based equivalent of `provideStrivacity()`, for applications that still bootstrap via `NgModule`.
30
+ *
31
+ * @param {AngularSDKInitConfig} options The SDK configuration options.
32
+ * @returns {ModuleWithProviders<StrivacityAuthModule>} The module along with its providers, to be imported once at the root of the application.
33
+ */
34
+ static forRoot(options) {
35
+ return {
36
+ ngModule: StrivacityAuthModule,
37
+ providers: [provideStrivacity(options)],
38
+ };
34
39
  }
35
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityWidgetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
36
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityWidgetService, providedIn: 'root' });
40
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
41
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.4", ngImport: i0, type: StrivacityAuthModule }); }
42
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityAuthModule }); }
37
43
  }
38
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityWidgetService, decorators: [{
39
- type: Injectable,
44
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityAuthModule, decorators: [{
45
+ type: NgModule,
40
46
  args: [{
41
- providedIn: 'root',
47
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
42
48
  }]
43
49
  }] });
44
50
 
45
- class StyWidgetRenderer {
46
- widgetService;
47
- items = [];
48
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
- widgets;
50
- $containerRef;
51
- constructor(widgetService) {
52
- this.widgetService = widgetService;
53
- }
54
- ngOnChanges(changes) {
55
- if (changes['items']) {
56
- this.render();
57
- }
58
- }
59
- render() {
60
- this.$containerRef.clear();
61
- if (!this.items) {
62
- return;
63
- }
64
- for (const item of this.items) {
65
- if (item.type === 'widget') {
66
- const form = this.widgetService.state$.value.forms?.find((f) => f.id === item.formId);
67
- const widget = form?.widgets.find((w) => w.id === item.widgetId);
68
- const component = widget ? this.widgets[widget.type] : undefined;
69
- if (!form || !widget) {
70
- this.widgetService.triggerFallback(undefined, `Unable to find form or widget for item: formId=${item.formId}, widgetId=${item.widgetId}`);
71
- continue;
72
- }
73
- if (!component) {
74
- this.widgetService.triggerFallback(undefined, `No component found for widget type ${widget.type}`);
75
- continue;
76
- }
77
- const componentRef = this.$containerRef.createComponent(component);
78
- componentRef.setInput('formId', item.formId);
79
- componentRef.setInput('config', widget);
80
- componentRef.changeDetectorRef.detectChanges();
81
- }
82
- else if (item.type === 'vertical' || item.type === 'horizontal') {
83
- const component = this.widgets.layout;
84
- if (!component) {
85
- this.widgetService.triggerFallback(undefined, 'No layout component provided');
86
- continue;
87
- }
88
- const widgetRendererRef = this.$containerRef.createComponent(StyWidgetRenderer);
89
- widgetRendererRef.setInput('items', item.items);
90
- widgetRendererRef.setInput('widgets', this.widgets);
91
- widgetRendererRef.changeDetectorRef.detectChanges();
92
- const layoutRef = this.$containerRef.createComponent(component, {
93
- projectableNodes: [[widgetRendererRef.location.nativeElement]],
94
- });
95
- layoutRef.setInput('formId', item.items[0]?.formId);
96
- layoutRef.setInput('type', item.type);
97
- layoutRef.changeDetectorRef.detectChanges();
98
- }
99
- else {
100
- this.widgetService.triggerFallback(undefined, 'Unknown item type in layout');
101
- }
102
- }
103
- }
104
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StyWidgetRenderer, deps: [{ token: StrivacityWidgetService }], target: i0.ɵɵFactoryTarget.Component });
105
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: StyWidgetRenderer, isStandalone: true, selector: "sty-widget-renderer", inputs: { items: "items", widgets: "widgets" }, viewQueries: [{ propertyName: "$containerRef", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: '<ng-container #container></ng-container>', isInline: true, styles: [":host{display:contents}\n"] });
106
- }
107
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StyWidgetRenderer, decorators: [{
108
- type: Component,
109
- args: [{ standalone: true, selector: 'sty-widget-renderer', template: '<ng-container #container></ng-container>', styles: [":host{display:contents}\n"] }]
110
- }], ctorParameters: () => [{ type: StrivacityWidgetService }], propDecorators: { items: [{
111
- type: Input
112
- }], widgets: [{
113
- type: Input,
114
- args: [{ required: true }]
115
- }], $containerRef: [{
116
- type: ViewChild,
117
- args: ['container', { read: ViewContainerRef, static: true }]
118
- }] } });
119
-
120
- const STRIVACITY_SDK = new InjectionToken('sty');
51
+ const SESSION_TRANSFER_KEY = makeStateKey('sty.session');
121
52
  /**
122
- * Provides the Strivacity SDK configuration as a dependency injection token.
53
+ * Loads the current session from the encrypted cookie storage (using Angular's `REQUEST` token) before the app
54
+ * renders, and hands it over to the client through `TransferState`, so `StrivacityAuthService` can hydrate without
55
+ * an extra round-trip or a loading flash.
123
56
  *
124
- * This function is used to supply the Strivacity SDK configuration to the application
125
- * by binding it to the `STRIVACITY_SDK` token.
57
+ * Add this to the server-only `ApplicationConfig` (e.g. `app.config.server.ts`), alongside `provideServerRendering()`.
126
58
  *
127
- * @param {SDKOptions} config The SDK configuration options.
128
- * @returns {{ provide: InjectionToken<SDKOptions>, useValue: SDKOptions }} An object that provides the SDK configuration using the `STRIVACITY_SDK` token.
59
+ * @param {AngularServerSDK | undefined} serverSdk - The server SDK instance created via `createServerSDK()`, or `undefined` when server-side sessions aren't configured (a no-op in that case).
60
+ * @returns {EnvironmentProviders} The providers to add to the server `ApplicationConfig`.
129
61
  */
130
- function provideStrivacity(config) {
131
- return { provide: STRIVACITY_SDK, useValue: config };
62
+ function provideStrivacityServerSession(serverSdk) {
63
+ return provideAppInitializer(async () => {
64
+ const request = inject(REQUEST);
65
+ if (!request || !serverSdk) {
66
+ return;
67
+ }
68
+ const transferState = inject(TransferState);
69
+ const session = await serverSdk.getSession(request);
70
+ transferState.set(SESSION_TRANSFER_KEY, session);
71
+ });
132
72
  }
133
73
 
134
74
  /**
135
- * Service that manages Strivacity authentication flows.
136
- * Supports either PopupFlow or RedirectFlow types.
75
+ * Signal-based service exposing the Strivacity SDK state and actions to Angular components.
137
76
  *
138
- * @template Flow Type of authentication flow (PopupFlow or RedirectFlow).
139
- * @template Options Type of SDK options (defaults to SDKOptions).
77
+ * Provided via `provideStrivacity()` (standalone) or `StrivacityAuthModule.forRoot()` (NgModule).
140
78
  */
141
79
  class StrivacityAuthService {
142
- options;
143
- /**
144
- * Instance of the authentication flow (PopupFlow or RedirectFlow).
145
- */
146
- sdk;
147
- /**
148
- * BehaviorSubject that holds the current session state.
149
- * @protected
150
- * @readonly
151
- */
152
- sessionSubject;
153
- /**
154
- * Observable that emits the session state changes.
155
- * @readonly
156
- */
157
- session$;
158
- /**
159
- * Creates an instance of StrivacityAuthService.
160
- *
161
- * @param {Options} options SDK configuration options injected via STRIVACITY_SDK.
162
- */
163
- constructor(options) {
164
- this.options = options;
165
- this.sdk = initFlow(options);
166
- this.sessionSubject = new BehaviorSubject({
167
- loading: true,
168
- isAuthenticated: false,
169
- idTokenClaims: null,
170
- accessToken: null,
171
- refreshToken: null,
172
- accessTokenExpired: true,
173
- accessTokenExpirationDate: null,
174
- });
175
- this.session$ = this.sessionSubject.asObservable();
176
- const updateSession = async () => {
177
- this.sessionSubject.next({
178
- loading: false,
179
- isAuthenticated: await this.sdk.isAuthenticated,
180
- idTokenClaims: this.sdk.idTokenClaims || null,
181
- accessToken: this.sdk.accessToken || null,
182
- refreshToken: this.sdk.refreshToken || null,
183
- accessTokenExpired: this.sdk.accessTokenExpired,
184
- accessTokenExpirationDate: this.sdk.accessTokenExpirationDate || null,
185
- });
186
- };
187
- this.sdk.subscribeToEvent('init', updateSession);
188
- this.sdk.subscribeToEvent('loggedIn', updateSession);
189
- this.sdk.subscribeToEvent('sessionLoaded', updateSession);
190
- this.sdk.subscribeToEvent('tokenRefreshed', updateSession);
191
- this.sdk.subscribeToEvent('tokenRefreshFailed', updateSession);
192
- this.sdk.subscribeToEvent('logoutInitiated', updateSession);
193
- this.sdk.subscribeToEvent('tokenRevoked', updateSession);
194
- this.sdk.subscribeToEvent('tokenRevokeFailed', updateSession);
80
+ constructor() {
81
+ this.loadingSignal = signal(true, /* @ts-ignore */
82
+ ...(ngDevMode ? [{ debugName: "loadingSignal" }] : /* istanbul ignore next */ []));
83
+ this.languageSignal = signal(globalThis.navigator?.language ?? 'en-US', /* @ts-ignore */
84
+ ...(ngDevMode ? [{ debugName: "languageSignal" }] : /* istanbul ignore next */ []));
85
+ this.isAuthenticatedSignal = signal(false, /* @ts-ignore */
86
+ ...(ngDevMode ? [{ debugName: "isAuthenticatedSignal" }] : /* istanbul ignore next */ []));
87
+ this.idTokenClaimsSignal = signal(null, /* @ts-ignore */
88
+ ...(ngDevMode ? [{ debugName: "idTokenClaimsSignal" }] : /* istanbul ignore next */ []));
89
+ this.accessTokenSignal = signal(null, /* @ts-ignore */
90
+ ...(ngDevMode ? [{ debugName: "accessTokenSignal" }] : /* istanbul ignore next */ []));
91
+ this.refreshTokenSignal = signal(null, /* @ts-ignore */
92
+ ...(ngDevMode ? [{ debugName: "refreshTokenSignal" }] : /* istanbul ignore next */ []));
93
+ this.accessTokenExpiredSignal = signal(true, /* @ts-ignore */
94
+ ...(ngDevMode ? [{ debugName: "accessTokenExpiredSignal" }] : /* istanbul ignore next */ []));
95
+ this.accessTokenExpirationDateSignal = signal(null, /* @ts-ignore */
96
+ ...(ngDevMode ? [{ debugName: "accessTokenExpirationDateSignal" }] : /* istanbul ignore next */ []));
97
+ this.options = inject(STRIVACITY_SDK);
98
+ this.sdk = initFlow(this.options);
99
+ this.loading = this.loadingSignal.asReadonly();
100
+ this.language = this.languageSignal.asReadonly();
101
+ this.isAuthenticated = this.isAuthenticatedSignal.asReadonly();
102
+ this.idTokenClaims = this.idTokenClaimsSignal.asReadonly();
103
+ this.accessToken = this.accessTokenSignal.asReadonly();
104
+ this.refreshToken = this.refreshTokenSignal.asReadonly();
105
+ this.accessTokenExpired = this.accessTokenExpiredSignal.asReadonly();
106
+ this.accessTokenExpirationDate = this.accessTokenExpirationDateSignal.asReadonly();
107
+ if (this.options.serverSessionUri) {
108
+ const transferState = inject(TransferState);
109
+ const hydratedSession = transferState.get(SESSION_TRANSFER_KEY, undefined);
110
+ if (hydratedSession) {
111
+ this.sdk.session = hydratedSession;
112
+ if (isPlatformBrowser(inject(PLATFORM_ID))) {
113
+ transferState.remove(SESSION_TRANSFER_KEY);
114
+ }
115
+ }
116
+ }
117
+ void this.updateSession();
118
+ const subscription = this.sdk.subscribeToAllEvents(() => this.updateSession());
119
+ inject(DestroyRef).onDestroy(() => subscription.dispose());
195
120
  }
196
- /**
197
- * Checks if the user is authenticated.
198
- *
199
- * @returns {Observable<boolean>} An observable that emits the authentication status.
200
- */
201
- isAuthenticated() {
202
- return from(this.sdk.isAuthenticated);
121
+ init(...args) {
122
+ return this.sdk.init(...args);
203
123
  }
204
- /**
205
- * Logs the user in using the specified options.
206
- *
207
- * @param {Parameters<Flow['login']>[0]} [options] Options to customize the login behavior.
208
- * @returns {Observable<void>} An observable that completes when the login process is done.
209
- */
210
- login(options) {
211
- const result = this.sdk.login(options);
212
- if (result instanceof Promise) {
213
- return from(result);
214
- }
215
- return result;
124
+ subscribeToEvent(...args) {
125
+ return this.sdk.subscribeToEvent(...args);
216
126
  }
217
- /**
218
- * Initiates the entry process using the provided challenge.
219
- *
220
- * @param {string} [url] Optional URL to use for the entry process. If not provided, the current window location will be used.
221
- * @returns {Observable<void>} An observable that completes when the entry process is done.
222
- */
223
- entry(url) {
224
- const result = this.sdk.entry(url);
225
- if (result instanceof Promise) {
226
- return from(result);
227
- }
228
- return result;
127
+ subscribeToAllEvents(...args) {
128
+ return this.sdk.subscribeToAllEvents(...args);
229
129
  }
230
- /**
231
- * Registers a new user using the specified options.
232
- *
233
- * @param {Parameters<Flow['register']>[0]} [options] Options to customize the registration behavior.
234
- * @returns {Observable<void>} An observable that completes when the registration process is done.
235
- */
236
- register(options) {
237
- const result = this.sdk.register(options);
238
- if (result instanceof Promise) {
239
- return from(result);
240
- }
241
- return result;
130
+ checkAuthentication(...args) {
131
+ return this.sdk.checkAuthentication(...args);
132
+ }
133
+ tokenExchange(...args) {
134
+ return this.sdk.tokenExchange(...args);
135
+ }
136
+ handleCallback(...args) {
137
+ return this.sdk.handleCallback(...args);
242
138
  }
243
- /**
244
- * Refreshes the current authentication session.
245
- *
246
- * @returns {Observable<void>} An observable that completes when the session is refreshed.
247
- */
248
139
  refresh() {
249
- return from(this.sdk.refresh());
140
+ return this.sdk.refresh();
250
141
  }
251
- /**
252
- * Revokes the current session tokens.
253
- *
254
- * @returns {Observable<void>} An observable that completes when the tokens are revoked.
255
- */
256
142
  revoke() {
257
- return from(this.sdk.revoke());
143
+ return this.sdk.revoke();
258
144
  }
259
- /**
260
- * Logs the user out using the specified options.
261
- *
262
- * @param {Parameters<Flow['logout']>[0]} [options] Options to customize the logout behavior.
263
- * @returns {Observable<void>} An observable that completes when the logout process is done.
264
- */
265
- logout(options) {
266
- return from(this.sdk.logout(options));
145
+ logout(...args) {
146
+ return this.sdk.logout(...args);
147
+ }
148
+ login(...args) {
149
+ return this.sdk.login(...args);
150
+ }
151
+ register(...args) {
152
+ return this.sdk.register(...args);
153
+ }
154
+ entry(...args) {
155
+ return this.sdk.entry(...args);
156
+ }
157
+ async updateSession() {
158
+ const authenticated = await this.sdk.isAuthenticated;
159
+ if (this.loadingSignal()) {
160
+ this.loadingSignal.set(false);
161
+ }
162
+ if (this.sdk.language !== this.languageSignal()) {
163
+ this.languageSignal.set(this.sdk.language);
164
+ }
165
+ if (authenticated !== this.isAuthenticatedSignal()) {
166
+ this.isAuthenticatedSignal.set(authenticated);
167
+ }
168
+ if (this.sdk.idTokenClaims !== this.idTokenClaimsSignal()) {
169
+ this.idTokenClaimsSignal.set(this.sdk.idTokenClaims ?? null);
170
+ }
171
+ if (this.sdk.accessToken !== this.accessTokenSignal()) {
172
+ this.accessTokenSignal.set(this.sdk.accessToken ?? null);
173
+ }
174
+ if (this.sdk.refreshToken !== this.refreshTokenSignal()) {
175
+ this.refreshTokenSignal.set(this.sdk.refreshToken ?? null);
176
+ }
177
+ if (this.sdk.accessTokenExpired !== this.accessTokenExpiredSignal()) {
178
+ this.accessTokenExpiredSignal.set(this.sdk.accessTokenExpired);
179
+ }
180
+ if (this.sdk.accessTokenExpirationDate !== this.accessTokenExpirationDateSignal()) {
181
+ this.accessTokenExpirationDateSignal.set(this.sdk.accessTokenExpirationDate ?? null);
182
+ }
183
+ }
184
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
185
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityAuthService }); }
186
+ }
187
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityAuthService, decorators: [{
188
+ type: Injectable
189
+ }], ctorParameters: () => [] });
190
+
191
+ class StrivacityNativeLoginService {
192
+ constructor() {
193
+ this.authService = inject(StrivacityAuthService);
194
+ this.sdk = this.authService.sdk;
195
+ this.abortController = new AbortController();
196
+ this.options = {};
197
+ this.loadingSignal = signal(true, /* @ts-ignore */
198
+ ...(ngDevMode ? [{ debugName: "loadingSignal" }] : /* istanbul ignore next */ []));
199
+ this.formsSignal = signal({}, /* @ts-ignore */
200
+ ...(ngDevMode ? [{ debugName: "formsSignal" }] : /* istanbul ignore next */ []));
201
+ this.messagesSignal = signal({}, /* @ts-ignore */
202
+ ...(ngDevMode ? [{ debugName: "messagesSignal" }] : /* istanbul ignore next */ []));
203
+ this.stateSignal = signal({}, /* @ts-ignore */
204
+ ...(ngDevMode ? [{ debugName: "stateSignal" }] : /* istanbul ignore next */ []));
205
+ this.loading = this.loadingSignal.asReadonly();
206
+ this.forms = this.formsSignal.asReadonly();
207
+ this.messages = this.messagesSignal.asReadonly();
208
+ this.state = this.stateSignal.asReadonly();
209
+ inject(DestroyRef).onDestroy(() => this.abortController.abort());
267
210
  }
268
211
  /**
269
- * Handles the authentication callback (e.g., after a redirect or popup flow).
212
+ * Starts a native login flow session. Should be called once, from the page component that owns this service.
270
213
  *
271
- * @param {Parameters<Flow['handleCallback']>[0]} [url] The URL to handle for the callback.
272
- * @returns {Observable<void>} An observable that completes when the callback is handled.
214
+ * @param {NativeLoginOptions} [options] - Optional options for the login session.
273
215
  */
274
- handleCallback(url) {
275
- return from(this.sdk.handleCallback(url));
216
+ async start(options = {}) {
217
+ this.options = options;
218
+ await this.sdk.init();
219
+ try {
220
+ await this.startSession(options.params);
221
+ }
222
+ catch (error) {
223
+ if (error instanceof DOMException && error.name === 'AbortError') {
224
+ return;
225
+ }
226
+ this.sdk.logging?.error('Error fetching authorization URI', error);
227
+ await this.options.onError?.(error);
228
+ this.loadingSignal.set(false);
229
+ }
276
230
  }
277
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityAuthService, deps: [{ token: STRIVACITY_SDK }], target: i0.ɵɵFactoryTarget.Injectable });
278
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityAuthService, providedIn: 'root' });
279
- }
280
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityAuthService, decorators: [{
281
- type: Injectable,
282
- args: [{
283
- providedIn: 'root',
284
- }]
285
- }], ctorParameters: () => [{ type: undefined, decorators: [{
286
- type: Inject,
287
- args: [STRIVACITY_SDK]
288
- }] }] });
289
-
290
- class StyLoginRenderer {
291
- authService;
292
- widgetService;
293
- subscriptions = new Subscription();
294
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
295
- createdComponentRefs = [];
296
- loginHandler;
297
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
298
- widgets;
299
- sessionId;
300
- language;
301
- params = {};
302
- onLogin = new EventEmitter();
303
- onFallback = new EventEmitter();
304
- onClose = new EventEmitter();
305
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
306
- onError = new EventEmitter();
307
- onGlobalMessage = new EventEmitter();
308
- onBlockReady = new EventEmitter();
309
- onLanguageChange = new EventEmitter();
310
- $containerRef;
311
- constructor(authService, widgetService) {
312
- this.authService = authService;
313
- this.widgetService = widgetService;
314
- this.widgetService.triggerFallback = (hostedUrl, message) => {
315
- const url = hostedUrl || this.widgetService.state$.value.hostedUrl;
316
- this.authService.sdk.logging?.warn(message ? `Triggering fallback due to: ${message}` : 'Triggering fallback');
317
- if (!url) {
318
- const error = new Error('No hosted URL provided');
319
- this.authService.sdk.logging?.error('Fallback error', error);
320
- throw error;
231
+ async submitForm(formId, customBody) {
232
+ try {
233
+ this.loadingSignal.set(true);
234
+ const nextState = await this.sdk.submitForm(formId, customBody ?? unflattenObject(this.formsSignal()[formId] ?? {}));
235
+ if (nextState) {
236
+ await this.handleResponse(nextState);
321
237
  }
322
- this.onFallback.emit(new FallbackError(new URL(url)));
323
- };
324
- this.widgetService.triggerClose = () => {
325
- this.onClose.emit();
326
- };
327
- this.widgetService.submitForm = async (formId) => {
328
- try {
329
- this.widgetService.loading$.next(true);
330
- const data = await this.loginHandler.submitForm(formId, unflattenObject(this.widgetService.forms$.value[formId]));
331
- if (await this.authService.sdk.isAuthenticated) {
332
- this.onLogin.emit(this.authService.sdk.idTokenClaims);
333
- }
334
- else {
335
- const previousState = JSON.parse(JSON.stringify(this.widgetService.state$.value));
336
- const newState = {
337
- hostedUrl: data?.hostedUrl ?? this.widgetService.state$.value.hostedUrl,
338
- finalizeUrl: data?.finalizeUrl ?? this.widgetService.state$.value.finalizeUrl,
339
- screen: data?.screen ?? this.widgetService.state$.value.screen,
340
- forms: data?.forms ?? this.widgetService.state$.value.forms,
341
- layout: data?.layout ?? this.widgetService.state$.value.layout,
342
- messages: data?.messages ?? {},
343
- branding: data?.branding ?? this.widgetService.state$.value.branding,
344
- };
345
- if (newState.screen !== this.widgetService.state$.value.screen) {
346
- const forms = {};
347
- const messages = {};
348
- for (const form of newState.forms ?? []) {
349
- forms[form.id] = {};
350
- messages[form.id] = {};
351
- }
352
- this.widgetService.forms$.next(forms);
353
- this.widgetService.messages$.next(messages);
354
- }
355
- else {
356
- this.authService.sdk.logging?.info(`Updating screen: ${newState.screen}`);
357
- }
358
- Object.keys(newState.messages ?? {}).forEach((formId) => {
359
- if (formId === 'global') {
360
- this.onGlobalMessage.emit(newState.messages?.global?.text ?? '');
361
- }
362
- else {
363
- const messages = { ...this.widgetService.messages$.value };
364
- messages[formId] = newState.messages[formId];
365
- this.widgetService.messages$.next(messages);
366
- }
367
- });
368
- this.widgetService.state$.next(newState);
369
- this.onBlockReady.emit({ previousState, state: structuredClone(newState) });
370
- this.widgetService.loading$.next(false);
371
- }
238
+ }
239
+ catch (error) {
240
+ if (error instanceof FallbackError) {
241
+ this.sdk.logging?.error('Fallback error occurred', error);
242
+ await this.options.onFallback?.(error);
372
243
  }
373
- catch (error) {
374
- if (error instanceof FallbackError) {
375
- this.onFallback.emit(error);
376
- }
377
- else {
378
- this.onError.emit(error);
379
- }
244
+ else {
245
+ this.sdk.logging?.error('Error submitting form', error);
246
+ await this.options.onError?.(error);
380
247
  }
381
- };
248
+ }
382
249
  }
383
- ngOnInit() {
384
- this.subscriptions.add(this.widgetService.state$.subscribe((state) => {
385
- this.render(state);
386
- }));
387
- void this.init();
250
+ setFormValue(formId, widgetId, value) {
251
+ const forms = { ...this.formsSignal() };
252
+ forms[formId] = { ...(forms[formId] ?? {}), [widgetId]: value === '' ? null : value };
253
+ this.formsSignal.set(forms);
254
+ }
255
+ setMessage(formId, widgetId, value) {
256
+ const messages = { ...this.messagesSignal() };
257
+ messages[formId] = { ...(messages[formId] ?? {}), [widgetId]: value };
258
+ this.messagesSignal.set(messages);
259
+ }
260
+ triggerFallback(message) {
261
+ this.sdk.logging?.warn(message ? `Triggering fallback due to: ${message}` : 'Triggering fallback');
262
+ const hostedUrl = this.stateSignal().hostedUrl;
263
+ if (!hostedUrl) {
264
+ const error = new Error('No hosted URL provided');
265
+ this.sdk.logging?.error('Fallback error', error);
266
+ throw error;
267
+ }
268
+ void this.options.onFallback?.(new FallbackError(new URL(hostedUrl)));
388
269
  }
389
- ngOnDestroy() {
390
- this.subscriptions.unsubscribe();
391
- this.clearAndDestroyComponents();
392
- this.widgetService.loading$.next(false);
393
- this.widgetService.state$.next({});
394
- this.widgetService.forms$.next({});
395
- this.widgetService.messages$.next({});
270
+ triggerClose() {
271
+ this.sdk.logging?.debug('Triggering close');
272
+ void this.options.onClose?.();
396
273
  }
397
- async init() {
274
+ async startSession(loginParams = {}) {
398
275
  try {
399
- this.loginHandler = this.authService.sdk.login(this.params);
400
- const data = (await this.loginHandler.startSession(this.sessionId, this.language));
401
- const previousState = JSON.parse(JSON.stringify(this.widgetService.state$.value));
402
- const newState = {
403
- hostedUrl: data?.hostedUrl ?? this.widgetService.state$.value.hostedUrl,
404
- finalizeUrl: data?.finalizeUrl ?? this.widgetService.state$.value.finalizeUrl,
405
- screen: data?.screen ?? this.widgetService.state$.value.screen,
406
- forms: data?.forms ?? this.widgetService.state$.value.forms,
407
- layout: data?.layout ?? this.widgetService.state$.value.layout,
408
- messages: data?.messages ?? {},
409
- branding: data?.branding ?? this.widgetService.state$.value.branding,
410
- };
411
- this.onLanguageChange.emit(this.loginHandler.language);
412
- if (await this.authService.sdk.isAuthenticated) {
413
- this.onLogin.emit(this.authService.sdk.idTokenClaims);
414
- }
415
- else {
416
- if (newState.screen !== this.widgetService.state$.value.screen) {
417
- const newFormContexts = {};
418
- const newMessageContexts = {};
419
- for (const form of newState.forms ?? []) {
420
- newFormContexts[form.id] = {};
421
- newMessageContexts[form.id] = {};
422
- }
423
- this.widgetService.forms$.next(newFormContexts);
424
- this.widgetService.messages$.next(newMessageContexts);
425
- }
426
- Object.keys(newState.messages ?? {}).forEach((formId) => {
427
- if (formId === 'global') {
428
- this.onGlobalMessage.emit(newState.messages?.global?.text ?? '');
429
- }
430
- else {
431
- const messages = { ...this.widgetService.messages$.value };
432
- messages[formId] = newState.messages[formId];
433
- this.widgetService.messages$.next(messages);
434
- }
435
- });
436
- this.widgetService.state$.next(newState);
437
- this.onBlockReady.emit({ previousState, state: structuredClone(newState) });
276
+ this.loadingSignal.set(true);
277
+ const nextState = await this.sdk.startSession(loginParams);
278
+ if (nextState) {
279
+ await this.handleResponse(nextState);
438
280
  }
439
281
  }
440
282
  catch (error) {
441
283
  if (error instanceof FallbackError) {
442
- this.onFallback.emit(error);
284
+ this.sdk.logging?.error('Fallback error occurred', error);
285
+ await this.options.onFallback?.(error);
443
286
  }
444
287
  else {
445
- this.onError.emit(error);
288
+ this.sdk.logging?.error('Error starting session', error);
289
+ await this.options.onError?.(error);
446
290
  }
447
291
  }
448
292
  }
449
- render(state) {
450
- this.clearAndDestroyComponents();
451
- if (state.screen) {
452
- const component = this.widgets['layout'];
453
- if (!component) {
454
- this.widgetService.triggerFallback(undefined, 'No layout component provided');
293
+ async handleResponse(nextState) {
294
+ if (this.sdk.session) {
295
+ return await this.options.onLogin?.(this.sdk.session);
296
+ }
297
+ const currentState = this.stateSignal();
298
+ const newState = {
299
+ hostedUrl: nextState.hostedUrl ?? currentState.hostedUrl,
300
+ finalizeUrl: nextState.finalizeUrl ?? currentState.finalizeUrl,
301
+ screen: nextState.screen ?? currentState.screen,
302
+ forms: nextState.forms ?? currentState.forms,
303
+ layout: nextState.layout ?? currentState.layout,
304
+ messages: nextState.messages ?? {},
305
+ branding: nextState.branding ?? currentState.branding,
306
+ };
307
+ if (newState.screen !== currentState.screen) {
308
+ const nextForms = {};
309
+ const nextMessages = {};
310
+ for (const form of newState.forms ?? []) {
311
+ nextForms[form.id] = {};
312
+ nextMessages[form.id] = {};
455
313
  }
456
- const widgetRendererRef = this.$containerRef.createComponent(StyWidgetRenderer);
457
- widgetRendererRef.setInput('items', state.layout?.items);
458
- widgetRendererRef.setInput('widgets', this.widgets);
459
- widgetRendererRef.changeDetectorRef.detectChanges();
460
- const layoutRef = this.$containerRef.createComponent(component, {
461
- projectableNodes: [[widgetRendererRef.location.nativeElement]],
462
- });
463
- layoutRef.setInput('formId', state.layout?.items[0]?.formId);
464
- layoutRef.setInput('type', state.layout?.type);
465
- layoutRef.setInput('tag', 'form');
466
- layoutRef.changeDetectorRef.detectChanges();
467
- this.createdComponentRefs.push(widgetRendererRef, layoutRef);
314
+ this.formsSignal.set(nextForms);
315
+ this.messagesSignal.set(nextMessages);
468
316
  }
469
317
  else {
470
- const loadingComponentType = this.widgets['loading'];
471
- if (loadingComponentType) {
472
- const loadingRef = this.$containerRef.createComponent(loadingComponentType);
473
- this.createdComponentRefs.push(loadingRef);
474
- }
318
+ this.sdk.logging?.info(`Updating screen: ${newState.screen}`);
475
319
  }
476
- }
477
- clearAndDestroyComponents() {
478
- this.createdComponentRefs.forEach((ref) => {
479
- if (!ref.hostView.destroyed) {
480
- ref.destroy();
320
+ const nextMessageMap = { ...this.messagesSignal() };
321
+ for (const formId of Object.keys(newState.messages ?? {})) {
322
+ if (formId === 'global') {
323
+ await this.options.onGlobalMessage?.(newState.messages.global);
481
324
  }
482
- });
483
- this.createdComponentRefs = [];
484
- this.$containerRef?.clear();
485
- }
486
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StyLoginRenderer, deps: [{ token: StrivacityAuthService }, { token: StrivacityWidgetService }], target: i0.ɵɵFactoryTarget.Component });
487
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: StyLoginRenderer, isStandalone: true, selector: "sty-login-renderer", inputs: { widgets: "widgets", sessionId: "sessionId", language: "language", params: "params" }, outputs: { onLogin: "login", onFallback: "fallback", onClose: "close", onError: "error", onGlobalMessage: "globalMessage", onBlockReady: "blockReady", onLanguageChange: "languageChange" }, host: { classAttribute: "login-renderer" }, viewQueries: [{ propertyName: "$containerRef", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: '<ng-container #container></ng-container>', isInline: true });
488
- }
489
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StyLoginRenderer, decorators: [{
490
- type: Component,
491
- args: [{
492
- standalone: true,
493
- // eslint-disable-next-line @angular-eslint/component-selector
494
- selector: 'sty-login-renderer',
495
- template: '<ng-container #container></ng-container>',
496
- host: {
497
- class: 'login-renderer',
498
- },
499
- }]
500
- }], ctorParameters: () => [{ type: StrivacityAuthService }, { type: StrivacityWidgetService }], propDecorators: { widgets: [{
501
- type: Input,
502
- args: [{ required: true }]
503
- }], sessionId: [{
504
- type: Input
505
- }], language: [{
506
- type: Input
507
- }], params: [{
508
- type: Input
509
- }], onLogin: [{
510
- type: Output,
511
- args: ['login']
512
- }], onFallback: [{
513
- type: Output,
514
- args: ['fallback']
515
- }], onClose: [{
516
- type: Output,
517
- args: ['close']
518
- }], onError: [{
519
- type: Output,
520
- args: ['error']
521
- }], onGlobalMessage: [{
522
- type: Output,
523
- args: ['globalMessage']
524
- }], onBlockReady: [{
525
- type: Output,
526
- args: ['blockReady']
527
- }], onLanguageChange: [{
528
- type: Output,
529
- args: ['languageChange']
530
- }], $containerRef: [{
531
- type: ViewChild,
532
- args: ['container', { read: ViewContainerRef, static: true }]
533
- }] } });
534
-
535
- class StrivacityAuthModule {
536
- static forRoot(options) {
537
- return {
538
- ngModule: StrivacityAuthModule,
539
- providers: [provideStrivacity(options)],
540
- };
325
+ else {
326
+ nextMessageMap[formId] = newState.messages[formId] ?? {};
327
+ }
328
+ }
329
+ this.messagesSignal.set(nextMessageMap);
330
+ this.stateSignal.set(newState);
331
+ if (!newState.finalizeUrl) {
332
+ this.loadingSignal.set(false);
333
+ }
541
334
  }
542
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityAuthModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
543
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: StrivacityAuthModule, imports: [StyLoginRenderer], exports: [StyLoginRenderer] });
544
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityAuthModule, providers: [StrivacityAuthService] });
335
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityNativeLoginService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
336
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityNativeLoginService }); }
545
337
  }
546
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StrivacityAuthModule, decorators: [{
547
- type: NgModule,
548
- args: [{
549
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
550
- providers: [StrivacityAuthService],
551
- imports: [StyLoginRenderer],
552
- exports: [StyLoginRenderer],
553
- }]
554
- }] });
338
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrivacityNativeLoginService, decorators: [{
339
+ type: Injectable
340
+ }], ctorParameters: () => [] });
555
341
 
556
342
  /**
557
343
  * Generated bundle index. Do not edit.
558
344
  */
559
345
 
560
- export { STRIVACITY_SDK, StrivacityAuthModule, StrivacityAuthService, StrivacityWidgetService, StyLoginRenderer, provideStrivacity };
346
+ export { STRIVACITY_SDK, StrivacityAuthModule, StrivacityAuthService, StrivacityNativeLoginService, provideStrivacity };
561
347
  //# sourceMappingURL=strivacity-sdk-angular.mjs.map