@schibsted/account-sdk-browser 6.0.0-alpha.1 → 6.0.0-alpha.3

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 (89) hide show
  1. package/README.md +39 -64
  2. package/dist/account-sdk.d.ts +4 -0
  3. package/dist/account.d.ts +29 -0
  4. package/dist/cache.d.ts +65 -0
  5. package/dist/config.d.ts +86 -0
  6. package/dist/get-account-sdk.d.ts +3 -0
  7. package/dist/global-registry.d.ts +23 -0
  8. package/dist/globals.d.ts +15 -0
  9. package/dist/has-access-response.d.ts +2 -0
  10. package/dist/identity.d.ts +323 -0
  11. package/dist/index.d.ts +8 -0
  12. package/dist/index.js +859 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/monetization.d.ts +58 -0
  15. package/{src → dist}/object.d.ts +4 -9
  16. package/dist/popup.d.ts +9 -0
  17. package/dist/resolve-browser-window.d.ts +1 -0
  18. package/dist/rest-client.d.ts +102 -0
  19. package/dist/rest-clients.d.ts +10 -0
  20. package/dist/sdk-error.d.ts +27 -0
  21. package/dist/session-response.d.ts +30 -0
  22. package/{src/spidTalk.d.ts → dist/spid-talk.d.ts} +4 -6
  23. package/dist/types/account.d.ts +13 -0
  24. package/dist/types/common.d.ts +22 -0
  25. package/dist/types/identity.d.ts +41 -0
  26. package/dist/types/login.d.ts +120 -0
  27. package/dist/types/monetization-guards.d.ts +2 -0
  28. package/dist/types/monetization.d.ts +21 -0
  29. package/dist/types/session-guards.d.ts +36 -0
  30. package/dist/types/session.d.ts +124 -0
  31. package/dist/url.d.ts +8 -0
  32. package/dist/validate.d.ts +50 -0
  33. package/dist/version.d.ts +2 -0
  34. package/package.json +30 -22
  35. package/src/account-sdk.ts +74 -0
  36. package/src/account.ts +149 -0
  37. package/src/{cache.js → cache.ts} +53 -38
  38. package/src/{config.js → config.ts} +7 -32
  39. package/src/get-account-sdk.ts +94 -0
  40. package/src/global-registry.ts +39 -0
  41. package/src/globals.ts +12 -0
  42. package/src/has-access-response.ts +35 -0
  43. package/src/identity.ts +1102 -0
  44. package/src/index.ts +32 -0
  45. package/src/{monetization.js → monetization.ts} +65 -73
  46. package/src/{object.js → object.ts} +9 -16
  47. package/src/popup.ts +74 -0
  48. package/src/resolve-browser-window.ts +11 -0
  49. package/src/rest-client.ts +253 -0
  50. package/src/rest-clients.ts +30 -0
  51. package/src/sdk-error.ts +59 -0
  52. package/src/session-response.ts +85 -0
  53. package/src/{spidTalk.js → spid-talk.ts} +10 -12
  54. package/src/types/account.ts +27 -0
  55. package/src/types/common.ts +26 -0
  56. package/src/types/identity.ts +55 -0
  57. package/src/types/login.ts +145 -0
  58. package/src/types/monetization-guards.ts +35 -0
  59. package/src/types/monetization.ts +24 -0
  60. package/src/types/session-guards.ts +89 -0
  61. package/src/types/session.ts +147 -0
  62. package/src/{url.js → url.ts} +6 -10
  63. package/src/{validate.js → validate.ts} +27 -43
  64. package/src/{version.js → version.ts} +1 -2
  65. package/identity.d.ts +0 -1
  66. package/identity.js +0 -5
  67. package/index.d.ts +0 -4
  68. package/index.js +0 -9
  69. package/monetization.d.ts +0 -1
  70. package/monetization.js +0 -5
  71. package/payment.d.ts +0 -1
  72. package/payment.js +0 -5
  73. package/src/RESTClient.d.ts +0 -89
  74. package/src/RESTClient.js +0 -193
  75. package/src/SDKError.d.ts +0 -16
  76. package/src/SDKError.js +0 -55
  77. package/src/cache.d.ts +0 -64
  78. package/src/config.d.ts +0 -34
  79. package/src/global-registry.js +0 -20
  80. package/src/identity.d.ts +0 -679
  81. package/src/identity.js +0 -1154
  82. package/src/monetization.d.ts +0 -80
  83. package/src/payment.d.ts +0 -115
  84. package/src/payment.js +0 -211
  85. package/src/popup.d.ts +0 -10
  86. package/src/popup.js +0 -59
  87. package/src/url.d.ts +0 -10
  88. package/src/validate.d.ts +0 -64
  89. package/src/version.d.ts +0 -2
@@ -0,0 +1,30 @@
1
+ /* Copyright 2026 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.
2
+ * See LICENSE.md in the project root.
3
+ */
4
+
5
+ import { NAMESPACE } from './config.js';
6
+ import RESTClient from './rest-client.js';
7
+ import type { LogFunction } from './types/common.js';
8
+ import { assert, isNonEmptyString } from './validate.js';
9
+
10
+ type AccountRestClientOptions = {
11
+ serverUrl: string;
12
+ log?: LogFunction;
13
+ defaultParams: Record<string, string | number | boolean | undefined>;
14
+ };
15
+
16
+ const isKnownNamespace = (env: string): env is keyof typeof NAMESPACE =>
17
+ Object.hasOwn(NAMESPACE, env);
18
+
19
+ export const buildClientSdrn = (env: string, clientId: string): string => {
20
+ assert(isNonEmptyString(clientId), 'clientId parameter is required');
21
+ assert(isKnownNamespace(env), `env parameter is invalid: ${env}`);
22
+
23
+ return `sdrn:${NAMESPACE[env]}:client:${clientId}`;
24
+ };
25
+
26
+ export const createAccountRestClient = ({
27
+ serverUrl,
28
+ log,
29
+ defaultParams,
30
+ }: AccountRestClientOptions): RESTClient => new RESTClient({ serverUrl, log, defaultParams });
@@ -0,0 +1,59 @@
1
+ /* Copyright 2024 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.
2
+ * See LICENSE.md in the project root.
3
+ */
4
+
5
+ /*
6
+ * Note: this module can't have any internal dependencies because it's used in ./validate which
7
+ * in turn is used as a dependency to a lot of other modules. Doing so may create a circular
8
+ * dependency that's hard to debug in Node.
9
+ */
10
+
11
+ const STRINGIFY_TYPES = ['boolean', 'number', 'string'];
12
+
13
+ /**
14
+ * Custom error class thrown by all SDK methods on failure.
15
+ *
16
+ * All rejected promises from API calls reject with an instance of this class.
17
+ */
18
+ export default class SDKError extends Error {
19
+ /** HTTP status code from the server error payload;
20
+ * declared explicitly to allow numeric comparisons without type-casting.
21
+ */
22
+ code?: number;
23
+ /**
24
+ * Properties copied from the server error object.
25
+ * @internal
26
+ */
27
+ [key: string]: unknown;
28
+
29
+ /**
30
+ * @param message - Human-readable error message
31
+ * @param errorObject - Optional server error payload. All enumerable properties are
32
+ * shallow-copied onto this instance.
33
+ */
34
+ constructor(message: string, errorObject?: Record<string, unknown>) {
35
+ super(message);
36
+ this.name = 'SDKError';
37
+
38
+ if (errorObject) {
39
+ try {
40
+ Object.assign(this, errorObject);
41
+ } catch {
42
+ // silent
43
+ }
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Returns a multi-line string with the error name, message, and any other properties copied
49
+ * from the server error payload.
50
+ */
51
+ toString(): string {
52
+ const ret = `${this.name}: ${this.message}`;
53
+ const additionalInfo = Object.keys(this)
54
+ .filter((key) => key !== 'name' && STRINGIFY_TYPES.includes(typeof this[key]))
55
+ .map((key) => ` ${key}: ${this[key]}`)
56
+ .join('\n');
57
+ return additionalInfo ? `${ret}\n${additionalInfo}` : ret;
58
+ }
59
+ }
@@ -0,0 +1,85 @@
1
+ /* Copyright 2026 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.
2
+ * See LICENSE.md in the project root.
3
+ */
4
+
5
+ import type {
6
+ ConnectedSessionResponse,
7
+ ConnectedSessionWithUserIdResponse,
8
+ NormalizedSessionPayload,
9
+ RedirectSessionPayload,
10
+ SessionFailureResponse,
11
+ SessionResponse,
12
+ SessionSuccessResponse,
13
+ } from './types/session.js';
14
+ import { isObject, isStr } from './validate.js';
15
+
16
+ export function isSessionSuccessResponse(value: unknown): value is SessionSuccessResponse {
17
+ return (
18
+ isObject(value) &&
19
+ typeof value.code === 'number' &&
20
+ isStr(value.type) &&
21
+ isStr(value.description) &&
22
+ typeof value.result === 'boolean'
23
+ );
24
+ }
25
+
26
+ export function isConnectedSessionResponse(value: unknown): value is ConnectedSessionResponse {
27
+ return isSessionSuccessResponse(value) && value.result === true;
28
+ }
29
+
30
+ export function isSessionFailureResponse(value: unknown): value is SessionFailureResponse {
31
+ return isObject(value) && Boolean(value.error);
32
+ }
33
+
34
+ export function isRedirectSessionPayload(value: unknown): value is RedirectSessionPayload {
35
+ return isObject(value) && Object.keys(value).length === 1 && isStr(value.redirectURL);
36
+ }
37
+
38
+ export function isConnectedSessionWithUserIdResponse(
39
+ value: SessionResponse,
40
+ ): value is ConnectedSessionWithUserIdResponse {
41
+ return (
42
+ isConnectedSessionResponse(value) &&
43
+ (typeof value.userId === 'number' || typeof value.userId === 'string')
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Converts an unknown Session Service `hasSession` payload into one of the shapes the SDK knows how
49
+ * to handle.
50
+ *
51
+ * This function is intentionally tolerant because `hasSession()` historically resolved unknown
52
+ * object payloads instead of rejecting them. The SDK relies on that behavior for backwards
53
+ * compatibility with clients that check the raw response themselves.
54
+ *
55
+ * The normal cases are:
56
+ * - regular success payloads with a boolean `result`;
57
+ * - failure payloads wrapped as `{ error, response? }`;
58
+ * - Safari/session-refresh redirect payloads shaped as `{ redirectURL }`.
59
+ *
60
+ * Any other object is preserved as an unrecognized response so callers can still receive it.
61
+ * Non-object values are normalized to `{}`, matching the old "empty response" fallback used by
62
+ * `hasSession()` and cached-session reads.
63
+ *
64
+ * This is used both for network responses parsed by `RESTClient` and for values restored from the
65
+ * session cache, so the rest of the Identity flow can switch on typed guards instead of repeatedly
66
+ * re-validating unknown data.
67
+ *
68
+ * @internal
69
+ */
70
+ export function normalizeSessionResponse(value: unknown): NormalizedSessionPayload {
71
+ if (isRedirectSessionPayload(value)) {
72
+ return value;
73
+ }
74
+ if (isSessionFailureResponse(value)) {
75
+ return value;
76
+ }
77
+ if (isSessionSuccessResponse(value)) {
78
+ return value;
79
+ }
80
+ if (isObject(value)) {
81
+ return value;
82
+ }
83
+
84
+ return {};
85
+ }
@@ -2,13 +2,12 @@
2
2
  * See LICENSE.md in the project root.
3
3
  */
4
4
 
5
- 'use strict';
6
-
7
5
  /**
8
6
  * A workaround for how Schibsted account handles JSONP calls in the browser
9
7
  * @private
10
8
  */
11
9
 
10
+ import './globals.js';
12
11
  import { isFunction } from './validate.js';
13
12
 
14
13
  /**
@@ -20,20 +19,19 @@ import { isFunction } from './validate.js';
20
19
  * The old SPiD JSONP APIs relied on SPiD.Talk.response(callbackName, data) but fetch-jsonp merely
21
20
  * calls window[callbackName](data) which is more common practice. This module simply acts as an
22
21
  * adapter to modernize the old mechanism until we support CORS. To see how the SPiD solution works,
23
- * try to run a simple JSONP request or debug it in netowrk tab. This module will be removed when
24
- * CORS is supported.
25
- * @memberof core
26
- * @param {object} global - a reference to the global object
27
- * @return {void}
22
+ * try to run a simple JSONP request or debug it in the network tab. This module will be removed
23
+ * when CORS is supported.
24
+ * @param global - A reference to the global object, typically the `Window`.
28
25
  */
29
- export function emulate(global) {
30
- if (global.SPiD === null || typeof global.SPiD !== 'object') {
26
+ export function emulate(global: Window): void {
27
+ if (!global.SPiD || typeof global.SPiD !== 'object') {
31
28
  global.SPiD = {};
32
29
  }
33
- if (global.SPiD.Talk === null || typeof global.SPiD.Talk !== 'object') {
34
- global.SPiD.Talk = {}
30
+ if (!global.SPiD.Talk || typeof global.SPiD.Talk !== 'object') {
31
+ global.SPiD.Talk = {};
35
32
  }
36
33
  if (!isFunction(global.SPiD.Talk.response)) {
37
- global.SPiD.Talk.response = (callbackName, data) => global[callbackName](data);
34
+ global.SPiD.Talk.response = (callbackName: string, data: unknown) =>
35
+ (global as any)[callbackName](data);
38
36
  }
39
37
  }
@@ -0,0 +1,27 @@
1
+ import type { AccountBaseConfig, LogFunction } from './common';
2
+ import type { IdentityBeforeRedirectCallback, VarnishCookieOptions } from './identity';
3
+
4
+ export type AccountConfig = AccountBaseConfig & {
5
+ /** Receives debug log information. If not set, no logging will be done. */
6
+ log?: LogFunction;
7
+ /** Callback triggered before a session refresh redirect happens. */
8
+ callbackBeforeRedirect?: IdentityBeforeRedirectCallback;
9
+ /** Optional Varnish cookie setup applied during Account initialization. */
10
+ varnish?: VarnishCookieOptions;
11
+ };
12
+
13
+ export type AccountSessionEventName =
14
+ | 'login'
15
+ | 'logout'
16
+ | 'userChange'
17
+ | 'sessionChange'
18
+ | 'notLoggedin'
19
+ | 'sessionInit'
20
+ | 'statusChange'
21
+ | 'error'
22
+ | 'simplifiedLoginOpened'
23
+ | 'simplifiedLoginCancelled';
24
+
25
+ export type AccountEventName = AccountSessionEventName | 'hasAccess';
26
+
27
+ export type AccountEventHandler = (...args: unknown[]) => void;
@@ -0,0 +1,26 @@
1
+ /* Common types used across the SDK */
2
+
3
+ /**
4
+ * Log callback used by the SDK for debug output.
5
+ * Receives a pre-formatted log line.
6
+ * @internal
7
+ */
8
+ export type LogFunction = (message: string) => void;
9
+
10
+ /**
11
+ * Supported Schibsted account environment keys.
12
+ */
13
+ export type AccountEnvironment = 'LOCAL' | 'DEV' | 'PRE' | 'PRO' | 'PRO_NO' | 'PRO_FI' | 'PRO_DK';
14
+
15
+ export type AccountBaseConfig = {
16
+ /** Mandatory client id. Example: "1234567890abcdef12345678" */
17
+ clientId: string;
18
+ /** Redirect uri. Example: "https://site.com" */
19
+ redirectUri: string;
20
+ /** Session Service domain. Example: "https://id.site.com" */
21
+ sessionDomain: string;
22
+ /** Schibsted account environment. Defaults to PRE. */
23
+ env?: AccountEnvironment;
24
+ /** Window object to use. Defaults to the global `window`. */
25
+ window?: Window;
26
+ };
@@ -0,0 +1,55 @@
1
+ import type { AccountBaseConfig, LogFunction } from './common';
2
+
3
+ /**
4
+ * Callback invoked before the SDK performs a full-page session refresh redirect.
5
+ */
6
+ export type IdentityBeforeRedirectCallback = () => void | Promise<void>;
7
+
8
+ /**
9
+ * Options accepted by the {@link Identity} constructor.
10
+ */
11
+ export type IdentityOptions = AccountBaseConfig & {
12
+ /** Receives debug log information. If not set, no logging will be done. */
13
+ log?: LogFunction;
14
+ /** Callback triggered before a session refresh redirect happens. */
15
+ callbackBeforeRedirect?: IdentityBeforeRedirectCallback;
16
+ };
17
+
18
+ /**
19
+ * Identity events emitted by the SDK.
20
+ *
21
+ * - `login`: emitted when `hasSession()` resolves with a logged-in user payload.
22
+ * - `logout`: emitted when a previously logged-in user is no longer logged in, or when
23
+ * `logout()` is called.
24
+ * - `userChange`: emitted when the current session user id changes.
25
+ * - `sessionChange`: emitted when either the previous or current `hasSession()` payload contains
26
+ * a logged-in user.
27
+ * - `notLoggedin`: emitted when neither the previous nor current `hasSession()` payload contains
28
+ * a logged-in user.
29
+ * - `sessionInit`: emitted once for the first logged-in session payload.
30
+ * - `statusChange`: emitted when the session `userStatus` changes.
31
+ * - `error`: emitted when `hasSession()` fails.
32
+ * - `simplifiedLoginOpened`: emitted when the simplified login widget is displayed.
33
+ * - `simplifiedLoginCancelled`: emitted when the simplified login widget is closed.
34
+ */
35
+ export type IdentityEventName =
36
+ | 'login'
37
+ | 'logout'
38
+ | 'userChange'
39
+ | 'sessionChange'
40
+ | 'notLoggedin'
41
+ | 'sessionInit'
42
+ | 'statusChange'
43
+ | 'error'
44
+ | 'simplifiedLoginOpened'
45
+ | 'simplifiedLoginCancelled';
46
+
47
+ /**
48
+ * Options for {@link Identity#enableVarnishCookie}.
49
+ */
50
+ export type VarnishCookieOptions = {
51
+ /** Override number of seconds before the Varnish cookie expires. */
52
+ expiresIn?: number;
53
+ /** Override cookie domain. E.g. "vg.no" instead of "www.vg.no". */
54
+ domain?: string;
55
+ };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Options accepted by {@link Identity#login} and related login URL helpers.
3
+ */
4
+ export type LoginOptions = {
5
+ /**
6
+ * An opaque value used by the client to maintain state between the request and callback.
7
+ * It's also recommended to prevent CSRF {@link https://tools.ietf.org/html/rfc6749#section-10.12}
8
+ */
9
+ state: string;
10
+ /**
11
+ * Authentication Context Class Reference Values. If omitted, the user will be asked to
12
+ * authenticate using username+password. For 2FA (Two-Factor Authentication) possible values
13
+ * are `sms`, `otp` (one time password), `password` (will force password confirmation, even if
14
+ * user is already logged in), `eid`. Those values might be mixed as space-separated string.
15
+ * To make sure that user has authenticated with 2FA you need to verify AMR (Authentication
16
+ * Methods References) claim in ID token. Might also be used to ensure additional acr
17
+ * (sms, otp, eid) for already logged in users. Supported value is also 'otp-email' means one
18
+ * time password using email.
19
+ */
20
+ acrValues?:
21
+ | 'password'
22
+ | 'otp'
23
+ | 'sms'
24
+ | 'eid-dk'
25
+ | 'eid-no'
26
+ | 'eid-se'
27
+ | 'eid-fi'
28
+ | 'eid'
29
+ | 'otp-email'
30
+ | (string & {});
31
+ /**
32
+ * The OAuth scopes for the tokens. This is a list of scopes, separated by space. If the list
33
+ * of scopes contains `openid`, the generated tokens includes the id token which can be useful
34
+ * for getting information about the user. Omitting scope is allowed, while `invalid_scope` is
35
+ * returned when the client asks for a scope you aren't allowed to request.
36
+ * {@link https://tools.ietf.org/html/rfc6749#section-3.3}
37
+ * Defaults to `openid`.
38
+ */
39
+ scope?: 'openid' | (string & {});
40
+ /**
41
+ * Redirect uri that will receive the code. Must exactly match a redirectUri from your client
42
+ * in self-service
43
+ */
44
+ redirectUri?: string;
45
+ /** Should we try to open a popup window? Defaults to `false`. */
46
+ preferPopup?: boolean;
47
+ /** user email or UUID hint */
48
+ loginHint?: string;
49
+ /** Pulse tag */
50
+ tag?: string;
51
+ /** Teaser slug. Teaser with given slug will be displayed in place of default teaser */
52
+ teaser?: string;
53
+ /**
54
+ * Specifies the allowable elapsed time in seconds since the last time the End-User was actively
55
+ * authenticated. If last authentication time is more than maxAge seconds in the past,
56
+ * re-authentication will be required. See the OpenID Connect spec section 3.1.2.1 for more
57
+ * information. Prefer a number of seconds; strings remain accepted for legacy callers.
58
+ */
59
+ maxAge?: number | string;
60
+ /**
61
+ * Optional parameter to overwrite client locale setting.
62
+ * New flows supports nb_NO, fi_FI, sv_SE, en_US
63
+ */
64
+ locale?: 'nb_NO' | 'fi_FI' | 'sv_SE' | 'en_US';
65
+ /** display username and password on one screen. Defaults to `false`. */
66
+ oneStepLogin?: boolean;
67
+ /**
68
+ * String that specifies whether the Authorization Server prompts the End-User for
69
+ * reauthentication or confirm account screen. Supported values: `select_account` or `login`.
70
+ * Defaults to `select_account`.
71
+ */
72
+ prompt?: 'select_account' | 'login';
73
+ /** Identifier for cross-domain tracking in Pulse */
74
+ xDomainId?: string;
75
+ /** Environment for cross-domain tracking in Pulse */
76
+ xEnvironmentId?: string;
77
+ /** Campaign identifier for tracking in Pulse */
78
+ originCampaign?: string;
79
+ };
80
+
81
+ /**
82
+ * Identical to {@link LoginOptions} except that `state` may also be a (possibly async) function;
83
+ * all other fields reuse the `LoginOptions` documentation.
84
+ */
85
+ export type SimplifiedLoginWidgetLoginOptions = Omit<LoginOptions, 'state'> & {
86
+ /**
87
+ * An opaque value used by the client to maintain state between the request and callback.
88
+ * It's also recommended to prevent CSRF {@link https://tools.ietf.org/html/rfc6749#section-10.12}
89
+ */
90
+ state: string | (() => string | Promise<string>);
91
+ };
92
+
93
+ /**
94
+ * Minimal user information used by the simplified login widget flow.
95
+ */
96
+ export type SimplifiedLoginData = {
97
+ /** Deprecated: User UUID, to be be used as `loginHint` for {@link Identity#login} */
98
+ identifier: string;
99
+ /** Human-readable user identifier */
100
+ display_text: string;
101
+ /** Client name */
102
+ client_name: string;
103
+ /** Provider id used by the simplified login widget when supplied by the backend. */
104
+ provider_id?: string;
105
+ };
106
+
107
+ /**
108
+ * Configuration options for {@link Identity#showSimplifiedLoginWidget}.
109
+ */
110
+ export type SimplifiedLoginWidgetOptions = {
111
+ /** expected encoding of simplified login widget. Could be utf-8 (default), iso-8859-1 or iso-8859-15 */
112
+ encoding?: string;
113
+ /**
114
+ * expected locale of simplified login widget. Should be provided in a short format like 'nb',
115
+ * 'sv'. If not set, a value from the env variable is used.
116
+ */
117
+ locale?: 'nb' | 'sv' | 'fi' | 'da' | 'en';
118
+ };
119
+
120
+ export type SimplifiedLoginWidgetInitialParams = {
121
+ displayText: string;
122
+ env: string;
123
+ clientName: string;
124
+ clientId: string;
125
+ providerId?: string;
126
+ locale?: SimplifiedLoginWidgetOptions['locale'];
127
+ windowWidth: () => number;
128
+ windowOnResize: (handler: () => void) => void;
129
+ };
130
+
131
+ export type SimplifiedLoginWidgetHandler = () => void | Promise<void>;
132
+
133
+ export type OpenSimplifiedLoginWidget = (
134
+ initialParams: SimplifiedLoginWidgetInitialParams,
135
+ loginHandler: SimplifiedLoginWidgetHandler,
136
+ loginNotYouHandler: SimplifiedLoginWidgetHandler,
137
+ initHandler: SimplifiedLoginWidgetHandler,
138
+ cancelLoginHandler: SimplifiedLoginWidgetHandler,
139
+ ) => unknown;
140
+
141
+ export const hasOpenSimplifiedLoginWidget = (
142
+ targetWindow: Window,
143
+ ): targetWindow is Window & { openSimplifiedLoginWidget: OpenSimplifiedLoginWidget } => {
144
+ return typeof targetWindow.openSimplifiedLoginWidget === 'function';
145
+ };
@@ -0,0 +1,35 @@
1
+ /* Copyright 2026 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.
2
+ * See LICENSE.md in the project root.
3
+ */
4
+
5
+ import SDKError from '../sdk-error.js';
6
+ import { isObject, isStr } from '../validate.js';
7
+ import type { HasAccessResult } from './monetization.js';
8
+
9
+ export function parseHasAccessResult(value: unknown): HasAccessResult {
10
+ if (!isObject(value)) {
11
+ throw new SDKError('Invalid hasAccess response');
12
+ }
13
+
14
+ const { entitled, ttl, allowedFeatures, userId, uuid, sig } = value;
15
+
16
+ if (
17
+ typeof entitled !== 'boolean' ||
18
+ typeof ttl !== 'number' ||
19
+ !Array.isArray(allowedFeatures) ||
20
+ (typeof userId !== 'number' && typeof userId !== 'string') ||
21
+ !isStr(uuid) ||
22
+ !isStr(sig)
23
+ ) {
24
+ throw new SDKError('Invalid hasAccess response');
25
+ }
26
+
27
+ return {
28
+ entitled,
29
+ ttl,
30
+ allowedFeatures: allowedFeatures.filter(isStr),
31
+ userId,
32
+ uuid,
33
+ sig,
34
+ };
35
+ }
@@ -0,0 +1,24 @@
1
+ import type { AccountBaseConfig } from './common';
2
+ import type { SessionUserId } from './session';
3
+
4
+ /**
5
+ * Result returned by the Session Service `/hasAccess` endpoint, describing whether the user
6
+ * is entitled to a requested set of products/features.
7
+ * @internal
8
+ */
9
+ export interface HasAccessResult {
10
+ /** Whether the user is entitled to at least one of the requested products/features. */
11
+ entitled: boolean;
12
+ /** How long this result stays valid, in seconds, before the cache entry expires. */
13
+ ttl: number;
14
+ allowedFeatures: string[];
15
+ userId: SessionUserId;
16
+ uuid: string;
17
+ sig: string;
18
+ }
19
+
20
+ /**
21
+ * Options accepted by the {@link Monetization} constructor.
22
+ */
23
+ export type MonetizationOptions = Pick<AccountBaseConfig, 'clientId' | 'env' | 'window'> &
24
+ Partial<Pick<AccountBaseConfig, 'redirectUri' | 'sessionDomain'>>;
@@ -0,0 +1,89 @@
1
+ /* Copyright 2026 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.
2
+ * See LICENSE.md in the project root.
3
+ */
4
+
5
+ import { isObject, isStr } from '../validate.js';
6
+ import type {
7
+ ConnectedSessionResponse,
8
+ ConnectedSessionWithUserIdResponse,
9
+ SessionFailureResponse,
10
+ SessionResponse,
11
+ SessionSuccessResponse,
12
+ UnrecognizedSessionResponse,
13
+ } from './session.js';
14
+
15
+ type SessionObjectResponse =
16
+ | SessionSuccessResponse
17
+ | SessionFailureResponse
18
+ | UnrecognizedSessionResponse;
19
+
20
+ type RedirectSessionPayload = {
21
+ redirectURL: string;
22
+ };
23
+
24
+ export type NormalizedSessionPayload = SessionObjectResponse | RedirectSessionPayload;
25
+
26
+ export function isSessionSuccessResponse(value: unknown): value is SessionSuccessResponse {
27
+ return isObject(value) && typeof value.result === 'boolean';
28
+ }
29
+
30
+ export function isConnectedSessionResponse(value: unknown): value is ConnectedSessionResponse {
31
+ return isSessionSuccessResponse(value) && value.result === true;
32
+ }
33
+
34
+ export function isSessionFailureResponse(value: unknown): value is SessionFailureResponse {
35
+ return isObject(value) && Boolean(value.error);
36
+ }
37
+
38
+ export function isRedirectSessionPayload(value: unknown): value is RedirectSessionPayload {
39
+ return isObject(value) && Object.keys(value).length === 1 && isStr(value.redirectURL);
40
+ }
41
+
42
+ export function isConnectedSessionWithUserIdResponse(
43
+ value: SessionResponse,
44
+ ): value is ConnectedSessionWithUserIdResponse {
45
+ return (
46
+ isConnectedSessionResponse(value) &&
47
+ (typeof value.userId === 'number' || typeof value.userId === 'string')
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Converts an unknown Session Service `hasSession` payload into one of the shapes the SDK knows how
53
+ * to handle.
54
+ *
55
+ * This function is intentionally tolerant because `hasSession()` historically resolved unknown
56
+ * object payloads instead of rejecting them. The SDK relies on that behavior for backwards
57
+ * compatibility with clients that check the raw response themselves.
58
+ *
59
+ * The normal cases are:
60
+ * - regular success payloads with a boolean `result`;
61
+ * - failure payloads wrapped as `{ error, response? }`;
62
+ * - Safari/session-refresh redirect payloads shaped as `{ redirectURL }`.
63
+ *
64
+ * Any other object is preserved as an unrecognized response so callers can still receive it.
65
+ * Non-object values are normalized to `{}`, matching the old "empty response" fallback used by
66
+ * `hasSession()` and cached-session reads.
67
+ *
68
+ * This is used both for network responses parsed by `RESTClient` and for values restored from the
69
+ * session cache, so the rest of the Identity flow can switch on typed guards instead of repeatedly
70
+ * re-validating unknown data.
71
+ *
72
+ * @internal
73
+ */
74
+ export function normalizeSessionResponse(value: unknown): NormalizedSessionPayload {
75
+ if (isRedirectSessionPayload(value)) {
76
+ return value;
77
+ }
78
+ if (isSessionFailureResponse(value)) {
79
+ return value;
80
+ }
81
+ if (isSessionSuccessResponse(value)) {
82
+ return value;
83
+ }
84
+ if (isObject(value)) {
85
+ return value;
86
+ }
87
+
88
+ return {};
89
+ }