@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.
- package/README.md +39 -64
- package/dist/account-sdk.d.ts +4 -0
- package/dist/account.d.ts +29 -0
- package/dist/cache.d.ts +65 -0
- package/dist/config.d.ts +86 -0
- package/dist/get-account-sdk.d.ts +3 -0
- package/dist/global-registry.d.ts +23 -0
- package/dist/globals.d.ts +15 -0
- package/dist/has-access-response.d.ts +2 -0
- package/dist/identity.d.ts +323 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +859 -0
- package/dist/index.js.map +1 -0
- package/dist/monetization.d.ts +58 -0
- package/{src → dist}/object.d.ts +4 -9
- package/dist/popup.d.ts +9 -0
- package/dist/resolve-browser-window.d.ts +1 -0
- package/dist/rest-client.d.ts +102 -0
- package/dist/rest-clients.d.ts +10 -0
- package/dist/sdk-error.d.ts +27 -0
- package/dist/session-response.d.ts +30 -0
- package/{src/spidTalk.d.ts → dist/spid-talk.d.ts} +4 -6
- package/dist/types/account.d.ts +13 -0
- package/dist/types/common.d.ts +22 -0
- package/dist/types/identity.d.ts +41 -0
- package/dist/types/login.d.ts +120 -0
- package/dist/types/monetization-guards.d.ts +2 -0
- package/dist/types/monetization.d.ts +21 -0
- package/dist/types/session-guards.d.ts +36 -0
- package/dist/types/session.d.ts +124 -0
- package/dist/url.d.ts +8 -0
- package/dist/validate.d.ts +50 -0
- package/dist/version.d.ts +2 -0
- package/package.json +30 -22
- package/src/account-sdk.ts +74 -0
- package/src/account.ts +149 -0
- package/src/{cache.js → cache.ts} +53 -38
- package/src/{config.js → config.ts} +7 -32
- package/src/get-account-sdk.ts +94 -0
- package/src/global-registry.ts +39 -0
- package/src/globals.ts +12 -0
- package/src/has-access-response.ts +35 -0
- package/src/identity.ts +1102 -0
- package/src/index.ts +32 -0
- package/src/{monetization.js → monetization.ts} +65 -73
- package/src/{object.js → object.ts} +9 -16
- package/src/popup.ts +74 -0
- package/src/resolve-browser-window.ts +11 -0
- package/src/rest-client.ts +253 -0
- package/src/rest-clients.ts +30 -0
- package/src/sdk-error.ts +59 -0
- package/src/session-response.ts +85 -0
- package/src/{spidTalk.js → spid-talk.ts} +10 -12
- package/src/types/account.ts +27 -0
- package/src/types/common.ts +26 -0
- package/src/types/identity.ts +55 -0
- package/src/types/login.ts +145 -0
- package/src/types/monetization-guards.ts +35 -0
- package/src/types/monetization.ts +24 -0
- package/src/types/session-guards.ts +89 -0
- package/src/types/session.ts +147 -0
- package/src/{url.js → url.ts} +6 -10
- package/src/{validate.js → validate.ts} +27 -43
- package/src/{version.js → version.ts} +1 -2
- package/identity.d.ts +0 -1
- package/identity.js +0 -5
- package/index.d.ts +0 -4
- package/index.js +0 -9
- package/monetization.d.ts +0 -1
- package/monetization.js +0 -5
- package/payment.d.ts +0 -1
- package/payment.js +0 -5
- package/src/RESTClient.d.ts +0 -89
- package/src/RESTClient.js +0 -193
- package/src/SDKError.d.ts +0 -16
- package/src/SDKError.js +0 -55
- package/src/cache.d.ts +0 -64
- package/src/config.d.ts +0 -34
- package/src/global-registry.js +0 -20
- package/src/identity.d.ts +0 -679
- package/src/identity.js +0 -1154
- package/src/monetization.d.ts +0 -80
- package/src/payment.d.ts +0 -115
- package/src/payment.js +0 -211
- package/src/popup.d.ts +0 -10
- package/src/popup.js +0 -59
- package/src/url.d.ts +0 -10
- package/src/validate.d.ts +0 -64
- package/src/version.d.ts +0 -2
package/src/account.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
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 { registerAndDispatchInGlobal } from './global-registry.js';
|
|
6
|
+
import Identity from './identity.js';
|
|
7
|
+
import Monetization from './monetization.js';
|
|
8
|
+
import { resolveBrowserWindow } from './resolve-browser-window.js';
|
|
9
|
+
import SDKError from './sdk-error.js';
|
|
10
|
+
import type { AccountConfig, AccountEventHandler, AccountEventName } from './types/account.js';
|
|
11
|
+
import type {
|
|
12
|
+
LoginOptions,
|
|
13
|
+
SimplifiedLoginData,
|
|
14
|
+
SimplifiedLoginWidgetLoginOptions,
|
|
15
|
+
SimplifiedLoginWidgetOptions,
|
|
16
|
+
} from './types/login.js';
|
|
17
|
+
import type { HasAccessResult } from './types/monetization.js';
|
|
18
|
+
import type { ConnectedSessionResponse, SessionResponse, SessionUserId } from './types/session.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Unified client-side interface for a configured Schibsted Account integration.
|
|
22
|
+
*/
|
|
23
|
+
export class Account {
|
|
24
|
+
#identity: Identity;
|
|
25
|
+
#monetization: Monetization;
|
|
26
|
+
|
|
27
|
+
constructor(config: AccountConfig) {
|
|
28
|
+
const browserWindow = resolveBrowserWindow(config.window);
|
|
29
|
+
|
|
30
|
+
if (!browserWindow) {
|
|
31
|
+
throw new SDKError('Schibsted Account SDK is not available in this browser context');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (browserWindow.schAccount) {
|
|
35
|
+
throw new SDKError('Schibsted Account SDK has already been initialized');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { log, callbackBeforeRedirect, varnish } = config;
|
|
39
|
+
const sharedConfig = {
|
|
40
|
+
clientId: config.clientId,
|
|
41
|
+
redirectUri: config.redirectUri,
|
|
42
|
+
sessionDomain: config.sessionDomain,
|
|
43
|
+
env: config.env,
|
|
44
|
+
window: browserWindow,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
this.#identity = new Identity({
|
|
48
|
+
...sharedConfig,
|
|
49
|
+
log,
|
|
50
|
+
callbackBeforeRedirect,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (varnish) {
|
|
54
|
+
if (typeof varnish !== 'object' || Array.isArray(varnish)) {
|
|
55
|
+
throw new SDKError('Account varnish config must be an object');
|
|
56
|
+
}
|
|
57
|
+
this.#identity.enableVarnishCookie(varnish);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.#monetization = new Monetization(sharedConfig);
|
|
61
|
+
|
|
62
|
+
registerAndDispatchInGlobal(browserWindow, 'schAccount', this);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
hasSession(): Promise<SessionResponse> {
|
|
66
|
+
return this.#identity.hasSession();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
isLoggedIn(): Promise<boolean> {
|
|
70
|
+
return this.#identity.isLoggedIn();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
isConnected(): Promise<boolean> {
|
|
74
|
+
return this.#identity.isConnected();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getUser(): Promise<ConnectedSessionResponse> {
|
|
78
|
+
return this.#identity.getUser();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getUserId(): Promise<string> {
|
|
82
|
+
return this.#identity.getUserId();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getUserSDRN(): Promise<string> {
|
|
86
|
+
return this.#identity.getUserSDRN();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getUserContextData(): Promise<SimplifiedLoginData | null> {
|
|
90
|
+
return this.#identity.getUserContextData();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getSpId(): Promise<string | null> {
|
|
94
|
+
return this.#identity.getSpId();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
login(options: LoginOptions): Window | null {
|
|
98
|
+
return this.#identity.login(options);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logout(redirectUri?: string): void {
|
|
102
|
+
this.#identity.logout(redirectUri);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
loginUrl(options: LoginOptions): string {
|
|
106
|
+
return this.#identity.loginUrl(options);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
showSimplifiedLoginWidget(
|
|
110
|
+
loginParams: SimplifiedLoginWidgetLoginOptions,
|
|
111
|
+
options?: SimplifiedLoginWidgetOptions,
|
|
112
|
+
): Promise<boolean> {
|
|
113
|
+
return this.#identity.showSimplifiedLoginWidget(loginParams, options);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
clearCachedUserSession(): void {
|
|
117
|
+
this.#identity.clearCachedUserSession();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
hasAccess(productIds: string[], userId: SessionUserId): Promise<HasAccessResult | null> {
|
|
121
|
+
return this.#monetization.hasAccess(productIds, userId);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
clearCachedAccessResult(productIds: string[], userId: SessionUserId): void {
|
|
125
|
+
this.#monetization.clearCachedAccessResult(productIds, userId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
on(event: AccountEventName, callback: AccountEventHandler, ctx?: unknown): this {
|
|
129
|
+
if (event === 'hasAccess') {
|
|
130
|
+
this.#monetization.on(event, callback, ctx);
|
|
131
|
+
} else {
|
|
132
|
+
this.#identity.on(event, callback, ctx);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
off(event: AccountEventName, callback?: AccountEventHandler): this {
|
|
139
|
+
if (event === 'hasAccess') {
|
|
140
|
+
this.#monetization.off(event, callback);
|
|
141
|
+
} else {
|
|
142
|
+
this.#identity.off(event, callback);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default Account;
|
|
@@ -2,45 +2,47 @@
|
|
|
2
2
|
* See LICENSE.md in the project root.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import SDKError from './SDKError.js';
|
|
5
|
+
import SDKError from './sdk-error.js';
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* Check whether we are able to use web storage
|
|
11
|
-
* @param
|
|
9
|
+
* @param storeProvider - A function to return a WebStorage instance (either
|
|
12
10
|
* `sessionStorage` or `localStorage` from a `Window` object)
|
|
13
11
|
* @private
|
|
14
|
-
* @returns {boolean}
|
|
15
12
|
*/
|
|
16
|
-
function webStorageWorks(storeProvider) {
|
|
13
|
+
function webStorageWorks(storeProvider: (() => Storage) | undefined): boolean {
|
|
17
14
|
if (!storeProvider) {
|
|
18
15
|
return false;
|
|
19
16
|
}
|
|
20
17
|
try {
|
|
21
18
|
const store = storeProvider();
|
|
22
|
-
const randomKey = 'x-x-x-x'.replace(/x/g, () => Math.random());
|
|
19
|
+
const randomKey = 'x-x-x-x'.replace(/x/g, () => String(Math.random()));
|
|
23
20
|
const testValue = 'TEST-VALUE';
|
|
24
21
|
store.setItem(randomKey, testValue);
|
|
25
22
|
const val = store.getItem(randomKey);
|
|
26
23
|
store.removeItem(randomKey);
|
|
27
|
-
return
|
|
28
|
-
} catch
|
|
24
|
+
return val === testValue;
|
|
25
|
+
} catch {
|
|
29
26
|
return false;
|
|
30
27
|
}
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
/**
|
|
34
|
-
*
|
|
31
|
+
* Cache implementation used when web storage is available. Wraps a `Storage` instance.
|
|
35
32
|
* @private
|
|
36
33
|
*/
|
|
37
34
|
class WebStorageCache {
|
|
35
|
+
store: Storage;
|
|
36
|
+
get: (key: string) => string | null;
|
|
37
|
+
set: (key: string, value: string) => void;
|
|
38
|
+
delete: (key: string) => void;
|
|
39
|
+
|
|
38
40
|
/**
|
|
39
41
|
* Create web storage cache object
|
|
40
|
-
* @param
|
|
42
|
+
* @param store - A reference to either `sessionStorage` or `localStorage` from a
|
|
41
43
|
* `Window` object
|
|
42
44
|
*/
|
|
43
|
-
constructor(store) {
|
|
45
|
+
constructor(store: Storage) {
|
|
44
46
|
this.store = store;
|
|
45
47
|
this.get = (key) => this.store.getItem(key);
|
|
46
48
|
this.set = (key, value) => this.store.setItem(key, value);
|
|
@@ -49,35 +51,49 @@ class WebStorageCache {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
54
|
+
* Cache implementation used when web storage is not available. Stores entries in an in-memory object.
|
|
53
55
|
* @private
|
|
54
56
|
*/
|
|
55
57
|
class LiteralCache {
|
|
58
|
+
store: Record<string, string>;
|
|
59
|
+
get: (key: string) => string | undefined;
|
|
60
|
+
set: (key: string, value: string) => void;
|
|
61
|
+
delete: (key: string) => void;
|
|
62
|
+
|
|
56
63
|
/**
|
|
57
64
|
* Create JS object literal cache object
|
|
58
65
|
*/
|
|
59
66
|
constructor() {
|
|
60
67
|
this.store = {};
|
|
61
68
|
this.get = (key) => this.store[key];
|
|
62
|
-
this.set = (key, value) =>
|
|
63
|
-
|
|
69
|
+
this.set = (key, value) => {
|
|
70
|
+
this.store[key] = value;
|
|
71
|
+
};
|
|
72
|
+
this.delete = (key) => {
|
|
73
|
+
delete this.store[key];
|
|
74
|
+
};
|
|
64
75
|
}
|
|
65
76
|
}
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
type CacheEntry = { expiresOn: number; value: unknown };
|
|
79
|
+
|
|
80
|
+
const maxExpiresIn = 2 ** 31 - 1;
|
|
68
81
|
|
|
69
82
|
/**
|
|
70
83
|
* Cache class that attempts WebStorage (session/local storage), and falls back to JS object literal
|
|
71
84
|
* @private
|
|
72
85
|
*/
|
|
73
86
|
export default class Cache {
|
|
87
|
+
cache: WebStorageCache | LiteralCache;
|
|
88
|
+
type: string;
|
|
89
|
+
|
|
74
90
|
/**
|
|
75
|
-
* @param
|
|
91
|
+
* @param storeProvider - A function to return a WebStorage instance (either
|
|
76
92
|
* `sessionStorage` or `localStorage` from a `Window` object)
|
|
77
93
|
* @throws {SDKError} - If sessionStorage or localStorage are not accessible
|
|
78
94
|
*/
|
|
79
|
-
constructor(storeProvider) {
|
|
80
|
-
if (webStorageWorks(storeProvider)) {
|
|
95
|
+
constructor(storeProvider?: () => Storage) {
|
|
96
|
+
if (storeProvider && webStorageWorks(storeProvider)) {
|
|
81
97
|
this.cache = new WebStorageCache(storeProvider());
|
|
82
98
|
this.type = 'WebStorage';
|
|
83
99
|
} else {
|
|
@@ -88,46 +104,46 @@ export default class Cache {
|
|
|
88
104
|
|
|
89
105
|
/**
|
|
90
106
|
* Get a value from cache (checks that the object has not expired)
|
|
91
|
-
* @param
|
|
107
|
+
* @param key
|
|
92
108
|
* @private
|
|
93
|
-
* @returns {*} - The value if it exists, otherwise null
|
|
94
109
|
*/
|
|
95
|
-
get(key) {
|
|
110
|
+
get(key: string): unknown {
|
|
96
111
|
/**
|
|
97
112
|
* JSON.parse safe wrapper
|
|
98
|
-
* @param
|
|
99
|
-
* @returns {*} parsed value or null if failed to parse
|
|
113
|
+
* @param raw
|
|
100
114
|
*/
|
|
101
|
-
function getObj(raw) {
|
|
115
|
+
function getObj(raw: string | null | undefined): CacheEntry | null {
|
|
116
|
+
if (raw == null) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
102
119
|
try {
|
|
103
120
|
return JSON.parse(raw);
|
|
104
|
-
} catch
|
|
121
|
+
} catch {
|
|
105
122
|
return null;
|
|
106
123
|
}
|
|
107
124
|
}
|
|
108
125
|
|
|
109
126
|
try {
|
|
110
127
|
const raw = this.cache.get(key);
|
|
111
|
-
|
|
128
|
+
const obj = getObj(raw);
|
|
112
129
|
if (obj && Number.isInteger(obj.expiresOn) && obj.expiresOn > Date.now()) {
|
|
113
130
|
return obj.value;
|
|
114
131
|
}
|
|
115
132
|
this.delete(key);
|
|
116
133
|
return null;
|
|
117
134
|
} catch (e) {
|
|
118
|
-
throw new SDKError(e);
|
|
135
|
+
throw new SDKError(String(e));
|
|
119
136
|
}
|
|
120
137
|
}
|
|
121
138
|
|
|
122
139
|
/**
|
|
123
140
|
* Set a cache entry
|
|
124
|
-
* @param
|
|
125
|
-
* @param
|
|
126
|
-
* @param
|
|
141
|
+
* @param key
|
|
142
|
+
* @param value
|
|
143
|
+
* @param expiresIn - Value in milliseconds until the entry expires
|
|
127
144
|
* @private
|
|
128
|
-
* @returns {void}
|
|
129
145
|
*/
|
|
130
|
-
set(key, value, expiresIn = 0) {
|
|
146
|
+
set(key: string, value: unknown, expiresIn = 0): void {
|
|
131
147
|
if (expiresIn <= 0) {
|
|
132
148
|
return;
|
|
133
149
|
}
|
|
@@ -138,21 +154,20 @@ export default class Cache {
|
|
|
138
154
|
this.cache.set(key, JSON.stringify({ expiresOn, value }));
|
|
139
155
|
setTimeout(() => this.delete(key), expiresIn);
|
|
140
156
|
} catch (e) {
|
|
141
|
-
throw new SDKError(e);
|
|
157
|
+
throw new SDKError(String(e));
|
|
142
158
|
}
|
|
143
159
|
}
|
|
144
160
|
|
|
145
161
|
/**
|
|
146
162
|
* Delete a cache entry
|
|
147
|
-
* @param
|
|
163
|
+
* @param key
|
|
148
164
|
* @private
|
|
149
|
-
* @returns {void}
|
|
150
165
|
*/
|
|
151
|
-
delete(key) {
|
|
166
|
+
delete(key: string): void {
|
|
152
167
|
try {
|
|
153
168
|
this.cache.delete(key);
|
|
154
169
|
} catch (e) {
|
|
155
|
-
throw new SDKError(e);
|
|
170
|
+
throw new SDKError(String(e));
|
|
156
171
|
}
|
|
157
172
|
}
|
|
158
173
|
}
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
* See LICENSE.md in the project root.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
'use strict';
|
|
6
|
-
|
|
7
5
|
/*
|
|
8
6
|
* This file declares configs that are essentially part of how the SDK works and interacts with our
|
|
9
7
|
* backend servers.
|
|
@@ -18,34 +16,11 @@
|
|
|
18
16
|
*/
|
|
19
17
|
|
|
20
18
|
/**
|
|
21
|
-
* Core configuration used by the SDK
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* @prop {string} ENDPOINTS.SPiD.LOCAL - Local endpoint (for Identity team)
|
|
27
|
-
* @prop {string} ENDPOINTS.SPiD.DEV - Dev environment (for Identity team)
|
|
28
|
-
* @prop {string} ENDPOINTS.SPiD.PRE - Staging environment
|
|
29
|
-
* @prop {string} ENDPOINTS.SPiD.PRO - Production environment Sweden
|
|
30
|
-
* @prop {string} ENDPOINTS.SPiD.PRO_NO - Production environment Norway
|
|
31
|
-
* @prop {string} ENDPOINTS.SPiD.PRO_FI - Production environment Finland
|
|
32
|
-
* @prop {string} ENDPOINTS.SPiD.PRO_DK - Production environment Denmark
|
|
33
|
-
* @prop {object} ENDPOINTS.BFF - Endpoints used with new GDPR-compliant web flows
|
|
34
|
-
* @prop {string} ENDPOINTS.BFF.LOCAL - Local endpoint (for Identity team)
|
|
35
|
-
* @prop {string} ENDPOINTS.BFF.DEV - Dev environment (for Identity team)
|
|
36
|
-
* @prop {string} ENDPOINTS.BFF.PRE - Staging environment
|
|
37
|
-
* @prop {string} ENDPOINTS.BFF.PRO - Production environment Sweden
|
|
38
|
-
* @prop {string} ENDPOINTS.BFF.PRO_NO - Production environment Norway
|
|
39
|
-
* @prop {string} ENDPOINTS.BFF.PRO_FI - Production environment Finland
|
|
40
|
-
* @prop {string} ENDPOINTS.BFF.PRO_DK - Production environment Denmark
|
|
41
|
-
* @prop {object} ENDPOINTS.SESSION_SERVICE - Endpoints to check global user session data
|
|
42
|
-
* @prop {string} ENDPOINTS.SESSION_SERVICE.LOCAL - Local endpoint (for Identity team)
|
|
43
|
-
* @prop {string} ENDPOINTS.SESSION_SERVICE.DEV - Dev environment (for Identity team)
|
|
44
|
-
* @prop {string} ENDPOINTS.SESSION_SERVICE.PRE - Staging environment
|
|
45
|
-
* @prop {string} ENDPOINTS.SESSION_SERVICE.PRO - Production environment Sweden
|
|
46
|
-
* @prop {string} ENDPOINTS.SESSION_SERVICE.PRO_NO - Production environment Norway
|
|
47
|
-
* @prop {string} ENDPOINTS.SESSION_SERVICE.PRO_FI - Production environment Finland
|
|
48
|
-
* @prop {string} ENDPOINTS.SESSION_SERVICE.PRO_DK - Production environment Denmark
|
|
19
|
+
* Core configuration used by the SDK.
|
|
20
|
+
* - `ENDPOINTS.SPiD` — SPiD endpoints per environment
|
|
21
|
+
* - `ENDPOINTS.BFF` — Endpoints used with new GDPR-compliant web flows
|
|
22
|
+
* - `ENDPOINTS.SESSION_SERVICE` — Endpoints to check global user session data
|
|
23
|
+
*
|
|
49
24
|
*/
|
|
50
25
|
const config = {
|
|
51
26
|
ENDPOINTS: {
|
|
@@ -85,8 +60,8 @@ const config = {
|
|
|
85
60
|
PRO_NO: 'spid.no',
|
|
86
61
|
PRO_FI: 'schibsted.fi',
|
|
87
62
|
PRO_DK: 'schibsted.dk',
|
|
88
|
-
}
|
|
89
|
-
};
|
|
63
|
+
},
|
|
64
|
+
} as const;
|
|
90
65
|
|
|
91
66
|
export default config;
|
|
92
67
|
export const ENDPOINTS = config.ENDPOINTS;
|
|
@@ -0,0 +1,94 @@
|
|
|
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 { Account } from './account.js';
|
|
6
|
+
import { resolveBrowserWindow } from './resolve-browser-window.js';
|
|
7
|
+
import type { AccountConfig } from './types/account.js';
|
|
8
|
+
|
|
9
|
+
const ACCOUNT_READY_EVENT = 'schAccount:ready';
|
|
10
|
+
const GET_ACCOUNT_SDK_TIMEOUT_MS = 4000;
|
|
11
|
+
const IGNORED_CONFIG_WARNING =
|
|
12
|
+
'Schibsted Account SDK has already been initialized. The config passed to getAccountSdk() was ignored.';
|
|
13
|
+
|
|
14
|
+
let pendingAccountPromise: Promise<Account> | undefined;
|
|
15
|
+
let ignoredConfigWarningSent = false;
|
|
16
|
+
|
|
17
|
+
const createUnavailableError = (): Error =>
|
|
18
|
+
new Error('Schibsted Account SDK is not available in this browser context');
|
|
19
|
+
|
|
20
|
+
const createTimeoutError = (): Error =>
|
|
21
|
+
new Error(`Schibsted Account SDK was not initialized within ${GET_ACCOUNT_SDK_TIMEOUT_MS} ms`);
|
|
22
|
+
|
|
23
|
+
const warnIgnoredConfigOnce = (): void => {
|
|
24
|
+
if (ignoredConfigWarningSent) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.warn(IGNORED_CONFIG_WARNING);
|
|
29
|
+
ignoredConfigWarningSent = true;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function getAccountSdk(config?: AccountConfig): Promise<Account> {
|
|
33
|
+
const browserWindow = resolveBrowserWindow(config?.window);
|
|
34
|
+
|
|
35
|
+
if (!browserWindow) {
|
|
36
|
+
const error = createUnavailableError();
|
|
37
|
+
console.error(error.message);
|
|
38
|
+
return Promise.reject(error);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (browserWindow.schAccount) {
|
|
42
|
+
if (config) {
|
|
43
|
+
warnIgnoredConfigOnce();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Promise.resolve(browserWindow.schAccount);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (config) {
|
|
50
|
+
return Promise.resolve(new Account(config));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (pendingAccountPromise) {
|
|
54
|
+
return pendingAccountPromise;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pendingAccountPromise = new Promise<Account>((resolve, reject) => {
|
|
58
|
+
let timeoutId: number | undefined;
|
|
59
|
+
|
|
60
|
+
const cleanup = () => {
|
|
61
|
+
browserWindow.removeEventListener(ACCOUNT_READY_EVENT, handleReady);
|
|
62
|
+
if (timeoutId !== undefined) {
|
|
63
|
+
browserWindow.clearTimeout(timeoutId);
|
|
64
|
+
}
|
|
65
|
+
pendingAccountPromise = undefined;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const resolveRegisteredAccount = () => {
|
|
69
|
+
const account = browserWindow.schAccount;
|
|
70
|
+
if (!account) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
cleanup();
|
|
75
|
+
resolve(account);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleReady = () => {
|
|
79
|
+
resolveRegisteredAccount();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
browserWindow.addEventListener(ACCOUNT_READY_EVENT, handleReady);
|
|
83
|
+
timeoutId = browserWindow.setTimeout(() => {
|
|
84
|
+
const error = createTimeoutError();
|
|
85
|
+
console.error(error.message);
|
|
86
|
+
cleanup();
|
|
87
|
+
reject(error);
|
|
88
|
+
}, GET_ACCOUNT_SDK_TIMEOUT_MS);
|
|
89
|
+
|
|
90
|
+
resolveRegisteredAccount();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return pendingAccountPromise;
|
|
94
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import './globals.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Names of the SDK instance properties that can be registered on the global {@link Window} object.
|
|
5
|
+
*/
|
|
6
|
+
type Property = 'schAccount' | 'schIdentity' | 'schMonetization';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Registers an SDK instance on the global {@link Window} object's given property and dispatches a ready event.
|
|
10
|
+
*
|
|
11
|
+
* The dispatched event is a {@link CustomEvent} fired on `global` (i.e. `window`) with:
|
|
12
|
+
* - name: `${property}:ready` (e.g. `"schIdentity:ready"` or `"schMonetization:ready"`)
|
|
13
|
+
* - `detail.instance`: the registered SDK instance
|
|
14
|
+
*
|
|
15
|
+
* @typeParam T - Constrained to {@link Property}; inferred from the `property` argument.
|
|
16
|
+
* @param global - The `Window` object to register on.
|
|
17
|
+
* @param property - The window property name to assign the instance to, 'schIdentity' or 'schMonetization'.
|
|
18
|
+
* @param instance - The SDK instance to register. Must be non-nullable.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* registerAndDispatchInGlobal(window, 'schIdentity', identityInstance);
|
|
22
|
+
* / window.schIdentity === identityInstance
|
|
23
|
+
* / CustomEvent 'schIdentity:ready' has been dispatched on window
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export const registerAndDispatchInGlobal = <T extends Property>(
|
|
27
|
+
global: Window,
|
|
28
|
+
property: T,
|
|
29
|
+
instance: NonNullable<Window[T]>,
|
|
30
|
+
): void => {
|
|
31
|
+
if (!global[property]) {
|
|
32
|
+
global[property] = instance;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
global.dispatchEvent(
|
|
36
|
+
//TODO -> https://schibsted.ghe.com/connect/team/issues/645
|
|
37
|
+
new CustomEvent(`${property}:ready`, { detail: { instance: global[property] } }),
|
|
38
|
+
);
|
|
39
|
+
};
|
package/src/globals.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Account } from './account.js';
|
|
2
|
+
import type { Identity } from './identity.js';
|
|
3
|
+
import type { Monetization } from './monetization.js';
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
schAccount?: Account;
|
|
8
|
+
schIdentity?: Identity;
|
|
9
|
+
schMonetization?: Monetization;
|
|
10
|
+
SPiD?: { Talk?: { response?: (callbackName: string, data: unknown) => unknown } };
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -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 type { HasAccessResult } from './types/monetization.js';
|
|
7
|
+
import { isObject, isStr } from './validate.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
|
+
}
|