@jolibox/implement 1.1.4-beta.9 → 1.1.5
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/.rush/temp/package-deps_build.json +33 -33
- package/dist/{h5 → common}/ads/ads-action-detection.d.ts +3 -1
- package/dist/{h5 → common}/ads/anti-cheating.d.ts +2 -2
- package/dist/common/ads/channel-policy.d.ts +26 -0
- package/dist/{h5 → common}/ads/index.d.ts +12 -3
- package/dist/common/context/index.d.ts +2 -0
- package/dist/common/http/uuid.d.ts +2 -2
- package/dist/common/http/xua.d.ts +0 -15
- package/dist/common/report/base-tracker.d.ts +10 -1
- package/dist/common/report/errors/index.d.ts +2 -2
- package/dist/common/report/task-track/index.d.ts +3 -0
- package/dist/common/report/types.d.ts +2 -55
- package/dist/h5/api/index.d.ts +1 -0
- package/dist/h5/http/index.d.ts +3 -3
- package/dist/h5/report/index.d.ts +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +3 -5033
- package/dist/index.native.js +3 -3750
- package/dist/native/api/keyboard.d.ts +9 -3
- package/dist/native/network/index.d.ts +0 -3
- package/esbuild.config.js +1 -1
- package/implement.build.log +2 -2
- package/package.json +3 -3
- package/src/{h5 → common}/ads/ads-action-detection.ts +4 -4
- package/src/{h5 → common}/ads/anti-cheating.ts +2 -4
- package/src/common/ads/channel-policy.ts +52 -0
- package/src/{h5 → common}/ads/index.ts +39 -45
- package/src/common/api-factory/index.ts +3 -3
- package/src/common/context/index.ts +20 -11
- package/src/common/http/uuid.ts +2 -10
- package/src/common/http/xua.ts +2 -69
- package/src/common/report/base-tracker.ts +40 -44
- package/src/common/report/errors/index.ts +3 -3
- package/src/common/report/errors/report/listeners.ts +0 -2
- package/src/common/report/task-track/index.ts +63 -14
- package/src/common/report/track.ts +2 -2
- package/src/common/report/types.ts +2 -64
- package/src/h5/api/ads.ts +24 -0
- package/src/h5/api/get-system-info.ts +5 -1
- package/src/h5/api/index.ts +1 -0
- package/src/h5/http/index.ts +24 -28
- package/src/h5/http/utils/__tests__/xua.test.ts +1 -1
- package/src/h5/http/utils/session.ts +1 -1
- package/src/h5/report/errors/index.ts +1 -1
- package/src/h5/report/event-tracker.ts +1 -1
- package/src/h5/report/index.ts +5 -4
- package/src/index.ts +0 -1
- package/src/native/api/ads.ts +32 -8
- package/src/native/api/get-system-info.ts +5 -2
- package/src/native/api/keyboard.ts +13 -13
- package/src/native/bootstrap/index.ts +0 -1
- package/src/native/network/index.ts +11 -4
- package/src/native/report/index.ts +4 -4
- package/src/native/types/native-method-map.d.ts +11 -0
- package/dist/common/report/errors/error-types.d.ts +0 -122
- package/src/common/report/errors/error-types.ts +0 -206
- package/src/h5/http/utils/__tests__/uuid.test.ts +0 -16
- /package/dist/h5/{http/utils/__tests__/uuid.test.d.ts → api/ads.d.ts} +0 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
export declare const showKeyboard: (
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export declare const showKeyboard: (params: {
|
|
2
|
+
defaultValue?: string | undefined;
|
|
3
|
+
maxLength?: unknown;
|
|
4
|
+
multiple?: boolean | undefined;
|
|
5
|
+
}) => import("@jolibox/types").StandardResponse<void>;
|
|
6
|
+
export declare const updateKeyboard: (params: {
|
|
7
|
+
value: string;
|
|
8
|
+
}) => import("@jolibox/types").StandardResponse<void>;
|
|
9
|
+
export declare const hideKeyboard: () => import("@jolibox/types").StandardResponse<void>;
|
package/esbuild.config.js
CHANGED
package/implement.build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Invoking: npm run clean && npm run build:esm && tsc
|
|
2
2
|
|
|
3
|
-
> @jolibox/implement@1.1.
|
|
3
|
+
> @jolibox/implement@1.1.5 clean
|
|
4
4
|
> rimraf ./dist
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
> @jolibox/implement@1.1.
|
|
7
|
+
> @jolibox/implement@1.1.5 build:esm
|
|
8
8
|
> BUILD_VERSION=$(node -p "require('./package.json').version") node esbuild.config.js --format=esm
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jolibox/implement",
|
|
3
3
|
"description": "This project is Jolibox JS-SDk implement for Native && H5",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.5",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@jolibox/common": "1.1.
|
|
10
|
-
"@jolibox/types": "1.1.
|
|
9
|
+
"@jolibox/common": "1.1.5",
|
|
10
|
+
"@jolibox/types": "1.1.5",
|
|
11
11
|
"localforage": "1.10.0"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Track } from '../report';
|
|
2
2
|
|
|
3
3
|
export class AdsActionDetection {
|
|
4
4
|
adBreakIsShowing = false;
|
|
5
5
|
|
|
6
6
|
private reportPageJumpOut = () => {
|
|
7
|
-
track('AdBreakJumpOut', {
|
|
7
|
+
this.track('AdBreakJumpOut', {
|
|
8
8
|
context: 'AdsActionDetection'
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
private reportPageHide = () => {
|
|
13
|
-
track('AdBreakHide', {
|
|
13
|
+
this.track('AdBreakHide', {
|
|
14
14
|
context: 'AdsActionDetection'
|
|
15
15
|
});
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
constructor() {
|
|
18
|
+
constructor(readonly track: Track) {
|
|
19
19
|
window.addEventListener('visibilitychange', () => {
|
|
20
20
|
if (document.hidden && this.adBreakIsShowing) {
|
|
21
21
|
this.reportPageHide();
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { tracker } from '../report/event-tracker';
|
|
2
|
-
|
|
3
1
|
export type AdsDisplayPermission =
|
|
4
2
|
| 'BLOCK_INITIAL'
|
|
5
3
|
| 'BANNED_FOR_SESSION'
|
|
@@ -41,10 +39,10 @@ export class AdsAntiCheating {
|
|
|
41
39
|
|
|
42
40
|
private initTimestamp = 0;
|
|
43
41
|
// private context: JoliboxSDKPipeExecutor;
|
|
44
|
-
private report = tracker;
|
|
45
42
|
|
|
46
43
|
constructor(
|
|
47
44
|
// context: JoliboxSDKPipeExecutor,
|
|
45
|
+
readonly checkNetwork: () => boolean,
|
|
48
46
|
config?: IAdsAntiCheatingConfig
|
|
49
47
|
) {
|
|
50
48
|
this.maxAllowedAdsForTime = config?.maxAllowedAdsForTime ?? 8;
|
|
@@ -220,7 +218,7 @@ export class AdsAntiCheating {
|
|
|
220
218
|
};
|
|
221
219
|
|
|
222
220
|
public checkAdsDisplayPermission: (type: 'reward' | 'interstitial') => AdsDisplayPermission = (type) => {
|
|
223
|
-
if (!this.
|
|
221
|
+
if (!this.checkNetwork()) {
|
|
224
222
|
return 'NETWORK_NOT_OK';
|
|
225
223
|
}
|
|
226
224
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { context } from '../context';
|
|
2
|
+
|
|
3
|
+
export interface IChannelPolicy {
|
|
4
|
+
/**
|
|
5
|
+
* The rate at which interstitial ads should be shown.
|
|
6
|
+
* A value of 1 means always bypass interstitials call, while a value of 0 means always forbid calling interstitials.
|
|
7
|
+
* A value between 0 and 1 means the interstitial call will be bypassed with a probability of `rate`.
|
|
8
|
+
*/
|
|
9
|
+
interstitialRate?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface HttpClient {
|
|
13
|
+
get<T>(url: string, options?: { query?: Record<string, string> }): Promise<T>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class ChannelPolicy {
|
|
17
|
+
private configs: Record<string, IChannelPolicy> | null = null;
|
|
18
|
+
private firstRun = true;
|
|
19
|
+
|
|
20
|
+
constructor(readonly httpClient: HttpClient) {
|
|
21
|
+
this.init();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async init() {
|
|
25
|
+
try {
|
|
26
|
+
this.configs = await this.httpClient.get<Record<string, IChannelPolicy>>(
|
|
27
|
+
'/api/fe-configs/js-sdk/ads-channel-config'
|
|
28
|
+
);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
// no-op
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Should bypass calling interstitial based on the channel policy.
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
shouldBypassCallingInterstitial() {
|
|
39
|
+
let result;
|
|
40
|
+
if (!this.configs) {
|
|
41
|
+
this.init();
|
|
42
|
+
result = !this.firstRun; // If configs are not loaded yet, should forbid calling interstitial on first run.
|
|
43
|
+
} else {
|
|
44
|
+
const channel = context.channel ?? '';
|
|
45
|
+
const policy: IChannelPolicy | undefined = this.configs[channel];
|
|
46
|
+
const rate = policy?.interstitialRate ?? 1;
|
|
47
|
+
result = Math.random() < rate; // Bypass interstitial calling with the given rate.
|
|
48
|
+
}
|
|
49
|
+
this.firstRun = false;
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { createCommands } from '@jolibox/common';
|
|
2
1
|
import { AdsActionDetection } from './ads-action-detection';
|
|
3
2
|
import { AdsAntiCheating } from './anti-cheating';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const commands = createCommands();
|
|
3
|
+
import { Track } from '../report';
|
|
4
|
+
import { context } from '../context';
|
|
5
|
+
import { ChannelPolicy } from './channel-policy';
|
|
8
6
|
|
|
9
7
|
declare global {
|
|
10
8
|
interface Window {
|
|
@@ -255,6 +253,10 @@ interface IJoliboxAdsResponse {
|
|
|
255
253
|
};
|
|
256
254
|
}
|
|
257
255
|
|
|
256
|
+
interface HttpClient {
|
|
257
|
+
get<T>(url: string, options?: { query?: Record<string, string> }): Promise<T>;
|
|
258
|
+
}
|
|
259
|
+
|
|
258
260
|
/**
|
|
259
261
|
* Jolibox Ads SDK
|
|
260
262
|
*/
|
|
@@ -264,18 +266,18 @@ export class JoliboxAdsImpl {
|
|
|
264
266
|
public clientId?: string;
|
|
265
267
|
public channelId?: string;
|
|
266
268
|
public unitId?: string;
|
|
267
|
-
|
|
268
|
-
// private context: JoliboxSDKPipeExecutor;
|
|
269
|
+
|
|
269
270
|
private antiCheating: AdsAntiCheating;
|
|
270
271
|
private adsActionDetection: AdsActionDetection;
|
|
272
|
+
private channelPolicy: ChannelPolicy;
|
|
271
273
|
|
|
272
274
|
/**
|
|
273
275
|
* Internal constructor, should not be called directly
|
|
274
276
|
*/
|
|
275
|
-
constructor(
|
|
276
|
-
|
|
277
|
-
this.
|
|
278
|
-
this.
|
|
277
|
+
constructor(readonly track: Track, readonly httpClient: HttpClient, readonly checkNetwork: () => boolean) {
|
|
278
|
+
this.antiCheating = new AdsAntiCheating(checkNetwork);
|
|
279
|
+
this.adsActionDetection = new AdsActionDetection(this.track);
|
|
280
|
+
this.channelPolicy = new ChannelPolicy(httpClient);
|
|
279
281
|
}
|
|
280
282
|
|
|
281
283
|
/**
|
|
@@ -284,7 +286,7 @@ export class JoliboxAdsImpl {
|
|
|
284
286
|
* @returns
|
|
285
287
|
*/
|
|
286
288
|
public init(config?: IAdsInitParams) {
|
|
287
|
-
track('CallAdsInit', null);
|
|
289
|
+
this.track('CallAdsInit', null);
|
|
288
290
|
if (typeof window === 'undefined') {
|
|
289
291
|
// skip if not in browser
|
|
290
292
|
return;
|
|
@@ -296,13 +298,11 @@ export class JoliboxAdsImpl {
|
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
private getGameId = () => {
|
|
299
|
-
|
|
300
|
-
return urlParams.get('gameId') ?? this.config.gameId;
|
|
301
|
+
return context.mpId;
|
|
301
302
|
};
|
|
302
303
|
|
|
303
304
|
private getTestAdsMode = () => {
|
|
304
|
-
|
|
305
|
-
return urlParams.get('testAdsMode') === 'true';
|
|
305
|
+
return context.testAdsMode;
|
|
306
306
|
};
|
|
307
307
|
|
|
308
308
|
private asyncLoad = async () => {
|
|
@@ -358,7 +358,7 @@ export class JoliboxAdsImpl {
|
|
|
358
358
|
script.setAttribute('data-ad-channel', this.channelId);
|
|
359
359
|
}
|
|
360
360
|
document.head.appendChild(script);
|
|
361
|
-
track('LoadAdsenseCompleted', null);
|
|
361
|
+
this.track('LoadAdsenseCompleted', null);
|
|
362
362
|
}
|
|
363
363
|
};
|
|
364
364
|
|
|
@@ -376,7 +376,7 @@ export class JoliboxAdsImpl {
|
|
|
376
376
|
*/
|
|
377
377
|
public adConfig = (params: IAdConfigParams) => {
|
|
378
378
|
const { onReady, ...paramsToTrack } = params;
|
|
379
|
-
track('CallAdConfig', paramsToTrack);
|
|
379
|
+
this.track('CallAdConfig', paramsToTrack);
|
|
380
380
|
if (!this.configured) {
|
|
381
381
|
this.configured = true;
|
|
382
382
|
this.push(params);
|
|
@@ -417,6 +417,20 @@ export class JoliboxAdsImpl {
|
|
|
417
417
|
console.warn('Game ID is not set, skip calling adBreak');
|
|
418
418
|
return;
|
|
419
419
|
}
|
|
420
|
+
|
|
421
|
+
if (params.type !== 'reward') {
|
|
422
|
+
const shouldBypassCallingInterstitial = this.channelPolicy.shouldBypassCallingInterstitial();
|
|
423
|
+
if (!shouldBypassCallingInterstitial) {
|
|
424
|
+
params.adBreakDone?.({
|
|
425
|
+
breakType: params.type,
|
|
426
|
+
breakName: 'skipInterstitials',
|
|
427
|
+
breakFormat: 'interstitial',
|
|
428
|
+
breakStatus: 'viewed'
|
|
429
|
+
});
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
420
434
|
const adsDisplayPermission = this.antiCheating.checkAdsDisplayPermission(
|
|
421
435
|
params.type === 'reward' ? 'reward' : 'interstitial'
|
|
422
436
|
);
|
|
@@ -431,7 +445,7 @@ export class JoliboxAdsImpl {
|
|
|
431
445
|
breakFormat: params.type === 'reward' ? 'reward' : 'interstitial',
|
|
432
446
|
breakStatus: 'noAdPreloaded'
|
|
433
447
|
});
|
|
434
|
-
track('PreventAdsCheating', {
|
|
448
|
+
this.track('PreventAdsCheating', {
|
|
435
449
|
reason: adsDisplayPermission
|
|
436
450
|
});
|
|
437
451
|
return;
|
|
@@ -444,7 +458,7 @@ export class JoliboxAdsImpl {
|
|
|
444
458
|
breakFormat: params.type === 'reward' ? 'reward' : 'interstitial',
|
|
445
459
|
breakStatus: 'viewed' // TODO: need to confirm
|
|
446
460
|
});
|
|
447
|
-
track('PreventAdsCheating', {
|
|
461
|
+
this.track('PreventAdsCheating', {
|
|
448
462
|
reason: adsDisplayPermission
|
|
449
463
|
});
|
|
450
464
|
return;
|
|
@@ -459,7 +473,7 @@ export class JoliboxAdsImpl {
|
|
|
459
473
|
const originAdBreakDone = params.adBreakDone;
|
|
460
474
|
const adBreakDone = (placementInfo: IPlacementInfo) => {
|
|
461
475
|
this.adsActionDetection.adBreakIsShowing = false;
|
|
462
|
-
track('CallAdBreakDone', {
|
|
476
|
+
this.track('CallAdBreakDone', {
|
|
463
477
|
breakType: placementInfo.breakType,
|
|
464
478
|
breakName: placementInfo.breakName ?? '',
|
|
465
479
|
breakFormat: placementInfo.breakFormat,
|
|
@@ -476,7 +490,7 @@ export class JoliboxAdsImpl {
|
|
|
476
490
|
if (params.type === 'reward') {
|
|
477
491
|
const originBeforeReward = params.beforeReward;
|
|
478
492
|
const wrapShowAdFn = (originShowAdFn: () => void) => () => {
|
|
479
|
-
track('CallShowAdFn', null);
|
|
493
|
+
this.track('CallShowAdFn', null);
|
|
480
494
|
originShowAdFn();
|
|
481
495
|
};
|
|
482
496
|
const beforeReward = (originShowAdFn: () => void) => {
|
|
@@ -502,7 +516,7 @@ export class JoliboxAdsImpl {
|
|
|
502
516
|
break;
|
|
503
517
|
}
|
|
504
518
|
this.adsActionDetection.adBreakIsShowing = true;
|
|
505
|
-
track('CallAdBreak', paramsToTrack);
|
|
519
|
+
this.track('CallAdBreak', paramsToTrack);
|
|
506
520
|
this.push(params);
|
|
507
521
|
|
|
508
522
|
// https://rsgce94md216.sg.larksuite.com/wiki/JGrAwRk7BiRJyekzJmKlbOawg7e?from=from_copylink
|
|
@@ -524,7 +538,7 @@ export class JoliboxAdsImpl {
|
|
|
524
538
|
* @returns
|
|
525
539
|
*/
|
|
526
540
|
public adUnit = async (params: IAdUnitParams) => {
|
|
527
|
-
track('CallAdUnit', {
|
|
541
|
+
this.track('CallAdUnit', {
|
|
528
542
|
adFormat: params.adFormat?.toString() ?? null,
|
|
529
543
|
fullWidthResponsive: params.fullWidthResponsive ?? null
|
|
530
544
|
});
|
|
@@ -621,7 +635,7 @@ export class JoliboxAdsImpl {
|
|
|
621
635
|
mutations.forEach((mutation) => {
|
|
622
636
|
if (mutation.type === 'attributes' && mutation.attributeName === 'data-ad-status') {
|
|
623
637
|
const status = ins.getAttribute('data-ad-status');
|
|
624
|
-
track('AdSenseUnitStatusChanged', {
|
|
638
|
+
this.track('AdSenseUnitStatusChanged', {
|
|
625
639
|
status: status ?? 'null'
|
|
626
640
|
});
|
|
627
641
|
}
|
|
@@ -636,23 +650,3 @@ export class JoliboxAdsImpl {
|
|
|
636
650
|
}
|
|
637
651
|
};
|
|
638
652
|
}
|
|
639
|
-
|
|
640
|
-
export default JoliboxAdsImpl;
|
|
641
|
-
|
|
642
|
-
const ads = new JoliboxAdsImpl();
|
|
643
|
-
|
|
644
|
-
commands.registerCommand('AdsSDK.init', (config) => {
|
|
645
|
-
ads.init(config);
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
commands.registerCommand('AdsSDK.adConfig', (params) => {
|
|
649
|
-
ads.adConfig(params);
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
commands.registerCommand('AdsSDK.adBreak', (params) => {
|
|
653
|
-
ads.adBreak(params);
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
commands.registerCommand('AdsSDK.adUnit', (params) => {
|
|
657
|
-
ads.adUnit(params);
|
|
658
|
-
});
|
|
@@ -4,7 +4,7 @@ import { ResponseType, StandardResponse } from '@jolibox/types';
|
|
|
4
4
|
import { Track } from '../report';
|
|
5
5
|
import { reportError } from '../report/errors/report';
|
|
6
6
|
import { Type, typeAsserts, Validator } from './validator';
|
|
7
|
-
import {
|
|
7
|
+
import { createUserValidateAPIError } from '../report/errors';
|
|
8
8
|
|
|
9
9
|
let traceId = 1;
|
|
10
10
|
|
|
@@ -65,7 +65,7 @@ export const createAPIFactory = (track: Track) => {
|
|
|
65
65
|
typeAsserts(config.paramsSchema, inputs, 'params');
|
|
66
66
|
} catch (err: unknown) {
|
|
67
67
|
const error = err as { message: string };
|
|
68
|
-
throw
|
|
68
|
+
throw createUserValidateAPIError(`${method}:fail ${error.message}`);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
const res = config.implement(...inputs);
|
|
@@ -142,7 +142,7 @@ export const createAPIFactory = (track: Track) => {
|
|
|
142
142
|
try {
|
|
143
143
|
typeAsserts(config.paramsSchema, inputs, 'params');
|
|
144
144
|
} catch (error) {
|
|
145
|
-
throw
|
|
145
|
+
throw createUserValidateAPIError((error as Error).message);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -2,7 +2,8 @@ import { mergeArray, mergeWith } from '@jolibox/common';
|
|
|
2
2
|
import { DeviceInfo, HostInfo, HostUserInfo, SdkInfo } from './types';
|
|
3
3
|
import { Env } from '@jolibox/types';
|
|
4
4
|
import { parseUrlQuery } from './url-parse';
|
|
5
|
-
import { getAppVersion
|
|
5
|
+
import { getAppVersion } from '../http/xua';
|
|
6
|
+
import { getDeviceId } from '@jolibox/common';
|
|
6
7
|
|
|
7
8
|
declare const globalThis: {
|
|
8
9
|
joliboxJSCore?: {
|
|
@@ -24,7 +25,7 @@ const defaultEnv: Env = {
|
|
|
24
25
|
nativeSDKVersion: '',
|
|
25
26
|
jssdkVersion: getAppVersion()
|
|
26
27
|
},
|
|
27
|
-
schema:
|
|
28
|
+
schema: window.location.href,
|
|
28
29
|
platform: 'h5'
|
|
29
30
|
};
|
|
30
31
|
|
|
@@ -33,14 +34,16 @@ const nativeEnv = globalThis.joliboxJSCore?.env;
|
|
|
33
34
|
const env = Object.assign({}, nativeEnv?.() ?? defaultEnv);
|
|
34
35
|
|
|
35
36
|
const wrapContext = () => {
|
|
36
|
-
const { payloadJson, headerJson } = parseUrlQuery(env.schema);
|
|
37
|
+
const { payloadJson, headerJson } = env.schema.length ? parseUrlQuery(env.schema) : {};
|
|
37
38
|
const defaultSessionId = `${env.deviceInfo.did}-${new Date().getTime()}`;
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
39
|
+
const url = new URL(env.schema.length ? env.schema : window.location.href);
|
|
40
|
+
const urlParams = url.searchParams;
|
|
41
|
+
const defaultGameID = urlParams.get('mpId') ?? urlParams.get('appId') ?? urlParams.get('gameId') ?? '';
|
|
42
|
+
const sessionId = payloadJson?.sessionId ?? urlParams.get('sessionId') ?? defaultSessionId;
|
|
43
|
+
const testAdsMode = !!(payloadJson?.testAdsMode ?? urlParams.get('testAdsMode') === 'true');
|
|
44
|
+
const joliboxEnv = payloadJson?.joliboxEnv ?? urlParams.get('joliboxEnv') ?? 'production';
|
|
43
45
|
const testMode = joliboxEnv === 'staging';
|
|
46
|
+
const channel = headerJson?.channel;
|
|
44
47
|
|
|
45
48
|
return {
|
|
46
49
|
get testMode(): boolean {
|
|
@@ -52,14 +55,17 @@ const wrapContext = () => {
|
|
|
52
55
|
get joliboxEnv() {
|
|
53
56
|
return joliboxEnv;
|
|
54
57
|
},
|
|
58
|
+
get joliSource(): string | null {
|
|
59
|
+
return urlParams.get('joliSource') ?? null;
|
|
60
|
+
},
|
|
55
61
|
get mpId(): string {
|
|
56
|
-
return defaultGameID ?? payloadJson
|
|
62
|
+
return defaultGameID ?? payloadJson?.id ?? '';
|
|
57
63
|
},
|
|
58
64
|
get mpVersion(): string {
|
|
59
|
-
return headerJson
|
|
65
|
+
return headerJson?.ver ?? getAppVersion();
|
|
60
66
|
},
|
|
61
67
|
get platform(): Env['platform'] {
|
|
62
|
-
return env.platform;
|
|
68
|
+
return env.platform ?? env.deviceInfo.platform;
|
|
63
69
|
},
|
|
64
70
|
get deviceInfo(): DeviceInfo {
|
|
65
71
|
return env.deviceInfo;
|
|
@@ -76,6 +82,9 @@ const wrapContext = () => {
|
|
|
76
82
|
get sessionId(): string {
|
|
77
83
|
return env.clientSessionId ?? sessionId ?? defaultSessionId;
|
|
78
84
|
},
|
|
85
|
+
get channel(): string | undefined {
|
|
86
|
+
return channel;
|
|
87
|
+
},
|
|
79
88
|
onEnvConfigChanged: (newConfig: Partial<Env>) => {
|
|
80
89
|
mergeWith(env, newConfig, mergeArray);
|
|
81
90
|
}
|
package/src/common/http/uuid.ts
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
3
|
-
const r = (Math.random() * 16) | 0;
|
|
4
|
-
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
5
|
-
return v.toString(16);
|
|
6
|
-
});
|
|
7
|
-
};
|
|
1
|
+
import { uuidv4, isValidUUIDV4 } from '@jolibox/common';
|
|
8
2
|
|
|
9
|
-
export
|
|
10
|
-
return /^[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/i.test(uuid);
|
|
11
|
-
};
|
|
3
|
+
export { uuidv4, isValidUUIDV4 };
|
package/src/common/http/xua.ts
CHANGED
|
@@ -1,34 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export const platform = {
|
|
4
|
-
isiOS:
|
|
5
|
-
navigator.userAgent.includes('iPhone') ||
|
|
6
|
-
navigator.userAgent.includes('iPod') ||
|
|
7
|
-
navigator.userAgent.includes('iPad') ||
|
|
8
|
-
navigator.userAgent.includes('iPhone OS'),
|
|
9
|
-
iosVersion: () => {
|
|
10
|
-
const v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/) as string[];
|
|
11
|
-
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || '0', 10)];
|
|
12
|
-
},
|
|
13
|
-
isAndroid: navigator.userAgent.includes('Android'),
|
|
14
|
-
isMac: navigator.userAgent.includes('Mac'),
|
|
15
|
-
isFacebook: navigator.userAgent.includes('FB_IAB'),
|
|
16
|
-
isPC: !navigator.userAgent.includes('iPhone') && !navigator.userAgent.includes('Android')
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const getPlatform = () => {
|
|
20
|
-
if (platform.isiOS) {
|
|
21
|
-
return 'iOS';
|
|
22
|
-
} else if (platform.isAndroid) {
|
|
23
|
-
return 'Android';
|
|
24
|
-
} else if (platform.isMac) {
|
|
25
|
-
return 'Mac';
|
|
26
|
-
} else if (platform.isFacebook) {
|
|
27
|
-
return 'Facebook';
|
|
28
|
-
} else {
|
|
29
|
-
return 'PC';
|
|
30
|
-
}
|
|
31
|
-
};
|
|
1
|
+
import { xUserAgent as xua } from '@jolibox/common';
|
|
32
2
|
|
|
33
3
|
const jssdkVersion = '__JOLIBOX_LOCAL_SDK_VERSION__';
|
|
34
4
|
|
|
@@ -36,44 +6,7 @@ export const getAppVersion = () => {
|
|
|
36
6
|
return jssdkVersion;
|
|
37
7
|
};
|
|
38
8
|
|
|
39
|
-
export const DEVICE_ID = 'device_id';
|
|
40
|
-
export const ADVERTISING_ID = 'advertising_id';
|
|
41
|
-
|
|
42
|
-
export const getStorage = (cookieKey: string) => {
|
|
43
|
-
if (!localStorage.getItem(cookieKey)) {
|
|
44
|
-
localStorage.setItem(cookieKey, uuidv4());
|
|
45
|
-
}
|
|
46
|
-
return localStorage.getItem(cookieKey)!;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export const getDeviceId = () => {
|
|
50
|
-
return getStorage(DEVICE_ID);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const getAdvertisingId = () => {
|
|
54
|
-
return getStorage(ADVERTISING_ID);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const firstCharUpperCase = (chars: string) => chars.charAt(0).toUpperCase() + chars.slice(1);
|
|
58
|
-
|
|
59
|
-
export const getUtmSource = () => {
|
|
60
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
61
|
-
return firstCharUpperCase(urlParams.get('utm_source') ?? '') || 'JoliboxSDK';
|
|
62
|
-
};
|
|
63
|
-
|
|
64
9
|
export const xUserAgent = () => {
|
|
65
|
-
const applicationName = 'JoliboxWebSDK';
|
|
66
|
-
const thePlatform = getPlatform();
|
|
67
|
-
const locale = navigator.language;
|
|
68
|
-
const deviceId = getDeviceId();
|
|
69
|
-
const adid = getAdvertisingId();
|
|
70
10
|
const appVersion = getAppVersion();
|
|
71
|
-
|
|
72
|
-
// const deviceModel = parserResult.device.model;
|
|
73
|
-
const deviceModel = 'UnknownModel';
|
|
74
|
-
// const systemVersion = parserResult.os.version;
|
|
75
|
-
const systemVersion = 'UnknownSystemVersion';
|
|
76
|
-
return `${applicationName} (${getUtmSource()}${thePlatform}; ${deviceModel}; ${systemVersion}; ${locale}) uuid/${deviceId} adid/${adid} version/${
|
|
77
|
-
appVersion || ''
|
|
78
|
-
}`;
|
|
11
|
+
return `${xua(appVersion)}`;
|
|
79
12
|
};
|
|
@@ -1,51 +1,18 @@
|
|
|
1
1
|
import { context } from '@/common/context';
|
|
2
2
|
import { logger } from '@jolibox/common';
|
|
3
|
-
import { IDevice, IPage, IEventPackage, IEvent, EProject } from '
|
|
3
|
+
import { IDevice, IPage, IEventPackage, IEvent, EProject, serializeEventPackage } from '@jolibox/common';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
'appVersion',
|
|
11
|
-
'appId',
|
|
12
|
-
'model',
|
|
13
|
-
'brand',
|
|
14
|
-
'uuid',
|
|
15
|
-
'jsSdkVersion',
|
|
16
|
-
'extra'
|
|
17
|
-
];
|
|
18
|
-
function serializeObject<T>(obj: T, order: (keyof T)[]): any[] {
|
|
19
|
-
return order.map((key) => {
|
|
20
|
-
if (key === 'params' && obj[key]) {
|
|
21
|
-
const params = obj[key] as Record<string, any>;
|
|
22
|
-
return Object.keys(params).reduce((acc, k) => {
|
|
23
|
-
acc[k] = String(params[k]);
|
|
24
|
-
return acc;
|
|
25
|
-
}, {} as Record<string, any>);
|
|
26
|
-
}
|
|
27
|
-
return obj[key];
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function serializeEvent(event: IEvent): any[] {
|
|
32
|
-
const location = event.location ? serializeObject(event.location, keysOfPage) : null;
|
|
33
|
-
const target = event.target ? serializeObject(event.target, keysOfPage) : null;
|
|
34
|
-
|
|
35
|
-
// 定义IEvent的序列化顺序
|
|
36
|
-
return serializeObject({ ...event, location, target }, keysOfEvent);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function serializeEventPackage(eventPackage: IEventPackage): any[] {
|
|
40
|
-
// 序列化事件数组
|
|
41
|
-
const events = eventPackage.events.map((event) => serializeEvent(event));
|
|
42
|
-
|
|
43
|
-
// 定义IDevice的序列化顺序
|
|
44
|
-
const device = serializeObject(eventPackage.device, keysOfDevice);
|
|
45
|
-
|
|
46
|
-
return [eventPackage.protocolVersion, events, device, eventPackage.project];
|
|
5
|
+
interface ISamplesConfig {
|
|
6
|
+
samples: {
|
|
7
|
+
[sampleRate: number]: string[];
|
|
8
|
+
};
|
|
9
|
+
cluster: number;
|
|
47
10
|
}
|
|
48
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Base class for event tracker, all the event tracker should inherit from this class
|
|
13
|
+
* Send event to oss reporter
|
|
14
|
+
* @abstract
|
|
15
|
+
*/
|
|
49
16
|
export abstract class EventTracker {
|
|
50
17
|
deviceInfo: IDevice | null = null;
|
|
51
18
|
|
|
@@ -54,6 +21,7 @@ export abstract class EventTracker {
|
|
|
54
21
|
abstract doReport<T extends Record<string, unknown>>(event: unknown[], extra?: T): void;
|
|
55
22
|
|
|
56
23
|
private pastTrackings: boolean[] = [];
|
|
24
|
+
private samplesConfig: ISamplesConfig | null = null;
|
|
57
25
|
|
|
58
26
|
private pushPastTracking = (tracking: boolean) => {
|
|
59
27
|
this.pastTrackings.push(tracking);
|
|
@@ -62,6 +30,10 @@ export abstract class EventTracker {
|
|
|
62
30
|
}
|
|
63
31
|
};
|
|
64
32
|
|
|
33
|
+
constructor() {
|
|
34
|
+
this.fetchSamplesConfig();
|
|
35
|
+
}
|
|
36
|
+
|
|
65
37
|
public get networkIsOk() {
|
|
66
38
|
// in the past 10 trackings, at least 60% of them are successful
|
|
67
39
|
return (
|
|
@@ -70,6 +42,26 @@ export abstract class EventTracker {
|
|
|
70
42
|
);
|
|
71
43
|
}
|
|
72
44
|
|
|
45
|
+
private async fetchSamplesConfig() {
|
|
46
|
+
const host = context.testMode ? 'https://stg-api.jolibox.com' : 'https://api.jolibox.com';
|
|
47
|
+
const path = `${host}/api/fe-configs/js-sdk/samples-config`;
|
|
48
|
+
const samplesConfig = (await (await fetch(path)).json()) as ISamplesConfig;
|
|
49
|
+
this.samplesConfig = samplesConfig;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private checkIsSampled(event: IEvent) {
|
|
53
|
+
if (!this.samplesConfig) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const { samples } = this.samplesConfig;
|
|
57
|
+
for (const [sampleRate, events] of Object.entries(samples)) {
|
|
58
|
+
if (events.includes(event.name)) {
|
|
59
|
+
return Math.floor(Math.random() * 101) < parseFloat(sampleRate);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
73
65
|
getDevice(): IDevice {
|
|
74
66
|
const { nativeSDKVersion, jssdkVersion } = context.sdkInfo;
|
|
75
67
|
return {
|
|
@@ -98,6 +90,10 @@ export abstract class EventTracker {
|
|
|
98
90
|
}
|
|
99
91
|
|
|
100
92
|
async trackEvent(event: IEvent, project: EProject, extraInfo?: Record<string, unknown>) {
|
|
93
|
+
// check if the event is sampled
|
|
94
|
+
if (this.checkIsSampled(event)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
101
97
|
const location = event.location ? event.location : await this.getLocation();
|
|
102
98
|
const target = event.target || null;
|
|
103
99
|
const extra = event.extra || null;
|