@accelbyte/sdk 1.1.3 → 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.
- package/dist/cjs/node/index.node.js +842 -0
- package/dist/cjs/node/index.node.js.map +1 -0
- package/dist/es/browser/index.browser.js +518 -18597
- package/dist/es/browser/index.browser.js.map +1 -1
- package/dist/es/node/index.node.js +518 -18598
- package/dist/es/node/index.node.js.map +1 -1
- package/dist/index.d.ts +260 -73566
- package/examples/next/README.md +40 -7
- package/examples/next/package.json +3 -5
- package/examples/next/pages/index.tsx +41 -26
- package/examples/next/src/sdk.ts +32 -0
- package/examples/next/yarn.lock +271 -211
- package/examples/node/README.md +29 -0
- package/examples/node/package.json +2 -4
- package/examples/node/yarn.lock +156 -551
- package/examples/vite/README.md +40 -7
- package/examples/vite/package.json +2 -4
- package/examples/vite/src/App.tsx +13 -10
- package/examples/vite/src/Sdk.ts +30 -15
- package/examples/vite/yarn.lock +4 -4
- package/package.json +3 -3
- package/dist/cjs/node/index.cjs +0 -19820
- package/dist/cjs/node/index.cjs.map +0 -1
- package/examples/next/pages/api/hello.ts +0 -10
|
@@ -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
|