@accelbyte/sdk 1.1.2 → 1.2.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.
@@ -0,0 +1,842 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+ var axios = require('axios');
5
+ var qs = require('query-string');
6
+ var uuid = require('uuid');
7
+ var zod = require('zod');
8
+ var isEqual = require('lodash/isEqual.js');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var uuid__namespace = /*#__PURE__*/_interopNamespaceDefault(uuid);
28
+
29
+ /*
30
+ * Copyright (c) 2018-2023 AccelByte Inc. All Rights Reserved
31
+ * This is licensed software from AccelByte Inc, for limitations
32
+ * and restrictions contact your company contract manager.
33
+ */
34
+ global.crypto = crypto.webcrypto;
35
+
36
+ var _a$1;
37
+ class SdkDevice {
38
+ }
39
+ _a$1 = SdkDevice;
40
+ SdkDevice.ID_KEY = 'deviceId';
41
+ SdkDevice.TYPE = {
42
+ MOBILE: 'mobile',
43
+ DESKTOP: 'desktop'
44
+ };
45
+ SdkDevice.getType = () => {
46
+ return isMobile() ? SdkDevice.TYPE.MOBILE : SdkDevice.TYPE.DESKTOP;
47
+ };
48
+ SdkDevice.generateUUID = () => {
49
+ const deviceIdInUUID = uuid__namespace.v4().split('-').join('');
50
+ localStorage.setItem(SdkDevice.ID_KEY, deviceIdInUUID);
51
+ return deviceIdInUUID;
52
+ };
53
+ SdkDevice.getDeviceId = () => {
54
+ return localStorage.getItem(_a$1.ID_KEY) || _a$1.generateUUID();
55
+ };
56
+ /*
57
+ Bellow function is copied from npm 'is-mobile'
58
+ */
59
+ const mobileRE = /(android|bb\d+|meego).+mobile|armv7l|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i;
60
+ const tabletRE = /android|ipad|playbook|silk/i;
61
+ const isMobile = (opts) => {
62
+ if (!opts)
63
+ opts = {};
64
+ let ua = opts.ua;
65
+ if (!ua && typeof navigator !== 'undefined')
66
+ ua = navigator.userAgent;
67
+ if (ua && ua.headers && typeof ua.headers['user-agent'] === 'string') {
68
+ ua = ua.headers['user-agent'];
69
+ }
70
+ if (typeof ua !== 'string')
71
+ return false;
72
+ let result = mobileRE.test(ua) || (!!opts.tablet && tabletRE.test(ua));
73
+ if (!result &&
74
+ opts.tablet &&
75
+ opts.featureDetect &&
76
+ navigator &&
77
+ navigator.maxTouchPoints > 1 &&
78
+ ua.indexOf('Macintosh') !== -1 &&
79
+ ua.indexOf('Safari') !== -1) {
80
+ result = true;
81
+ }
82
+ return result;
83
+ };
84
+
85
+ /*
86
+ * Copyright (c) 2022-2023 AccelByte Inc. All Rights Reserved
87
+ * This is licensed software from AccelByte Inc, for limitations
88
+ * and restrictions contact your company contract manager.
89
+ */
90
+ const requestInterceptors = new Map();
91
+ const responseInterceptors = new Map();
92
+ const injectRequestInterceptors = (requestInterceptor, errorInterceptor) => {
93
+ const pair = { interceptor: requestInterceptor, errorInterceptor };
94
+ const ejectId = axios.interceptors.request.use(requestInterceptor, errorInterceptor);
95
+ requestInterceptors.set(ejectId, pair);
96
+ return ejectId;
97
+ };
98
+ const injectResponseInterceptors = (responseInterceptor, errorInterceptor) => {
99
+ const pair = { interceptor: responseInterceptor, errorInterceptor };
100
+ const ejectId = axios.interceptors.response.use(responseInterceptor, errorInterceptor);
101
+ responseInterceptors.set(ejectId, pair);
102
+ return ejectId;
103
+ };
104
+ const loggerInterceptor = (axiosInstance) => {
105
+ axiosInstance.interceptors.request.use(config => {
106
+ // Logger.info(config.method?.toUpperCase(), `${config.url}`)
107
+ return config;
108
+ }, error => {
109
+ return Promise.reject(error);
110
+ });
111
+ };
112
+ class Network {
113
+ //
114
+ static create(...configs) {
115
+ const axiosInstance = axios.create(Object.assign({
116
+ paramsSerializer: qs.stringify
117
+ }, ...configs));
118
+ Array.from(requestInterceptors).forEach(([_key, interceptorPair]) => {
119
+ const { interceptor, errorInterceptor } = interceptorPair;
120
+ axiosInstance.interceptors.request.use(interceptor, errorInterceptor);
121
+ });
122
+ Array.from(responseInterceptors).forEach(([_key, interceptorPair]) => {
123
+ const { interceptor, errorInterceptor } = interceptorPair;
124
+ axiosInstance.interceptors.response.use(interceptor, errorInterceptor);
125
+ });
126
+ loggerInterceptor(axiosInstance);
127
+ return axiosInstance;
128
+ }
129
+ static withBearerToken(accessToken, config) {
130
+ return Network.create(config || {}, {
131
+ headers: { Authorization: `Bearer ${accessToken}` }
132
+ });
133
+ }
134
+ }
135
+ Network.setDeviceTokenCookie = () => {
136
+ const deviceId = SdkDevice.getDeviceId();
137
+ document.cookie = `device_token=${deviceId}; path=/;`;
138
+ };
139
+ Network.removeDeviceTokenCookie = () => {
140
+ document.cookie = `device_token=; expires=${new Date(0).toUTCString()}`;
141
+ };
142
+ Network.getFormUrlEncodedData = (data) => {
143
+ const formPayload = new URLSearchParams();
144
+ const formKeys = Object.keys(data);
145
+ formKeys.forEach(key => {
146
+ if (data[key])
147
+ formPayload.append(key, data[key]);
148
+ });
149
+ return formPayload;
150
+ };
151
+
152
+ const ERROR_ELIGIBILITY_CODE = 13130;
153
+ const injectErrorInterceptors = (baseUrl, onUserEligibilityChange, onError) => {
154
+ injectResponseInterceptors(response => {
155
+ return response;
156
+ }, (error) => {
157
+ if (error.response) {
158
+ const { response } = error;
159
+ if (response?.status === 403 && response?.config.url.includes(baseUrl) && response?.config.withCredentials) {
160
+ if (response.data.errorCode === ERROR_ELIGIBILITY_CODE && onUserEligibilityChange) {
161
+ onUserEligibilityChange();
162
+ }
163
+ }
164
+ }
165
+ if (onError) {
166
+ onError(error);
167
+ }
168
+ throw error;
169
+ });
170
+ };
171
+
172
+ /*
173
+ * Copyright (c) 2022 AccelByte Inc. All Rights Reserved
174
+ * This is licensed software from AccelByte Inc, for limitations
175
+ * and restrictions contact your company contract manager.
176
+ */
177
+ class BrowserHelper {
178
+ }
179
+ BrowserHelper.isOnBrowser = () => {
180
+ return typeof window !== 'undefined' && window.document;
181
+ };
182
+
183
+ class RefreshSession {
184
+ }
185
+ // --
186
+ RefreshSession.KEY = 'RefreshSession.lock';
187
+ RefreshSession.isLocked = () => {
188
+ if (!BrowserHelper.isOnBrowser())
189
+ return false;
190
+ const lockStatus = localStorage.getItem(RefreshSession.KEY);
191
+ if (!lockStatus) {
192
+ return false;
193
+ }
194
+ const lockExpiry = Number(lockStatus);
195
+ if (isNaN(lockExpiry)) {
196
+ return false;
197
+ }
198
+ return lockExpiry > new Date().getTime();
199
+ };
200
+ RefreshSession.lock = (expiry) => {
201
+ if (!BrowserHelper.isOnBrowser())
202
+ return;
203
+ localStorage.setItem(RefreshSession.KEY, `${new Date().getTime() + expiry}`);
204
+ };
205
+ RefreshSession.unlock = () => {
206
+ if (!BrowserHelper.isOnBrowser())
207
+ return;
208
+ localStorage.removeItem(RefreshSession.KEY);
209
+ };
210
+ RefreshSession.sleepAsync = (timeInMs) => new Promise(resolve => setTimeout(resolve, timeInMs));
211
+ RefreshSession.isBearerAuth = (config) => {
212
+ // @ts-ignore
213
+ if (config?.headers?.Authorization?.toLowerCase().indexOf('bearer') > -1) {
214
+ return true;
215
+ }
216
+ return false;
217
+ };
218
+
219
+ /*
220
+ * Copyright (c) 2022 AccelByte Inc. All Rights Reserved
221
+ * This is licensed software from AccelByte Inc, for limitations
222
+ * and restrictions contact your company contract manager.
223
+ */
224
+ class CodeGenUtil {
225
+ /**
226
+ * Returns a hash code from a string
227
+ * @param {String} str The string to hash.
228
+ * @return {Number} A 32bit integer
229
+ * @see http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
230
+ */
231
+ static hashCode(str) {
232
+ let hash = 0;
233
+ for (let i = 0, len = str.length; i < len; i++) {
234
+ const chr = str.charCodeAt(i);
235
+ hash = (hash << 5) - hash + chr;
236
+ hash |= 0; // Convert to 32bit integer
237
+ }
238
+ return hash;
239
+ }
240
+ }
241
+ CodeGenUtil.getFormUrlEncodedData = (data) => {
242
+ const formPayload = new URLSearchParams();
243
+ const formKeys = Object.keys(data);
244
+ formKeys.forEach(key => {
245
+ if (typeof data[key] !== 'undefined')
246
+ formPayload.append(key, data[key]);
247
+ });
248
+ return formPayload;
249
+ };
250
+
251
+ /*
252
+ * Copyright (c) 2022 AccelByte Inc. All Rights Reserved
253
+ * This is licensed software from AccelByte Inc, for limitations
254
+ * and restrictions contact your company contract manager.
255
+ */
256
+ // TODO replace with Winston
257
+ class Logger {
258
+ static info(message, object = '') {
259
+ _log('info:', message, object);
260
+ }
261
+ static warn(message, object = '') {
262
+ _log('warn:', message, object);
263
+ }
264
+ static error(message, object = '') {
265
+ _log('error:', message, object);
266
+ }
267
+ }
268
+ const _log = (type, message = '', object = '') => {
269
+ if (type === 'error:') {
270
+ console.error('\x1b[31m%s\x1b[0m', type, message, object);
271
+ }
272
+ else {
273
+ console.log('\x1b[34m%s\x1b[0m', type, message, object); // blue
274
+ }
275
+ };
276
+
277
+ class Validate {
278
+ //
279
+ static responseType(networkCall, Codec) {
280
+ return wrapNetworkCallSafely(async () => {
281
+ const response = await networkCall();
282
+ const decodeResult = Codec.safeParse(response.data);
283
+ if (!decodeResult.success) {
284
+ throw new DecodeError(decodeResult.error, response);
285
+ }
286
+ return response;
287
+ });
288
+ }
289
+ static safeParse(data, Codec) {
290
+ const result = Codec.safeParse(data);
291
+ if (result.success) {
292
+ return result.data;
293
+ }
294
+ return null;
295
+ }
296
+ }
297
+ async function wrapNetworkCallSafely(networkCallFunction) {
298
+ try {
299
+ const response = await networkCallFunction();
300
+ // Cleanup so we avoid polluting the response
301
+ // @ts-ignore
302
+ delete response.headers; // perhaps this may be required?
303
+ // @ts-ignore
304
+ delete response.statusText;
305
+ // @ts-ignore
306
+ delete response.config; // axios specific
307
+ delete response.request;
308
+ return { response, error: null };
309
+ }
310
+ catch (error) {
311
+ return { response: null, error: error };
312
+ }
313
+ }
314
+ class DecodeError extends Error {
315
+ constructor(error, response) {
316
+ super(error.stack);
317
+ Logger.error(`url "${response.config.url}", data "${JSON.stringify(response.data, null, 2)}"`, error.stack || error.toString());
318
+ }
319
+ }
320
+
321
+ /*
322
+ * Copyright (c) 2022-2023 AccelByte Inc. All Rights Reserved
323
+ * This is licensed software from AccelByte Inc, for limitations
324
+ * and restrictions contact your company contract manager.
325
+ */
326
+ const NamespaceRole = zod.z.object({ namespace: zod.z.string(), roleId: zod.z.string() });
327
+ const PermissionV3 = zod.z.object({
328
+ action: zod.z.number().int(),
329
+ resource: zod.z.string(),
330
+ schedAction: zod.z.number().int().nullish(),
331
+ schedCron: zod.z.string().nullish(),
332
+ schedRange: zod.z.array(zod.z.string()).nullish()
333
+ });
334
+ const JwtBanV3 = zod.z.object({
335
+ ban: zod.z.string(),
336
+ disabledDate: zod.z.string().nullish(),
337
+ enabled: zod.z.boolean(),
338
+ endDate: zod.z.string(),
339
+ targetedNamespace: zod.z.string()
340
+ });
341
+ const TokenWithDeviceCookieResponseV3 = zod.z.object({
342
+ access_token: zod.z.string(),
343
+ auth_trust_id: zod.z.string().nullish(),
344
+ bans: zod.z.array(JwtBanV3).nullish(),
345
+ display_name: zod.z.string().nullish(),
346
+ expires_in: zod.z.number().int(),
347
+ is_comply: zod.z.boolean().nullish(),
348
+ jflgs: zod.z.number().int().nullish(),
349
+ namespace: zod.z.string(),
350
+ namespace_roles: zod.z.array(NamespaceRole).nullish(),
351
+ permissions: zod.z.array(PermissionV3),
352
+ platform_id: zod.z.string().nullish(),
353
+ platform_user_id: zod.z.string().nullish(),
354
+ refresh_expires_in: zod.z.number().int().nullish(),
355
+ refresh_token: zod.z.string().nullish(),
356
+ roles: zod.z.array(zod.z.string()).nullish(),
357
+ scope: zod.z.string(),
358
+ token_type: zod.z.string(),
359
+ user_id: zod.z.string().nullish(),
360
+ xuid: zod.z.string().nullish()
361
+ });
362
+ /*
363
+ * Copyright (c) 2022-2023 AccelByte Inc. All Rights Reserved
364
+ * This is licensed software from AccelByte Inc, for limitations
365
+ * and restrictions contact your company contract manager.
366
+ */
367
+ class OAuth20$ {
368
+ // @ts-ignore
369
+ constructor(axiosInstance, namespace, cache = false) {
370
+ this.axiosInstance = axiosInstance;
371
+ this.namespace = namespace;
372
+ this.cache = cache;
373
+ }
374
+ postOauthToken(data) {
375
+ const params = {};
376
+ const url = '/iam/v3/oauth/token';
377
+ const resultPromise = this.axiosInstance.post(url, CodeGenUtil.getFormUrlEncodedData(data), {
378
+ ...params,
379
+ headers: { ...params.headers, 'content-type': 'application/x-www-form-urlencoded' }
380
+ });
381
+ return Validate.responseType(() => resultPromise, TokenWithDeviceCookieResponseV3);
382
+ }
383
+ }
384
+
385
+ /*
386
+ * Copyright (c) 2022 AccelByte Inc. All Rights Reserved
387
+ * This is licensed software from AccelByte Inc, for limitations
388
+ * and restrictions contact your company contract manager.
389
+ */
390
+ class DesktopChecker {
391
+ static isDesktopApp() {
392
+ return DesktopChecker.desktopApp && !DesktopChecker.isInIframe();
393
+ }
394
+ static isInIframe() {
395
+ try {
396
+ return window.self !== window.top;
397
+ }
398
+ catch (error) {
399
+ return true;
400
+ }
401
+ }
402
+ // borrowed from https://github.com/cheton/is-electron
403
+ static isElectron() {
404
+ // @ts-ignore Renderer process
405
+ if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
406
+ return true;
407
+ }
408
+ // Main process
409
+ if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
410
+ return true;
411
+ }
412
+ // Detect the user agent when the `nodeIntegration` option is set to false
413
+ if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
414
+ return true;
415
+ }
416
+ return false;
417
+ }
418
+ }
419
+ DesktopChecker.desktopApp = DesktopChecker.isElectron();
420
+
421
+ /*
422
+ * Copyright (c) 2022-2023 AccelByte Inc. All Rights Reserved
423
+ * This is licensed software from AccelByte Inc, for limitations
424
+ * and restrictions contact your company contract manager.
425
+ */
426
+ const REFRESH_EXPIRY = 1000;
427
+ const REFRESH_EXPIRY_UPDATE_RATE = 500;
428
+ const REFRESH_EXPIRY_CHECK_RATE = 1000;
429
+ var LoginUrls;
430
+ (function (LoginUrls) {
431
+ LoginUrls["REFRESH_SESSION"] = "/iam/v3/oauth/token";
432
+ LoginUrls["LOGOUT"] = "/iam/v3/logout";
433
+ LoginUrls["REVOKE"] = "/iam/v3/oauth/revoke";
434
+ })(LoginUrls || (LoginUrls = {}));
435
+ /* eslint camelcase: 0 */
436
+ const refreshSession = ({ axiosConfig, refreshToken, clientId }) => {
437
+ const config = {
438
+ ...axiosConfig,
439
+ withCredentials: false,
440
+ headers: {
441
+ 'Content-Type': 'application/x-www-form-urlencoded',
442
+ Authorization: `Basic ${Buffer.from(`${clientId}:`).toString('base64')}`
443
+ }
444
+ };
445
+ const axios = Network.create(config);
446
+ const payload = {
447
+ refresh_token: refreshToken || undefined,
448
+ client_id: clientId,
449
+ grant_type: 'refresh_token'
450
+ };
451
+ const oauth20 = new OAuth20$(axios, 'NAMESPACE-NOT-REQUIRED', false);
452
+ return oauth20.postOauthToken(payload);
453
+ };
454
+ // Return Promise<true> if refresh in any tab is successful;
455
+ const refreshWithLock = ({ axiosConfig, refreshToken, clientId }) => {
456
+ //
457
+ if (RefreshSession.isLocked()) {
458
+ return Promise.resolve().then(async () => {
459
+ // This block is executed when other tab / request is refreshing
460
+ while (RefreshSession.isLocked()) {
461
+ await RefreshSession.sleepAsync(REFRESH_EXPIRY_CHECK_RATE);
462
+ }
463
+ return {};
464
+ });
465
+ }
466
+ RefreshSession.lock(REFRESH_EXPIRY);
467
+ let isLocallyRefreshingToken = true;
468
+ (async () => {
469
+ // eslint-disable-next-line no-unmodified-loop-condition
470
+ while (isLocallyRefreshingToken) {
471
+ RefreshSession.lock(REFRESH_EXPIRY);
472
+ await RefreshSession.sleepAsync(REFRESH_EXPIRY_UPDATE_RATE);
473
+ }
474
+ })();
475
+ return Promise.resolve()
476
+ .then(doRefreshSession({ axiosConfig, clientId, refreshToken }))
477
+ .finally(() => {
478
+ isLocallyRefreshingToken = false;
479
+ RefreshSession.unlock();
480
+ });
481
+ };
482
+ const doRefreshSession = ({ axiosConfig, clientId, refreshToken }) => async () => {
483
+ // we need this to check if app use “withCredentials: false” and don’t have refreshToken it should return false,
484
+ // because we track it as a logout user, if not do this even user logout on the desktop app (that use withCredentials: false)
485
+ // will automatically login with refreshSession
486
+ if (DesktopChecker.isDesktopApp() && !axiosConfig.withCredentials && !refreshToken) {
487
+ return false;
488
+ }
489
+ const result = await refreshSession({ axiosConfig, clientId, refreshToken });
490
+ if (result.error) {
491
+ return false;
492
+ }
493
+ return result.response.data;
494
+ };
495
+ const injectAuthInterceptors = (clientId, getSDKConfig, onSessionExpired, onGetUserSession, getRefreshToken) => {
496
+ // ===== request
497
+ injectRequestInterceptors(async (config) => {
498
+ // need to lock on the desktop as well to sleep other request before refresh session is done
499
+ const isRefreshTokenUrl = config.url === LoginUrls.REFRESH_SESSION;
500
+ // eslint-disable-next-line no-unmodified-loop-condition
501
+ while (RefreshSession.isLocked() && !isRefreshTokenUrl) {
502
+ await RefreshSession.sleepAsync(200);
503
+ }
504
+ return config;
505
+ }, (error) => {
506
+ return Promise.reject(error);
507
+ });
508
+ // ===== response
509
+ injectResponseInterceptors(response => {
510
+ return response;
511
+ }, async (error) => {
512
+ if (axios.isCancel(error)) {
513
+ // expected case, exit
514
+ throw error;
515
+ }
516
+ else {
517
+ const { config, response } = error;
518
+ if (!response) {
519
+ console.error('injectResponseInterceptors net::ERR_INTERNET_DISCONNECTED');
520
+ }
521
+ if (response?.status === 401) {
522
+ const { url } = config;
523
+ const axiosConfig = getSDKConfig();
524
+ const refreshToken = getRefreshToken ? getRefreshToken() : undefined;
525
+ // expected business case, exit
526
+ // @ts-ignore
527
+ if (Object.values(LoginUrls).includes(url)) {
528
+ throw error;
529
+ }
530
+ // need to lock on the desktop as well to prevent multiple token request
531
+ return refreshWithLock({ axiosConfig, clientId, refreshToken }).then(tokenResponse => {
532
+ return uponRefreshComplete(error, tokenResponse, onGetUserSession, onSessionExpired, axiosConfig, config);
533
+ });
534
+ }
535
+ }
536
+ return Promise.reject(error);
537
+ });
538
+ };
539
+ const uponRefreshComplete = (error, tokenResponse, onGetUserSession, onSessionExpired, axiosConfig, errorConfig) => {
540
+ //
541
+ if (tokenResponse) {
542
+ const { access_token, refresh_token } = tokenResponse;
543
+ if (onGetUserSession && access_token && refresh_token) {
544
+ onGetUserSession(access_token, refresh_token);
545
+ }
546
+ // desktop
547
+ if (!axiosConfig.withCredentials && access_token) {
548
+ return axios({
549
+ ...errorConfig,
550
+ headers: {
551
+ ...errorConfig.headers,
552
+ Authorization: `Bearer ${access_token}`
553
+ }
554
+ });
555
+ // web
556
+ }
557
+ else {
558
+ return axios(errorConfig);
559
+ }
560
+ }
561
+ if (onSessionExpired) {
562
+ console.log('session expired auth');
563
+ onSessionExpired();
564
+ }
565
+ throw error;
566
+ };
567
+
568
+ /*
569
+ * Copyright (c) 2022-2023 AccelByte Inc. All Rights Reserved
570
+ * This is licensed software from AccelByte Inc, for limitations
571
+ * and restrictions contact your company contract manager.
572
+ */
573
+ class ApiUtils {
574
+ }
575
+ ApiUtils.mergedConfigs = (config, overrides) => {
576
+ return {
577
+ ...config,
578
+ ...overrides?.config,
579
+ headers: {
580
+ ...config.headers,
581
+ ...overrides?.config?.headers
582
+ }
583
+ };
584
+ };
585
+
586
+ /*
587
+ * Copyright (c) 2022-2023 AccelByte Inc. All Rights Reserved
588
+ * This is licensed software from AccelByte Inc, for limitations
589
+ * and restrictions contact your company contract manager.
590
+ */
591
+ class AccelbyteSDKImpl {
592
+ constructor(options, config, events) {
593
+ this.getRefreshToken = () => this.refreshToken;
594
+ this.getConfig = () => this.config;
595
+ this.mergeConfigs = (configOverride) => {
596
+ this.config = ApiUtils.mergedConfigs(this.config, { config: configOverride });
597
+ };
598
+ this.refreshTokensImpl = (accessToken, refreshToken) => {
599
+ if (refreshToken) {
600
+ this.refreshToken = refreshToken;
601
+ }
602
+ this.mergeConfigs({ headers: { Authorization: accessToken ? `Bearer ${accessToken}` : '' } });
603
+ };
604
+ this.options = {
605
+ cache: false,
606
+ ...options
607
+ };
608
+ this.events = events;
609
+ this.config = {
610
+ timeout: 60000,
611
+ baseURL: options.baseURL,
612
+ withCredentials: true,
613
+ ...config,
614
+ headers: {
615
+ 'Content-Type': 'application/json',
616
+ ...config?.headers
617
+ }
618
+ };
619
+ }
620
+ init() {
621
+ const { baseURL, clientId } = this.options;
622
+ injectAuthInterceptors(clientId, this.getConfig, this.events?.onSessionExpired, this.events?.onGetUserSession, this.getRefreshToken);
623
+ injectErrorInterceptors(baseURL, this.events?.onUserEligibilityChange, this.events?.onError);
624
+ // TODO reintegrate doVersionDiagnostics later on
625
+ // setTimeout(() => this.doVersionDiagnostics(), TIMEOUT_TO_DIAGNOSTICS)
626
+ // @ts-ignore
627
+ // async function loadModule() {
628
+ // const module = (await import('./module2')) as { createModule: () => MyModule }
629
+ // const myModule = module.createModule()
630
+ // console.log('-- myModule', myModule)
631
+ // myModule.foo()
632
+ // }
633
+ return {
634
+ refreshTokens: (accessToken, refreshToken) => this.refreshTokensImpl(accessToken, refreshToken),
635
+ assembly: (cache = false) => {
636
+ return {
637
+ config: this.config,
638
+ namespace: this.options.namespace,
639
+ clientId: this.options.clientId,
640
+ redirectURI: this.options.redirectURI,
641
+ baseURL: this.options.baseURL,
642
+ cache
643
+ };
644
+ }
645
+ };
646
+ }
647
+ }
648
+ const sdkInit = ({ options, config, onEvents }) => {
649
+ const sdkFactory = new AccelbyteSDKImpl(options, config, onEvents);
650
+ return sdkFactory.init();
651
+ };
652
+ // ts-prune-ignore-next
653
+ const Accelbyte = { SDK: sdkInit };
654
+
655
+ /*
656
+ * Copyright (c) 2022 AccelByte Inc. All Rights Reserved
657
+ * This is licensed software from AccelByte Inc, for limitations
658
+ * and restrictions contact your company contract manager.
659
+ */
660
+ // ts-prune-ignore-next
661
+ const VALIDATION_ERROR_CODE = 20002;
662
+
663
+ /*
664
+ * Copyright (c) 2022-2023 AccelByte Inc. All Rights Reserved
665
+ * This is licensed software from AccelByte Inc, for limitations
666
+ * and restrictions contact your company contract manager.
667
+ */
668
+ const ERROR_LINK_ANOTHER_3RD_PARTY_ACCOUNT = 10200;
669
+ const ERROR_CODE_LINK_DELETION_ACCOUNT = 10135;
670
+ const ERROR_CODE_TOKEN_EXPIRED = 10196;
671
+ const ERROR_USER_BANNED = 10134;
672
+
673
+ /*
674
+ * Copyright (c) 2022 AccelByte Inc. All Rights Reserved
675
+ * This is licensed software from AccelByte Inc, for limitations
676
+ * and restrictions contact your company contract manager.
677
+ */
678
+ class UrlHelper {
679
+ static trimSlashFromStringEnd(pathString) {
680
+ let newString = pathString;
681
+ while (newString[newString.length - 1] === '/') {
682
+ newString = newString.slice(0, -1);
683
+ }
684
+ return newString;
685
+ }
686
+ static trimSlashFromStringStart(pathString) {
687
+ let newString = pathString;
688
+ while (newString[0] === '/') {
689
+ newString = newString.slice(1);
690
+ }
691
+ return newString;
692
+ }
693
+ static trimSlashFromStringEdges(pathString) {
694
+ return UrlHelper.trimSlashFromStringStart(this.trimSlashFromStringEnd(pathString));
695
+ }
696
+ static combinePaths(...paths) {
697
+ const completePath = paths.join('/');
698
+ // Replace 2 or more consecutive slashes with a single slash.
699
+ // This is also the behavior from Node's `path.join`.
700
+ return completePath.replace(/\/{2,}/g, '/');
701
+ }
702
+ static combineURLPaths(urlString, ...paths) {
703
+ const url = new URL(urlString);
704
+ const { origin } = url;
705
+ const pathname = UrlHelper.trimSlashFromStringEdges(UrlHelper.combinePaths(url.pathname, ...paths));
706
+ return new URL(pathname, origin).toString();
707
+ }
708
+ }
709
+ UrlHelper.isCompleteURLString = (urlString) => {
710
+ try {
711
+ const url = new URL(urlString);
712
+ return url.hostname !== '';
713
+ }
714
+ catch (error) { }
715
+ return false;
716
+ };
717
+
718
+ var _a;
719
+ class SdkCache {
720
+ static async withoutCache(apiCall) {
721
+ // Intercept the call and add onSync callback so that even though cache is false, the client cn still safely attach onSync
722
+ let apiResponse;
723
+ let apiError;
724
+ try {
725
+ apiResponse = await apiCall();
726
+ if (apiResponse.error)
727
+ throw apiResponse.error;
728
+ }
729
+ catch (e) {
730
+ apiError = e;
731
+ }
732
+ return new Promise((resolve, reject) => {
733
+ // const result = apiCall()
734
+ // resolve && resolve(result.then())
735
+ // reject && reject(result.catch())
736
+ const success = {
737
+ response: apiResponse?.response,
738
+ error: apiError,
739
+ onSync: _syncedData => {
740
+ // Add empty implementation so client onSync call does not fail with undefined 'onSync' function
741
+ }
742
+ };
743
+ resolve && resolve(success);
744
+ reject && reject(apiError || apiResponse.error);
745
+ });
746
+ }
747
+ static async withCache(key, apiCall) {
748
+ let syncObserver;
749
+ const cacheData = this.cacheStrategy.get(key);
750
+ if (!cacheData) {
751
+ const res = await apiCall();
752
+ const data = res.response?.data;
753
+ const error = res.error;
754
+ if (error) {
755
+ return {
756
+ response: res.response,
757
+ error,
758
+ onSync: func => (syncObserver = func)
759
+ };
760
+ }
761
+ const skipNon200FromCaching = res.response?.status !== 200;
762
+ if (skipNon200FromCaching) {
763
+ return {
764
+ response: res.response,
765
+ error: null,
766
+ onSync: func => (syncObserver = func)
767
+ };
768
+ }
769
+ if (data) {
770
+ this.cacheStrategy.set(key, data);
771
+ }
772
+ return {
773
+ response: { data, status: 200 },
774
+ error,
775
+ onSync: func => (syncObserver = func)
776
+ };
777
+ }
778
+ const onSyncCall = async () => {
779
+ const onSyncResponse = await apiCall();
780
+ if (onSyncResponse.error) {
781
+ const res = {
782
+ response: null,
783
+ error: onSyncResponse.error
784
+ };
785
+ syncObserver && syncObserver(res);
786
+ return res;
787
+ }
788
+ const newData = onSyncResponse.response?.data;
789
+ if (!isEqual(this.cacheStrategy.get(key), newData)) {
790
+ this.cacheStrategy.set(key, newData);
791
+ const res = {
792
+ response: { data: newData, status: 200 },
793
+ error: null
794
+ };
795
+ syncObserver && syncObserver(res);
796
+ return res;
797
+ }
798
+ const res = {
799
+ response: null,
800
+ error: null
801
+ };
802
+ return res;
803
+ };
804
+ Promise.resolve().then(onSyncCall);
805
+ return {
806
+ response: { data: cacheData, status: 200 },
807
+ error: null,
808
+ onSync: observer => (syncObserver = observer)
809
+ };
810
+ }
811
+ }
812
+ _a = SdkCache;
813
+ // this could be localStorage or indexeddb
814
+ SdkCache.cacheStrategy = new Map();
815
+ SdkCache.clearCache = () => {
816
+ _a.cacheStrategy.clear();
817
+ };
818
+
819
+ exports.Accelbyte = Accelbyte;
820
+ exports.ApiUtils = ApiUtils;
821
+ exports.BrowserHelper = BrowserHelper;
822
+ exports.CodeGenUtil = CodeGenUtil;
823
+ exports.DecodeError = DecodeError;
824
+ exports.DesktopChecker = DesktopChecker;
825
+ exports.ERROR_CODE_LINK_DELETION_ACCOUNT = ERROR_CODE_LINK_DELETION_ACCOUNT;
826
+ exports.ERROR_CODE_TOKEN_EXPIRED = ERROR_CODE_TOKEN_EXPIRED;
827
+ exports.ERROR_LINK_ANOTHER_3RD_PARTY_ACCOUNT = ERROR_LINK_ANOTHER_3RD_PARTY_ACCOUNT;
828
+ exports.ERROR_USER_BANNED = ERROR_USER_BANNED;
829
+ exports.Network = Network;
830
+ exports.RefreshSession = RefreshSession;
831
+ exports.SdkCache = SdkCache;
832
+ exports.SdkDevice = SdkDevice;
833
+ exports.UrlHelper = UrlHelper;
834
+ exports.VALIDATION_ERROR_CODE = VALIDATION_ERROR_CODE;
835
+ exports.Validate = Validate;
836
+ exports.doRefreshSession = doRefreshSession;
837
+ exports.injectAuthInterceptors = injectAuthInterceptors;
838
+ exports.injectErrorInterceptors = injectErrorInterceptors;
839
+ exports.injectRequestInterceptors = injectRequestInterceptors;
840
+ exports.injectResponseInterceptors = injectResponseInterceptors;
841
+ exports.refreshWithLock = refreshWithLock;
842
+ //# sourceMappingURL=index.node.js.map