@schibsted/account-sdk-browser 6.0.0-alpha.5 → 6.0.0-alpha.7

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.
package/src/identity.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  * See LICENSE.md in the project root.
3
3
  */
4
4
 
5
- import { TinyEmitter } from 'tiny-emitter';
6
5
  import Cache from './cache.js';
7
6
  import { ENDPOINTS } from './config.js';
8
7
  import { cloneDeep } from './object.js';
@@ -19,6 +18,7 @@ import {
19
18
  normalizeSessionResponse,
20
19
  } from './session-response.js';
21
20
  import * as spidTalk from './spid-talk.js';
21
+ import type { AccountEventName } from './types/account.js';
22
22
  import type { LogFunction } from './types/common.js';
23
23
  import type {
24
24
  IdentityBeforeRedirectCallback,
@@ -63,33 +63,44 @@ const globalWindow = () => window;
63
63
  /**
64
64
  * Provides Identity functionalty to a web page
65
65
  */
66
- export class Identity extends TinyEmitter {
67
- window: Window;
68
- clientId: string;
69
- sessionStorageCache: Cache;
70
- localStorageCache: Cache;
71
- redirectUri: string;
72
- env: string;
73
- log?: LogFunction;
74
- callbackBeforeRedirect: IdentityBeforeRedirectCallback;
75
- _sessionDomain: string;
76
- _enableSessionCaching: boolean;
66
+ export class Identity {
67
+ // Event bus
68
+ private readonly eventTarget = new EventTarget();
69
+
70
+ // Configuration
71
+ private readonly window: Window;
72
+ private readonly clientId: string;
73
+ private readonly redirectUri: string;
74
+ private readonly env: string;
75
+ private readonly log?: LogFunction;
76
+ private readonly callbackBeforeRedirect: IdentityBeforeRedirectCallback;
77
+
78
+ // Caches
79
+ private readonly sessionStorageCache: Cache;
80
+ private readonly localStorageCache: Cache;
81
+ private enableSessionCaching: boolean;
82
+
83
+ // REST clients
84
+ private readonly spid: RESTClient;
85
+ private readonly oauthService: RESTClient;
86
+ private readonly bffService: RESTClient;
87
+ private readonly sessionService: RESTClient;
88
+ private readonly globalSessionService: RESTClient;
89
+ private readonly usedSessionServiceGetSessionEndpoint: 'v2/session' | 'session';
90
+
91
+ // Session state
77
92
  /**
78
93
  * @internal Starts as `{}` before the first `hasSession()` call, then stores the latest
79
94
  * object response from hasSession().
80
95
  */
81
- _session: SessionObjectResponse;
82
- _spid: RESTClient;
83
- _oauthService: RESTClient;
84
- _bffService: RESTClient;
85
- _sessionService: RESTClient;
86
- _globalSessionService: RESTClient;
87
- _usedSessionServiceGetSessionEndpoint: 'v2/session' | 'session';
88
- _hasSessionInProgress?: false | Promise<SessionResponse>;
89
- popup?: Window | null;
90
- setVarnishCookie?: boolean;
91
- varnishExpiresIn?: number;
92
- varnishCookieDomain?: string;
96
+ private session: SessionObjectResponse;
97
+ private hasSessionInProgress?: false | Promise<SessionResponse>;
98
+
99
+ // Popup & Varnish cookie state
100
+ private popup?: Window | null;
101
+ private setVarnishCookie?: boolean;
102
+ private varnishExpiresIn?: number;
103
+ private varnishCookieDomain?: string;
93
104
 
94
105
  /**
95
106
  * @param options
@@ -112,7 +123,6 @@ export class Identity extends TinyEmitter {
112
123
  window = globalWindow(),
113
124
  callbackBeforeRedirect = () => {},
114
125
  }: IdentityOptions) {
115
- super();
116
126
  assert(isNonEmptyString(clientId), 'clientId parameter is required');
117
127
  assert(isObject(window), 'The reference to window is missing');
118
128
  assert(!redirectUri || isUrl(redirectUri), 'redirectUri parameter is invalid');
@@ -125,67 +135,72 @@ export class Identity extends TinyEmitter {
125
135
  spidTalk.emulate(window);
126
136
  this.window = window;
127
137
  this.clientId = clientId;
128
- this.sessionStorageCache = new Cache(() => this.window && this.window.sessionStorage);
129
- this.localStorageCache = new Cache(() => this.window && this.window.localStorage);
130
138
  this.redirectUri = redirectUri;
131
139
  this.env = env;
132
140
  this.log = log;
133
141
  this.callbackBeforeRedirect = callbackBeforeRedirect;
134
- this._sessionDomain = sessionDomain;
135
142
 
143
+ this.sessionStorageCache = new Cache(() => this.window && this.window.sessionStorage);
144
+ this.localStorageCache = new Cache(() => this.window && this.window.localStorage);
136
145
  // Internal hack: set to false to always refresh from hassession
137
- this._enableSessionCaching = true;
146
+ this.enableSessionCaching = true;
138
147
 
139
- // Old session
140
- this._session = {};
148
+ this.session = {};
141
149
 
142
150
  const accountClientParams = { client_id: this.clientId, redirect_uri: this.redirectUri };
143
151
  const clientSdrn = buildClientSdrn(this.env, this.clientId);
144
152
 
145
- this._sessionService = createAccountRestClient({
146
- serverUrl: sessionDomain,
153
+ this.spid = createAccountRestClient({
154
+ serverUrl: urlMapper(env, ENDPOINTS.SPiD),
147
155
  log: this.log,
148
- defaultParams: {
149
- client_sdrn: clientSdrn,
150
- redirect_uri: this.redirectUri,
151
- sdk_version: version,
152
- },
156
+ defaultParams: accountClientParams,
153
157
  });
154
- this._usedSessionServiceGetSessionEndpoint =
155
- this._sessionService.url.pathname && this._sessionService.url.pathname.length <= 1
156
- ? 'v2/session'
157
- : 'session';
158
-
159
- this._spid = createAccountRestClient({
158
+ this.oauthService = createAccountRestClient({
160
159
  serverUrl: urlMapper(env, ENDPOINTS.SPiD),
161
160
  log: this.log,
162
161
  defaultParams: accountClientParams,
163
162
  });
164
- this._bffService = createAccountRestClient({
163
+ this.bffService = createAccountRestClient({
165
164
  serverUrl: urlMapper(env, ENDPOINTS.BFF),
166
165
  log: this.log,
167
166
  defaultParams: accountClientParams,
168
167
  });
169
- this._oauthService = createAccountRestClient({
170
- serverUrl: urlMapper(env, ENDPOINTS.SPiD),
168
+ this.sessionService = createAccountRestClient({
169
+ serverUrl: sessionDomain,
171
170
  log: this.log,
172
- defaultParams: accountClientParams,
171
+ defaultParams: {
172
+ client_sdrn: clientSdrn,
173
+ redirect_uri: this.redirectUri,
174
+ sdk_version: version,
175
+ },
173
176
  });
174
- this._globalSessionService = createAccountRestClient({
177
+ this.globalSessionService = createAccountRestClient({
175
178
  serverUrl: urlMapper(env, ENDPOINTS.SESSION_SERVICE),
176
179
  log: this.log,
177
180
  defaultParams: { client_sdrn: clientSdrn, sdk_version: version },
178
181
  });
182
+ this.usedSessionServiceGetSessionEndpoint =
183
+ this.sessionService.url.pathname && this.sessionService.url.pathname.length <= 1
184
+ ? 'v2/session'
185
+ : 'session';
179
186
 
180
187
  this._unblockSessionCall();
181
188
  }
182
189
 
190
+ addEventListener(type: AccountEventName, listener: EventListener): void {
191
+ this.eventTarget.addEventListener(type, listener);
192
+ }
193
+
194
+ removeEventListener(type: AccountEventName, listener: EventListener): void {
195
+ this.eventTarget.removeEventListener(type, listener);
196
+ }
197
+
183
198
  /**
184
199
  * Read tabId from session storage
185
200
  * @private
186
201
  */
187
202
  _getTabId(): number | undefined {
188
- if (this._enableSessionCaching) {
203
+ if (this.enableSessionCaching) {
189
204
  const tabId = this.sessionStorageCache.get(TAB_ID_KEY);
190
205
  if (typeof tabId !== 'number') {
191
206
  this.sessionStorageCache.set(TAB_ID_KEY, TAB_ID, TAB_ID_TTL);
@@ -229,33 +244,6 @@ export class Identity extends TinyEmitter {
229
244
  this.localStorageCache.delete(SESSION_CALL_BLOCKED_CACHE_KEY);
230
245
  }
231
246
 
232
- /**
233
- * Emits the relevant events based on the previous and new reply from hasSession
234
- * @private
235
- * @param previous
236
- * @param current
237
- */
238
- _emitSessionEvent(previous: object, current: object): void {
239
- const previousUserId = isSessionSuccessResponse(previous) ? previous.userId : undefined;
240
- const currentUserId = isSessionSuccessResponse(current) ? current.userId : undefined;
241
-
242
- /**
243
- * Emitted when the user is logged in (This happens as a result of calling
244
- * {@link Identity#hasSession}, so it is also emitted if the user was previously logged in)
245
- * @event Identity#login
246
- */
247
- if (currentUserId) {
248
- this.emit('login', current);
249
- }
250
- /**
251
- * Emitted when the user logged out
252
- * @event Identity#logout
253
- */
254
- if (previousUserId && !currentUserId) {
255
- this.emit('logout', current);
256
- }
257
- }
258
-
259
247
  /**
260
248
  * Close this.popup if it exists and is open
261
249
  * @private
@@ -344,10 +332,10 @@ export class Identity extends TinyEmitter {
344
332
  */
345
333
  _clearVarnishCookie(): void {
346
334
  const baseDomain =
347
- this._session &&
348
- isSessionSuccessResponse(this._session) &&
349
- typeof this._session.baseDomain === 'string'
350
- ? this._session.baseDomain
335
+ this.session &&
336
+ isSessionSuccessResponse(this.session) &&
337
+ typeof this.session.baseDomain === 'string'
338
+ ? this.session.baseDomain
351
339
  : this.window.location.hostname;
352
340
 
353
341
  const domain = this.varnishCookieDomain || baseDomain || '';
@@ -361,19 +349,17 @@ export class Identity extends TinyEmitter {
361
349
  * determines the status of the user.
362
350
  * @throws {SDKError} - If the call to the hasSession service fails in any way (this will happen
363
351
  * if, say, the user is not logged in)
364
- * @fires Identity#login
365
- * @fires Identity#logout
366
352
  */
367
353
  hasSession(): Promise<SessionResponse> {
368
354
  const isSessionCallBlocked = this._isSessionCallBlocked();
369
355
 
370
356
  if (isSessionCallBlocked) {
371
- const sessionData = normalizeSessionResponse(this._session);
357
+ const sessionData = normalizeSessionResponse(this.session);
372
358
  return Promise.resolve(isRedirectSessionPayload(sessionData) ? {} : sessionData);
373
359
  }
374
360
 
375
- if (this._hasSessionInProgress) {
376
- return this._hasSessionInProgress;
361
+ if (this.hasSessionInProgress) {
362
+ return this.hasSessionInProgress;
377
363
  }
378
364
 
379
365
  const _postProcess = (sessionData: SessionObjectResponse): SessionObjectResponse => {
@@ -384,8 +370,7 @@ export class Identity extends TinyEmitter {
384
370
  throw new SDKError('HasSession failed', errorObject);
385
371
  }
386
372
  this._maybeSetVarnishCookie(sessionData);
387
- this._emitSessionEvent(this._session, sessionData);
388
- this._session = sessionData;
373
+ this.session = sessionData;
389
374
  return sessionData;
390
375
  };
391
376
 
@@ -395,7 +380,7 @@ export class Identity extends TinyEmitter {
395
380
  };
396
381
 
397
382
  const _getSession = async (): Promise<SessionResponse> => {
398
- if (this._enableSessionCaching) {
383
+ if (this.enableSessionCaching) {
399
384
  // Try to resolve from cache (it has a TTL)
400
385
  const cachedSession = _getCachedSession();
401
386
  if (cachedSession) {
@@ -407,14 +392,14 @@ export class Identity extends TinyEmitter {
407
392
  }
408
393
  let sessionData: NormalizedSessionPayload | null = null;
409
394
  try {
410
- sessionData = await this._sessionService.get(
411
- this._usedSessionServiceGetSessionEndpoint,
395
+ sessionData = await this.sessionService.get(
396
+ this.usedSessionServiceGetSessionEndpoint,
412
397
  { tabId: this._getTabId() },
413
398
  normalizeSessionResponse,
414
399
  );
415
400
  } catch (err: unknown) {
416
401
  const cacheableError = isObject(err) ? err : undefined;
417
- if (cacheableError?.code === 400 && this._enableSessionCaching) {
402
+ if (cacheableError?.code === 400 && this.enableSessionCaching) {
418
403
  const errorExpiresIn =
419
404
  typeof cacheableError.expiresIn === 'number'
420
405
  ? cacheableError.expiresIn
@@ -426,7 +411,7 @@ export class Identity extends TinyEmitter {
426
411
  typeof cacheableError.code === 'number' &&
427
412
  cacheableError.code >= 500 &&
428
413
  cacheableError.code < 600 &&
429
- this._enableSessionCaching
414
+ this.enableSessionCaching
430
415
  ) {
431
416
  // Temporary fix: 30 seconds cache to limit number of calls when service is unavailable
432
417
  this.sessionStorageCache.set(HAS_SESSION_CACHE_KEY, { error: err }, 30 * 1000);
@@ -441,13 +426,13 @@ export class Identity extends TinyEmitter {
441
426
 
442
427
  await this.callbackBeforeRedirect();
443
428
 
444
- const redirectUrl = this._sessionService.makeUrl(sessionData.redirectURL, {
429
+ const redirectUrl = this.sessionService.makeUrl(sessionData.redirectURL, {
445
430
  tabId: this._getTabId(),
446
431
  });
447
432
  return redirectUrl;
448
433
  }
449
434
 
450
- if (this._enableSessionCaching) {
435
+ if (this.enableSessionCaching) {
451
436
  const expiresIn =
452
437
  1000 *
453
438
  (isSessionSuccessResponse(sessionData)
@@ -459,21 +444,21 @@ export class Identity extends TinyEmitter {
459
444
 
460
445
  return _postProcess(sessionData ?? {});
461
446
  };
462
- this._hasSessionInProgress = _getSession().then(
447
+ this.hasSessionInProgress = _getSession().then(
463
448
  (sessionData) => {
464
- this._hasSessionInProgress = false;
449
+ this.hasSessionInProgress = false;
465
450
  if (typeof sessionData === 'string') {
466
451
  this.window.location.href = sessionData;
467
452
  }
468
453
  return sessionData;
469
454
  },
470
455
  (err) => {
471
- this._hasSessionInProgress = false;
456
+ this.hasSessionInProgress = false;
472
457
  throw new SDKError('HasSession failed', err);
473
458
  },
474
459
  );
475
460
 
476
- return this._hasSessionInProgress;
461
+ return this.hasSessionInProgress;
477
462
  }
478
463
 
479
464
  /**
@@ -490,6 +475,16 @@ export class Identity extends TinyEmitter {
490
475
  }
491
476
  }
492
477
 
478
+ /**
479
+ * Enables or disables caching for subsequent session lookups.
480
+ *
481
+ * Changing this setting does not remove an existing cached session. Use
482
+ * {@link Identity#clearCachedUserSession} to clear the cache explicitly.
483
+ */
484
+ setSessionCachingEnabled(enabled: boolean): void {
485
+ this.enableSessionCaching = enabled;
486
+ }
487
+
493
488
  /**
494
489
  * Removes the cached user session.
495
490
  */
@@ -592,7 +587,7 @@ export class Identity extends TinyEmitter {
592
587
  */
593
588
  async getUserContextData(): Promise<SimplifiedLoginData | null> {
594
589
  try {
595
- return await this._globalSessionService.get('user-context', undefined, (value) =>
590
+ return await this.globalSessionService.get('user-context', undefined, (value) =>
596
591
  isObject(value) && isStr(value.identifier)
597
592
  ? {
598
593
  identifier: value.identifier,
@@ -630,6 +625,7 @@ export class Identity extends TinyEmitter {
630
625
  * @param options.xDomainId
631
626
  * @param options.xEnvironmentId
632
627
  * @param options.originCampaign
628
+ * @fires Identity#login
633
629
  * @return - Reference to popup window if created (or `null` otherwise)
634
630
  */
635
631
  login({
@@ -673,11 +669,25 @@ export class Identity extends TinyEmitter {
673
669
  width: 360,
674
670
  height: 570,
675
671
  });
672
+
676
673
  if (this.popup) {
674
+ this.eventTarget.dispatchEvent(
675
+ new CustomEvent('login', {
676
+ detail: { url, method: 'popup' },
677
+ }),
678
+ );
679
+
677
680
  return this.popup;
678
681
  }
679
682
  }
683
+
684
+ this.eventTarget.dispatchEvent(
685
+ new CustomEvent('login', {
686
+ detail: { url, method: 'default' },
687
+ }),
688
+ );
680
689
  this.window.location.href = url;
690
+
681
691
  return null;
682
692
  }
683
693
 
@@ -702,14 +712,19 @@ export class Identity extends TinyEmitter {
702
712
 
703
713
  /**
704
714
  * @summary Logs the user out from the Identity platform
705
- * @param redirectUri - Where to redirect the browser after logging out of Schibsted
706
- * account
715
+ * @param redirectUri - Where to redirect the browser after logging out of Schibsted account
716
+ * @fires Identity#logout
707
717
  */
708
718
  logout(redirectUri: string = this.redirectUri): void {
709
719
  this.sessionStorageCache.delete(HAS_SESSION_CACHE_KEY);
710
720
  this._maybeClearVarnishCookie();
711
- this.emit('logout');
712
- this.window.location.href = this.logoutUrl(redirectUri);
721
+ const url = this.logoutUrl(redirectUri);
722
+ this.eventTarget.dispatchEvent(
723
+ new CustomEvent('logout', {
724
+ detail: { url },
725
+ }),
726
+ );
727
+ this.window.location.href = url;
713
728
  }
714
729
 
715
730
  /**
@@ -768,7 +783,7 @@ export class Identity extends TinyEmitter {
768
783
  `the state parameter should be a non empty string but it is ${state}`,
769
784
  );
770
785
 
771
- return this._oauthService.makeUrl('oauth/authorize', {
786
+ return this.oauthService.makeUrl('oauth/authorize', {
772
787
  response_type: 'code',
773
788
  redirect_uri: redirectUri,
774
789
  scope,
@@ -795,7 +810,7 @@ export class Identity extends TinyEmitter {
795
810
  logoutUrl(redirectUri: string = this.redirectUri): string {
796
811
  assert(isUrl(redirectUri), `logoutUrl(): redirectUri is invalid`);
797
812
  const params = { redirect_uri: redirectUri };
798
- return this._sessionService.makeUrl('logout', params);
813
+ return this.sessionService.makeUrl('logout', params);
799
814
  }
800
815
 
801
816
  /**
@@ -803,7 +818,7 @@ export class Identity extends TinyEmitter {
803
818
  * @param redirectUri
804
819
  */
805
820
  accountUrl(redirectUri: string = this.redirectUri): string {
806
- return this._spid.makeUrl('profile-pages', {
821
+ return this.spid.makeUrl('profile-pages', {
807
822
  response_type: 'code',
808
823
  redirect_uri: redirectUri,
809
824
  });
@@ -833,7 +848,7 @@ export class Identity extends TinyEmitter {
833
848
  if (options && options.encoding) {
834
849
  queryParams.encoding = options.encoding;
835
850
  }
836
- const widgetUrl = this._bffService.makeUrl('simplified-login-widget', queryParams, false);
851
+ const widgetUrl = this.bffService.makeUrl('simplified-login-widget', queryParams, false);
837
852
 
838
853
  const prepareLoginParams = async (
839
854
  loginParams: SimplifiedLoginWidgetLoginOptions,
@@ -893,11 +908,11 @@ export class Identity extends TinyEmitter {
893
908
  * Emitted when the simplified login widget is displayed on the screen
894
909
  * @event Identity#simplifiedLoginOpened
895
910
  */
896
- this.emit('simplifiedLoginOpened');
911
+ this.eventTarget.dispatchEvent(new Event('simplifiedLoginOpened'));
897
912
  };
898
913
 
899
914
  const cancelLoginHandler = () => {
900
- this.emit('simplifiedLoginCancelled');
915
+ this.eventTarget.dispatchEvent(new Event('simplifiedLoginCancelled'));
901
916
  };
902
917
 
903
918
  if (hasOpenSimplifiedLoginWidget(window)) {
package/src/index.ts CHANGED
@@ -10,7 +10,7 @@ export { default as SDKError } from './sdk-error.js';
10
10
  /* public available types */
11
11
  export type {
12
12
  AccountConfig,
13
- AccountEventHandler,
13
+ AccountEventListener,
14
14
  AccountEventName,
15
15
  } from './types/account.js';
16
16
  export type { AccountEnvironment, LogFunction } from './types/common.js';
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import Cache from './cache.js';
6
- import { ENDPOINTS } from './config.js';
7
6
  import { parseHasAccessResult } from './has-access-response.js';
8
7
  import type RESTClient from './rest-client.js';
9
8
  import { buildClientSdrn, createAccountRestClient } from './rest-clients.js';
@@ -11,7 +10,6 @@ import SDKError from './sdk-error.js';
11
10
  import * as spidTalk from './spid-talk.js';
12
11
  import type { HasAccessResult, MonetizationOptions } from './types/monetization.js';
13
12
  import type { SessionUserId } from './types/session.js';
14
- import { urlMapper } from './url.js';
15
13
  import { assert, isNonEmptyString, isStr, isUrl } from './validate.js';
16
14
  import version from './version.js';
17
15
 
@@ -21,13 +19,9 @@ const globalWindow = () => window;
21
19
  * Provides features related to monetization
22
20
  */
23
21
  export class Monetization {
24
- cache: Cache;
25
- clientId: string;
26
- env: string;
27
- redirectUri?: string;
28
- _spid: RESTClient;
29
- _sessionService?: RESTClient;
30
- private pendingHasAccessRequests: Record<string, Promise<HasAccessResult>>;
22
+ private readonly cache: Cache;
23
+ private readonly sessionService?: RESTClient;
24
+ private readonly pendingHasAccessRequests: Record<string, Promise<HasAccessResult>>;
31
25
 
32
26
  /**
33
27
  * @param options - Monetization configuration
@@ -43,24 +37,18 @@ export class Monetization {
43
37
  spidTalk.emulate(window);
44
38
  assert(isNonEmptyString(clientId), 'clientId parameter is required');
45
39
  assert(isStr(env), `env parameter is invalid: ${env}`);
40
+ const clientSdrn = buildClientSdrn(env, clientId);
46
41
 
47
42
  this.cache = new Cache(() => window && window.sessionStorage);
48
- this.clientId = clientId;
49
- this.env = env;
50
- this.redirectUri = redirectUri;
51
43
  this.pendingHasAccessRequests = {};
52
- this._spid = createAccountRestClient({
53
- serverUrl: urlMapper(env, ENDPOINTS.SPiD),
54
- defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },
55
- });
56
44
 
57
45
  if (sessionDomain) {
58
46
  assert(isUrl(sessionDomain), 'sessionDomain parameter is not a valid URL');
59
- this._sessionService = createAccountRestClient({
47
+ this.sessionService = createAccountRestClient({
60
48
  serverUrl: sessionDomain,
61
49
  defaultParams: {
62
- client_sdrn: buildClientSdrn(this.env, this.clientId),
63
- redirect_uri: this.redirectUri,
50
+ client_sdrn: clientSdrn,
51
+ redirect_uri: redirectUri,
64
52
  sdk_version: version,
65
53
  },
66
54
  });
@@ -77,7 +65,7 @@ export class Monetization {
77
65
  * doesn't have access to any of the given products/features)
78
66
  */
79
67
  async hasAccess(productIds: string[], userId: SessionUserId): Promise<HasAccessResult | null> {
80
- if (!this._sessionService) {
68
+ if (!this.sessionService) {
81
69
  throw new SDKError(`hasAccess can only be called if 'sessionDomain' is configured`);
82
70
  }
83
71
  if (!userId) {
@@ -88,11 +76,11 @@ export class Monetization {
88
76
  }
89
77
 
90
78
  const sortedIds = [...productIds].sort();
91
- const cacheKey = this._accessCacheKey(sortedIds, userId);
79
+ const cacheKey = this.accessCacheKey(sortedIds, userId);
92
80
  let data = this.cache.get(cacheKey) as HasAccessResult | null;
93
81
  if (!data) {
94
82
  if (!this.pendingHasAccessRequests[cacheKey]) {
95
- this.pendingHasAccessRequests[cacheKey] = this._sessionService.get(
83
+ this.pendingHasAccessRequests[cacheKey] = this.sessionService.get(
96
84
  `/hasAccess/${sortedIds.join(',')}`,
97
85
  undefined,
98
86
  parseHasAccessResult,
@@ -123,7 +111,7 @@ export class Monetization {
123
111
  * @param userId - id of currently logged in user
124
112
  */
125
113
  clearCachedAccessResult(productIds: string[], userId: SessionUserId): void {
126
- this.cache.delete(this._accessCacheKey(productIds, userId));
114
+ this.cache.delete(this.accessCacheKey(productIds, userId));
127
115
  }
128
116
 
129
117
  /**
@@ -132,7 +120,7 @@ export class Monetization {
132
120
  * @param productIds - which products/features to check
133
121
  * @param userId - id of currently logged in user
134
122
  */
135
- private _accessCacheKey(productIds: string[], userId: SessionUserId): string {
123
+ private accessCacheKey(productIds: string[], userId: SessionUserId): string {
136
124
  return `prd_${[...productIds].sort()}_${userId}`;
137
125
  }
138
126
  }
package/src/sdk-error.ts CHANGED
@@ -14,6 +14,8 @@ const STRINGIFY_TYPES = ['boolean', 'number', 'string'];
14
14
  * Custom error class thrown by all SDK methods on failure.
15
15
  *
16
16
  * All rejected promises from API calls reject with an instance of this class.
17
+ *
18
+ * @group Errors
17
19
  */
18
20
  export default class SDKError extends Error {
19
21
  /** HTTP status code from the server error payload;
@@ -1,6 +1,11 @@
1
1
  import type { AccountBaseConfig, LogFunction } from './common';
2
2
  import type { IdentityBeforeRedirectCallback, VarnishCookieOptions } from './identity';
3
3
 
4
+ /**
5
+ * Configuration used to create an {@link Account} instance.
6
+ *
7
+ * @group Configuration
8
+ */
4
9
  export type AccountConfig = AccountBaseConfig & {
5
10
  /** Receives debug log information. If not set, no logging will be done. */
6
11
  log?: LogFunction;
@@ -10,19 +15,36 @@ export type AccountConfig = AccountBaseConfig & {
10
15
  varnish?: VarnishCookieOptions;
11
16
  };
12
17
 
18
+ type LoginEventDetail = {
19
+ url: string;
20
+ method: 'popup' | 'default';
21
+ };
22
+
23
+ type LogoutEventDetail = {
24
+ url: string;
25
+ };
26
+
27
+ export type AccountEventMap = {
28
+ /** Emitted with the login URL and method when `login()` initiates a login flow. */
29
+ login: CustomEvent<LoginEventDetail>;
30
+ /** Emitted with the logout URL when `logout()` is called. */
31
+ logout: CustomEvent<LogoutEventDetail>;
32
+ /** Emitted when the simplified login widget is displayed. */
33
+ simplifiedLoginOpened: Event;
34
+ /** Emitted when the simplified login widget is closed. */
35
+ simplifiedLoginCancelled: Event;
36
+ };
37
+
38
+ /** Account event names emitted by the SDK. */
39
+ export type AccountEventName = keyof AccountEventMap;
40
+
13
41
  /**
14
- * Identity events emitted by the SDK.
42
+ * Native event listener used by Account event subscriptions.
43
+ *
44
+ * Payload-bearing events are `CustomEvent` instances and expose their payload through `detail`.
15
45
  *
16
- * - `login`: emitted when `hasSession()` resolves with a logged-in user payload.
17
- * - `logout`: emitted when a previously logged-in user is no longer logged in, or when
18
- * `logout()` is called.
19
- * - `simplifiedLoginOpened`: emitted when the simplified login widget is displayed.
20
- * - `simplifiedLoginCancelled`: emitted when the simplified login widget is closed.
46
+ * @group Events
21
47
  */
22
- export type AccountEventName =
23
- | 'login'
24
- | 'logout'
25
- | 'simplifiedLoginOpened'
26
- | 'simplifiedLoginCancelled';
27
-
28
- export type AccountEventHandler = (...args: unknown[]) => void;
48
+ export type AccountEventListener<EventName extends AccountEventName = AccountEventName> = (
49
+ event: AccountEventMap[EventName],
50
+ ) => void;
@@ -3,15 +3,21 @@
3
3
  /**
4
4
  * Log callback used by the SDK for debug output.
5
5
  * Receives a pre-formatted log line.
6
- * @internal
6
+ *
7
+ * @group Configuration
7
8
  */
8
9
  export type LogFunction = (message: string) => void;
9
10
 
10
11
  /**
11
12
  * Supported Schibsted account environment keys.
13
+ *
14
+ * @group Configuration
12
15
  */
13
16
  export type AccountEnvironment = 'LOCAL' | 'DEV' | 'PRE' | 'PRO' | 'PRO_NO' | 'PRO_FI' | 'PRO_DK';
14
17
 
18
+ /**
19
+ * Shared Account configuration fields.
20
+ */
15
21
  export type AccountBaseConfig = {
16
22
  /** Mandatory client id. Example: "1234567890abcdef12345678" */
17
23
  clientId: string;