@jolibox/implement 1.1.12 → 1.1.13-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.rush/temp/package-deps_build.json +35 -41
  2. package/.rush/temp/shrinkwrap-deps.json +1 -1
  3. package/dist/common/ads/anti-cheating.d.ts +5 -0
  4. package/dist/common/context/index.d.ts +5 -0
  5. package/dist/common/context/url-parse.d.ts +8 -1
  6. package/dist/common/rewards/__tests__/can-use-jolicoin.test.d.ts +1 -0
  7. package/dist/common/rewards/fetch-reward.d.ts +2 -0
  8. package/dist/common/rewards/index.d.ts +18 -0
  9. package/dist/common/rewards/registers/use-ads.d.ts +3 -0
  10. package/dist/common/rewards/registers/use-jolicoin.d.ts +9 -0
  11. package/dist/common/rewards/registers/utils/index.d.ts +2 -0
  12. package/dist/common/rewards/reward-helper.d.ts +14 -0
  13. package/dist/common/rewards/type.d.ts +23 -0
  14. package/dist/common/utils/index.d.ts +7 -0
  15. package/dist/index.js +3 -3
  16. package/dist/index.native.js +131 -4
  17. package/dist/native/api/index.d.ts +1 -0
  18. package/dist/native/api/keyboard.d.ts +1 -1
  19. package/dist/native/api/navigate.d.ts +1 -0
  20. package/dist/native/network/create-fetch.d.ts +1 -0
  21. package/dist/native/ui/retention.d.ts +1 -0
  22. package/implement.build.log +2 -2
  23. package/package.json +5 -3
  24. package/src/common/ads/anti-cheating.test.ts +4 -2
  25. package/src/common/ads/anti-cheating.ts +12 -4
  26. package/src/common/ads/index.ts +51 -33
  27. package/src/common/context/index.ts +22 -3
  28. package/src/common/context/url-parse.ts +24 -1
  29. package/src/common/rewards/__tests__/can-use-jolicoin.test.ts +94 -0
  30. package/src/common/rewards/fetch-reward.ts +33 -0
  31. package/src/common/rewards/index.ts +20 -0
  32. package/src/common/rewards/registers/use-ads.ts +9 -0
  33. package/src/common/rewards/registers/use-jolicoin.ts +89 -0
  34. package/src/common/rewards/registers/utils/index.ts +11 -0
  35. package/src/common/rewards/reward-helper.ts +56 -0
  36. package/src/common/rewards/type.ts +25 -0
  37. package/src/common/utils/index.ts +7 -0
  38. package/src/h5/api/ads.ts +20 -1
  39. package/src/h5/api/get-system-info.ts +1 -1
  40. package/src/h5/http/utils/__tests__/xua.test.ts +1 -1
  41. package/src/native/api/ads.ts +31 -3
  42. package/src/native/api/get-system-info.ts +2 -2
  43. package/src/native/api/index.ts +1 -0
  44. package/src/native/api/keyboard.ts +1 -1
  45. package/src/native/api/lifecycle.ts +16 -4
  46. package/src/native/api/login.ts +1 -1
  47. package/src/native/api/navigate.ts +61 -0
  48. package/src/native/api/request.ts +19 -10
  49. package/src/native/api/storage.ts +1 -1
  50. package/src/native/bootstrap/index.ts +111 -27
  51. package/src/native/network/create-fetch.ts +7 -2
  52. package/src/native/network/utils.ts +13 -6
  53. package/src/native/report/errors/index.ts +1 -1
  54. package/src/native/report/index.ts +1 -1
  55. package/src/native/report/task-tracker.ts +1 -1
  56. package/src/native/ui/retention.ts +153 -0
  57. package/dist/native/bootstrap/bridge.d.ts +0 -4
  58. package/dist/native/js-bridge/const.d.ts +0 -5
  59. package/dist/native/js-bridge/index.d.ts +0 -2
  60. package/dist/native/js-bridge/invoke.d.ts +0 -21
  61. package/dist/native/js-bridge/js-bridge.d.ts +0 -6
  62. package/dist/native/js-bridge/report.d.ts +0 -63
  63. package/dist/native/js-bridge/subscribe.d.ts +0 -8
  64. package/dist/native/js-bridge/types.d.ts +0 -14
  65. package/dist/native/js-bridge/utils.d.ts +0 -17
  66. package/dist/native/js-core/index.d.ts +0 -3
  67. package/dist/native/js-core/jolibox-js-core.d.ts +0 -45
  68. package/dist/native/js-core/message-port.d.ts +0 -12
  69. package/dist/native/js-core/utils.d.ts +0 -7
  70. package/src/native/bootstrap/bridge.ts +0 -59
  71. package/src/native/js-bridge/const.ts +0 -11
  72. package/src/native/js-bridge/index.ts +0 -2
  73. package/src/native/js-bridge/invoke.ts +0 -208
  74. package/src/native/js-bridge/js-bridge.ts +0 -23
  75. package/src/native/js-bridge/report.ts +0 -311
  76. package/src/native/js-bridge/subscribe.ts +0 -50
  77. package/src/native/js-bridge/types.ts +0 -26
  78. package/src/native/js-bridge/utils.ts +0 -116
  79. package/src/native/js-core/index.ts +0 -4
  80. package/src/native/js-core/jolibox-js-core.ts +0 -188
  81. package/src/native/js-core/message-port.ts +0 -52
  82. package/src/native/js-core/utils.ts +0 -9
  83. package/src/native/types/global.d.ts +0 -26
  84. package/src/native/types/native-method-map.d.ts +0 -300
  85. package/src/native/types/native-method.d.ts +0 -30
@@ -0,0 +1,25 @@
1
+ export interface IJoliCoin {
2
+ balance: number;
3
+ enableAutoDeduct: boolean;
4
+ }
5
+
6
+ export type IUnlockOptionType = 'JOLI_COIN' | 'ADS';
7
+
8
+ interface IJoliCoinChoice {
9
+ joliCoinQuantity: number;
10
+ }
11
+ export interface IUnlockOption {
12
+ type: IUnlockOptionType;
13
+ joliCoinChoices: IJoliCoinChoice[];
14
+ }
15
+
16
+ export interface IJolicoinRewardOption {
17
+ code: 'SUCCESS' | 'ERROR' | 'PARAMETER_ERROR' | 'EPISODE_LOCK_JUMP';
18
+ message: string;
19
+ data: {
20
+ unlockOptions?: IUnlockOption[];
21
+ };
22
+ extra: {
23
+ joliCoin: IJoliCoin;
24
+ };
25
+ }
@@ -1,9 +1,16 @@
1
1
  const JOLIBOX_CUSTOM_ADS_EVENT_TYPE = 'JOLIBOX_ADS_EVENT';
2
+ const JOLIBOX_CUSTOM_REWARDS_EVENT_TYPE = 'JOLIBOX_CUSTOM_REWARDS_EVENT';
2
3
 
3
4
  interface JoliboxCustomEvent {
4
5
  [JOLIBOX_CUSTOM_ADS_EVENT_TYPE]: {
5
6
  isAdShowing: boolean;
6
7
  };
8
+ [JOLIBOX_CUSTOM_REWARDS_EVENT_TYPE]: {
9
+ ['JOLI_COIN']?: {
10
+ quantity: number;
11
+ balance: number;
12
+ };
13
+ };
7
14
  }
8
15
 
9
16
  export const notifyCustomEvent = <T extends keyof JoliboxCustomEvent>(
package/src/h5/api/ads.ts CHANGED
@@ -3,10 +3,23 @@ import { track } from '../report';
3
3
  import { createCommands } from '@jolibox/common';
4
4
  import { httpClientManager } from '../http';
5
5
  import { notifyCustomEvent } from '@/common/utils';
6
+ import { createAdsRewardHandler, rewardsHelper, createJolicoinRewardHandler } from '@/common/rewards';
7
+
6
8
  const commands = createCommands();
7
9
 
8
10
  const httpClient = httpClientManager.create();
9
11
  const ads = new JoliboxAdsImpl(track, httpClient, () => httpClientManager.getNetworkStatus());
12
+ rewardsHelper.registerRewardHandler('ADS', createAdsRewardHandler(ads));
13
+ rewardsHelper.registerRewardHandler(
14
+ 'JOLI_COIN',
15
+ createJolicoinRewardHandler(ads.httpClient, {
16
+ onUnlockSuccess: (params: { quantity: number; balance: number }) => {
17
+ notifyCustomEvent('JOLIBOX_CUSTOM_REWARDS_EVENT', {
18
+ JOLI_COIN: params
19
+ });
20
+ }
21
+ })
22
+ );
10
23
 
11
24
  adEventEmitter.on('isAdShowing', (isAdShowing) => {
12
25
  notifyCustomEvent('JOLIBOX_ADS_EVENT', { isAdShowing });
@@ -21,7 +34,13 @@ commands.registerCommand('AdsSDK.adConfig', (params) => {
21
34
  });
22
35
 
23
36
  commands.registerCommand('AdsSDK.adBreak', (params) => {
24
- ads.adBreak(params);
37
+ if (params.type === 'reward') {
38
+ rewardsHelper.getRewardsTypes(ads.httpClient).then((rewardsTypes) => {
39
+ rewardsHelper.handleReward(rewardsTypes, params);
40
+ });
41
+ } else {
42
+ ads.adBreak(params);
43
+ }
25
44
  });
26
45
 
27
46
  commands.registerCommand('AdsSDK.adUnit', (params) => {
@@ -11,7 +11,7 @@ const API_GET_SYSTEM_SYNC = 'getSystemInfoSync';
11
11
  const getSystemInfo = () => {
12
12
  let config = {
13
13
  system: context.deviceInfo.system,
14
- platform: context.deviceInfo.platform,
14
+ platform: context.deviceInfo.platform.toLowerCase(),
15
15
  brand: context.deviceInfo.brand,
16
16
  pixelRatio: context.deviceInfo.pixelRatio,
17
17
  language: context.deviceInfo.lang,
@@ -4,7 +4,7 @@ import { isValidUUIDV4 } from '@jolibox/common';
4
4
 
5
5
  describe('xua', () => {
6
6
  it('test xua', () => {
7
- // window.__JOLIBOX_LOCAL_SDK_VERSION__ = '1.0.0';
7
+ (window as any).__joliboxLocalStorage__ = window.localStorage;
8
8
  const xua = xUserAgent();
9
9
  const deviceInfoRegex = /.* (\(.*\) ).*/;
10
10
  const deviceInfoMatch = xua.match(deviceInfoRegex);
@@ -1,5 +1,6 @@
1
- import { createCommands, logger } from '@jolibox/common';
1
+ import { createCommands } from '@jolibox/common';
2
2
  import { createSyncAPI, registerCanIUse } from './base';
3
+ import { createToast } from '@jolibox/ui';
3
4
 
4
5
  const commands = createCommands();
5
6
 
@@ -7,7 +8,8 @@ import { IAdsInitParams, JoliboxAdsImpl, IAdConfigParams, IAdBreakParams, IAdUni
7
8
  import { track } from '../report';
8
9
 
9
10
  import { innerFetch as fetch } from '../network';
10
- import { invokeNative } from '../bootstrap/bridge';
11
+ import { invokeNative } from '@jolibox/native-bridge';
12
+ import { rewardsHelper, createAdsRewardHandler, createJolicoinRewardHandler } from '@/common/rewards';
11
13
 
12
14
  const checkNetworkStatus = () => {
13
15
  const { data } = invokeNative('getNetworkStatusSync');
@@ -34,6 +36,26 @@ const ads = new JoliboxAdsImpl(
34
36
  checkNetworkStatus
35
37
  );
36
38
 
39
+ rewardsHelper.registerRewardHandler('ADS', createAdsRewardHandler(ads));
40
+ rewardsHelper.registerRewardHandler(
41
+ 'JOLI_COIN',
42
+ createJolicoinRewardHandler(ads.httpClient, {
43
+ onUnlockSuccess: (params) => {
44
+ const { quantity, balance } = params;
45
+ const balanceStr = balance >= 1000 ? '999+' : balance;
46
+ createToast(`{slot-checkmark} −${quantity} {slot-coin} | Balance: ${balanceStr} {slot-coin}`, {
47
+ customStyle: {
48
+ mark: {
49
+ marginRight: '8px'
50
+ }
51
+ }
52
+ });
53
+ },
54
+ onUnlockFailed: () => {
55
+ console.log('onUnlockFailed');
56
+ }
57
+ })
58
+ );
37
59
  const adInit = createSyncAPI('adInit', {
38
60
  implement: (config?: IAdsInitParams) => {
39
61
  ads.init(config);
@@ -48,7 +70,13 @@ const adConfig = createSyncAPI('adConfig', {
48
70
 
49
71
  const adBreak = createSyncAPI('adBreak', {
50
72
  implement: (params: IAdBreakParams) => {
51
- ads.adBreak(params);
73
+ if (params.type === 'reward') {
74
+ rewardsHelper.getRewardsTypes(ads.httpClient).then((rewardsTypes) => {
75
+ rewardsHelper.handleReward(rewardsTypes, params);
76
+ });
77
+ } else {
78
+ ads.adBreak(params);
79
+ }
52
80
  }
53
81
  });
54
82
 
@@ -1,6 +1,6 @@
1
1
  import { createCommands } from '@jolibox/common';
2
2
  import { createSyncAPI, registerCanIUse } from './base';
3
- import { invokeNative } from '../bootstrap/bridge';
3
+ import { invokeNative } from '@jolibox/native-bridge';
4
4
 
5
5
  const commands = createCommands();
6
6
 
@@ -13,7 +13,7 @@ const getSystemInfoSync = createSyncAPI(API_GET_SYSTEM_SYNC, {
13
13
  const { data } = res;
14
14
  return {
15
15
  system: data.deviceInfo.system,
16
- platform: data.deviceInfo.platform,
16
+ platform: data.deviceInfo.platform.toLowerCase() as 'h5' | 'android' | 'ios',
17
17
  version: data.sdkInfo.jssdkVersion,
18
18
  pixelRatio: data.deviceInfo.pixelRatio,
19
19
  language: data.deviceInfo.lang,
@@ -6,3 +6,4 @@ import './keyboard';
6
6
  import './task';
7
7
  import './login';
8
8
  import './ads';
9
+ import './navigate';
@@ -1,5 +1,5 @@
1
1
  import { createCommands } from '@jolibox/common';
2
- import { invokeNative } from '../bootstrap/bridge';
2
+ import { invokeNative } from '@jolibox/native-bridge';
3
3
  import { createSyncAPI, registerCanIUse, t } from './base';
4
4
 
5
5
  export const showKeyboard = createSyncAPI('showKeyboard', {
@@ -1,8 +1,9 @@
1
- import { BaseError, createCommands, wrapUserFunction, hostEmitter } from '@jolibox/common';
1
+ import { BaseError, createCommands, wrapUserFunction, hostEmitter, isBoolean } from '@jolibox/common';
2
2
  import { createAPI, createSyncAPI, registerCanIUse, t } from './base';
3
3
  import { reportError } from '@/common/report/errors/report';
4
- import { applyNative, onNative } from '../bootstrap/bridge';
4
+ import { applyNative, onNative, publish } from '@jolibox/native-bridge';
5
5
  import { nativeTaskEmitter } from '../report';
6
+ import { context } from '@/common/context';
6
7
 
7
8
  const EXIT_GAME = 'exitGame';
8
9
  const ON_READY = 'onReady';
@@ -13,11 +14,22 @@ const commands = createCommands();
13
14
 
14
15
  const safeCallbackWrapper = wrapUserFunction(reportError as (err: Error | BaseError) => void);
15
16
  const exitGame = createAPI(EXIT_GAME, {
16
- paramsSchema: t.tuple(t.function()),
17
- implement: async (onBeforeExit: () => void) => {
17
+ paramsSchema: t.tuple(t.function(), t.boolean().optional().default(false)),
18
+ implement: async (onBeforeExit: () => void, shouldStay = false) => {
18
19
  const safeCallback = safeCallbackWrapper(onBeforeExit);
19
20
  // 集中上报
20
21
  safeCallback.call(this);
22
+ // 透传context.shouldInterupt 且值为false时,为内部小程序。广播onRetentionResult
23
+ if (isBoolean(context.shouldInterupt) && !context.shouldInterupt && context.from) {
24
+ publish(
25
+ 'onRetentionResult',
26
+ {
27
+ shouldStay
28
+ },
29
+ context.from,
30
+ true
31
+ );
32
+ }
21
33
  await applyNative('exitAppAsync');
22
34
  }
23
35
  });
@@ -1,5 +1,5 @@
1
1
  import { context } from '@/common/context';
2
- import { applyNative, invokeNative, onNative } from '../bootstrap/bridge';
2
+ import { applyNative, invokeNative, onNative } from '@jolibox/native-bridge';
3
3
  import { createAPI, t, registerCanIUse } from './base';
4
4
  import { createCommands, Deferred, hostEmitter } from '@jolibox/common';
5
5
  import { createAPIError } from '@/common/report/errors';
@@ -0,0 +1,61 @@
1
+ import { createCommands, UserCustomError } from '@jolibox/common';
2
+ import { invokeNative } from '@jolibox/native-bridge';
3
+ import { createSyncAPI, t, registerCanIUse } from './base';
4
+ import { context } from '@/common/context';
5
+
6
+ const commands = createCommands();
7
+
8
+ const openSchemaSync = createSyncAPI('openSchemaSync', {
9
+ paramsSchema: t.tuple(t.string()),
10
+ implement: (schema) => {
11
+ // Add compatibility logic for incomplete URLs
12
+ let finalSchema = schema;
13
+ if (!schema.match(/^https?:\/\//)) {
14
+ finalSchema = `https://${context.mpId}.app.jolibox.com${schema.startsWith('/') ? '' : '/'}${schema}`;
15
+ }
16
+
17
+ const res = invokeNative('openSchemaSync', {
18
+ schema: finalSchema
19
+ });
20
+ if (res.errNo !== 0) {
21
+ throw new UserCustomError(res.errMsg, res.errNo);
22
+ }
23
+ }
24
+ });
25
+
26
+ const openPageSync = createSyncAPI('openPageSync', {
27
+ paramsSchema: t.tuple(t.string()),
28
+ implement: (page) => {
29
+ const { errNo, errMsg, data } = invokeNative('openPageSync', { url: page });
30
+ if (errNo !== 0 || !data) {
31
+ throw new UserCustomError(errMsg, errNo);
32
+ }
33
+ return { webviewId: data.webviewId };
34
+ }
35
+ });
36
+
37
+ const closePageSync = createSyncAPI('closePageSync', {
38
+ paramsSchema: t.tuple(t.number()),
39
+ implement: (webviewId) => {
40
+ const { errNo, errMsg } = invokeNative('closePageSync', { webviewId });
41
+ if (errNo !== 0) {
42
+ throw new UserCustomError(errMsg, errNo);
43
+ }
44
+ }
45
+ });
46
+
47
+ commands.registerCommand('RouterSDK.openSchema', openSchemaSync);
48
+ commands.registerCommand('RouterSDK.openPage', openPageSync);
49
+ commands.registerCommand('RouterSDK.closePage', closePageSync);
50
+
51
+ registerCanIUse('router.openSchema', {
52
+ version: '1.1.10'
53
+ });
54
+
55
+ registerCanIUse('router.openPage', {
56
+ version: '1.1.10'
57
+ });
58
+
59
+ registerCanIUse('router.closePage', {
60
+ version: '1.1.10'
61
+ });
@@ -1,16 +1,18 @@
1
1
  import { createAPIError } from '@/common/report/errors';
2
2
  import { createFetch } from '../network/create-fetch';
3
- import { createAPI, registerCanIUse, t } from './base';
3
+ import { createSyncAPI, registerCanIUse, t } from './base';
4
4
  import { dirtyURL, normalizeHeader, normalizeMethod, normalizeTimeout } from '../network/utils';
5
5
  import { BaseError, createCommands, Deferred, wrapUserFunction } from '@jolibox/common';
6
6
  import { AnyFunction, ResponseType } from '@jolibox/types';
7
7
  import { reportError } from '@/common/report/errors/report';
8
8
  import { FetchOptions } from '../network/types';
9
+ import { context } from '@/common/context';
9
10
 
10
11
  const LIMIT = 15;
11
12
 
12
13
  const publicFetch = createFetch('createRequestTaskSync', 'operateRequestTaskSync', {
13
- type: 'public'
14
+ type: 'public',
15
+ baseUrl: context.baseApiHost
14
16
  });
15
17
 
16
18
  /** current active count */
@@ -41,12 +43,12 @@ const request = (_params: unknown) => {
41
43
  // noop
42
44
  };
43
45
 
44
- const _request = createAPI('request', {
46
+ const _request = createSyncAPI('request', {
45
47
  paramsSchema: t.tuple(
46
48
  t.object({
47
49
  url: t.string(),
48
50
  method: t.string(),
49
- header: t.object(),
51
+ headers: t.object(),
50
52
  data: t.object().optional(),
51
53
  query: t.object().optional(),
52
54
  dataType: t.string().default('json'),
@@ -68,9 +70,10 @@ const request = (_params: unknown) => {
68
70
  if (deferred.state !== 'pending') {
69
71
  return;
70
72
  }
73
+
71
74
  const { data, dataType, responseType, enableCache, appendHostCookie } = params;
72
75
 
73
- const header = normalizeHeader(params.header);
76
+ const header = normalizeHeader(params.headers);
74
77
  const method = normalizeMethod(params.method);
75
78
  const timeout = normalizeTimeout(params.timeout);
76
79
  const url = dirtyURL(params.url, method, data);
@@ -89,15 +92,21 @@ const request = (_params: unknown) => {
89
92
 
90
93
  if (timeout) _params['timeout'] = timeout;
91
94
 
92
- const task = publicFetch<string | ArrayBuffer | JSON>(url, _params);
93
-
95
+ const task = publicFetch<
96
+ string | ArrayBuffer | JSON | { status: number; data: string | ArrayBuffer | JSON }
97
+ >(url, _params);
94
98
  {
95
99
  const { response } = await task;
96
100
  const { code, data, message } = response;
97
-
101
+ if (data && typeof data === 'object' && 'status' in data && data.status !== 200) {
102
+ throw createAPIError({
103
+ code: data.status || -1,
104
+ msg: `request:fail status ${data.status}`
105
+ });
106
+ }
98
107
  deferred.resolve({
99
108
  code: (code ?? 'SUCCESS') as ResponseType,
100
- data,
109
+ data: data as string | ArrayBuffer | JSON,
101
110
  message: message ?? 'request:ok'
102
111
  });
103
112
 
@@ -132,7 +141,7 @@ const request = (_params: unknown) => {
132
141
  }
133
142
  });
134
143
 
135
- _request(_params);
144
+ _request(_params as Parameters<typeof _request>[0]);
136
145
 
137
146
  return {
138
147
  /**
@@ -1,5 +1,5 @@
1
1
  import { createAPI, createSyncAPI, registerCanIUse, t } from './base';
2
- import { applyNative, invokeNative } from '../bootstrap/bridge';
2
+ import { applyNative, invokeNative } from '@jolibox/native-bridge';
3
3
  import { createCommands } from '@jolibox/common';
4
4
  import { innerFetch as fetch } from '../network';
5
5
  import { context } from '@/common/context';
@@ -1,39 +1,63 @@
1
- import { invokeNative, onNative, RuntimeLoader } from './bridge';
2
- import { joliboxJSCore } from '../js-core';
1
+ import { invokeNative, onNative, RuntimeLoader } from '@jolibox/native-bridge';
3
2
  import { context } from '@/common/context';
4
3
  import { hostEmitter, isBoolean } from '@jolibox/common';
5
4
  import { taskTracker, track } from '../report';
6
5
  import { initializeNativeEnv } from './init-env';
7
6
  import { adEventEmitter } from '@/common/ads';
7
+ import { openRetentionSchema } from '../ui/retention';
8
+ import { innerFetch } from '../network';
9
+ import { Env } from '@jolibox/types';
10
+ interface IBasicMetaConfig {
11
+ canShowRecommended: boolean;
12
+ }
13
+
14
+ declare const globalThis: {
15
+ joliboxJSCore: {
16
+ doExit: (uuid: string) => void;
17
+ onDocumentReady: (path: string) => void;
18
+ env: (params: { onError: (error: Error) => void }) => Env | undefined;
19
+ };
20
+ };
21
+
22
+ /**
23
+ * 全局变量
24
+ */
8
25
 
26
+ /**
27
+ * 清除safari jolibox样式
28
+ */
9
29
  let cleanStyles: () => void;
30
+
31
+ /**
32
+ * 启动时间戳
33
+ */
34
+ let start_timestamp: number;
35
+
36
+ /**
37
+ * 是否展示广告
38
+ */
39
+ let isAdShowing = false;
40
+
41
+ let baskcMeta: IBasicMetaConfig | null = null;
42
+
10
43
  RuntimeLoader.onReady(() => {
11
44
  // TODO: merge some env config
12
45
  });
13
46
 
14
- RuntimeLoader.doExit(() => {
15
- //埋点上报
16
- track('onBeforeExit', {
17
- timestamp: Date.now()
47
+ /**
48
+ * 添加广告展示监听
49
+ */
50
+ function addShowAdListener() {
51
+ adEventEmitter.on('isAdShowing', (isShowing) => {
52
+ isAdShowing = isShowing;
53
+ if (isBoolean(isAdShowing)) {
54
+ invokeNative('updateContainerConfigSync', {
55
+ displayCapsuleButton: !isAdShowing,
56
+ webviewId: context.webviewId
57
+ });
58
+ }
18
59
  });
19
- cleanStyles?.();
20
- taskTracker.close(Date.now() - start_timestamp);
21
- if (isAdShowing) {
22
- return true; // Forbid exit on watching ads
23
- }
24
- return false;
25
- });
26
-
27
- let isAdShowing = false;
28
- adEventEmitter.on('isAdShowing', (isShowing) => {
29
- isAdShowing = isShowing;
30
- if (isBoolean(isAdShowing)) {
31
- invokeNative('updateContainerConfigSync', {
32
- displayCapsuleButton: !isAdShowing,
33
- webviewId: context.webviewId
34
- });
35
- }
36
- });
60
+ }
37
61
 
38
62
  /**
39
63
  * The DOMContentLoaded event might be triggered very early,
@@ -41,7 +65,7 @@ adEventEmitter.on('isAdShowing', (isShowing) => {
41
65
  */
42
66
  function addWebviewReadyListener() {
43
67
  hostEmitter.on('onDocumentReady', () => {
44
- joliboxJSCore?.onDocumentReady(window.location.href);
68
+ globalThis.joliboxJSCore?.onDocumentReady(window.location.href);
45
69
  track('onDocumentReady', {
46
70
  start_timestamp,
47
71
  timestamp: Date.now()
@@ -72,14 +96,74 @@ function addGameServiceReadyListener() {
72
96
  });
73
97
 
74
98
  onNative('onBeforeExit', ({ uuid }) => {
75
- joliboxJSCore?.doExit(uuid);
99
+ globalThis.joliboxJSCore?.doExit(uuid);
76
100
  });
77
101
  }
78
- let start_timestamp: number;
102
+
103
+ function addDoExitLoader() {
104
+ /**
105
+ * 退出逻辑
106
+ * 1. 如果正在展示广告,则禁止退出
107
+ * 2. 如果指定退出挽留逻辑,则按照指定逻辑运行
108
+ * 3. 如果退出挽留逻辑返回 true,则按照指定逻辑运行
109
+ * 4. 否则,按照默认逻辑运行
110
+ */
111
+
112
+ const doActualExit = () => {
113
+ //埋点上报
114
+ track('onBeforeExit', {
115
+ timestamp: Date.now()
116
+ });
117
+ cleanStyles?.();
118
+ taskTracker.close(Date.now() - start_timestamp);
119
+ };
120
+
121
+ RuntimeLoader.onDoExit(async () => {
122
+ if (isAdShowing) {
123
+ return true; // Forbid exit on watching ads
124
+ }
125
+
126
+ // 指定退出挽留逻辑,则按照指定逻辑运行
127
+ if (isBoolean(context.shouldInterupt)) {
128
+ // 不需要打断退出,上报埋点
129
+ if (!context.shouldInterupt) {
130
+ doActualExit();
131
+ }
132
+ return context.shouldInterupt;
133
+ }
134
+
135
+ if (baskcMeta?.canShowRecommended) {
136
+ const stay = await openRetentionSchema();
137
+ if (stay) {
138
+ // 挽留成功,打断退出
139
+ return true;
140
+ }
141
+ }
142
+
143
+ // 退出,对应上报
144
+ //埋点上报
145
+ doActualExit();
146
+ return false;
147
+ });
148
+ }
149
+
150
+ async function fetchMetaConfig() {
151
+ const url = `${context.baseApiHost}/api/fe-configs/js-sdk/basic-meta`;
152
+ const {
153
+ response: { data }
154
+ } = await innerFetch<IBasicMetaConfig>(url);
155
+
156
+ if (data) {
157
+ baskcMeta = data;
158
+ }
159
+ }
79
160
 
80
161
  export function config(): void {
81
162
  start_timestamp = Date.now();
82
163
  addGameServiceReadyListener();
164
+ addShowAdListener();
165
+ addDoExitLoader();
83
166
  addWebviewReadyListener();
167
+ fetchMetaConfig();
84
168
  cleanStyles = initializeNativeEnv();
85
169
  }
@@ -2,7 +2,7 @@ import { isString, logger, Deferred, isObject, hostEmitter } from '@jolibox/comm
2
2
  import { reportNetworkAPI } from './report';
3
3
  import { RequestFrom } from './types';
4
4
  import { FetchResponse, FetchOptions } from './types';
5
- import { invokeNative, onNative } from '@/native/bootstrap/bridge';
5
+ import { invokeNative, onNative } from '@jolibox/native-bridge';
6
6
  import { AnyFunction } from '@jolibox/types';
7
7
  import { reportError } from '@/common/report/errors/report';
8
8
  import { createAPIError } from '@/common/report/errors';
@@ -46,6 +46,7 @@ export function createFetch(
46
46
  operateMethod: 'operateRequestTaskSync',
47
47
  options?: {
48
48
  baseUrl?: string;
49
+ defaultHeaders?: Record<string, string>;
49
50
  type: 'public';
50
51
  }
51
52
  ): PublicFetch;
@@ -81,7 +82,11 @@ export function createFetch(
81
82
 
82
83
  if (res.state === 'success') {
83
84
  const { requestTaskId, state, ...rest } = res;
84
- task.resolve(rest);
85
+ if (res.statusCode === 200) {
86
+ task.resolve(rest);
87
+ } else {
88
+ task.reject(rest);
89
+ }
85
90
  promiseMap.delete(requestTaskId);
86
91
  } else if (res.state === 'fail') {
87
92
  const { requestTaskId, state, ...rest } = res;
@@ -79,12 +79,19 @@ export function dirtyURL(url: string, method: HTTPMethod, data?: unknown) {
79
79
 
80
80
  if (!isObject(data)) return url;
81
81
 
82
- const [_url, search = ''] = url.split('?');
82
+ const [baseUrl, search = ''] = url.split('?');
83
83
 
84
- const query = JSON.stringify({
85
- ...JSON.parse(search),
86
- ...data
87
- });
84
+ // Parse existing query parameters
85
+ const dataParams = new URLSearchParams(search);
88
86
 
89
- return `${_url}?${query}`;
87
+ // Add new parameters from data object
88
+ for (const [key, value] of Object.entries(data)) {
89
+ if (value !== undefined && value !== null) {
90
+ dataParams.append(key, String(value));
91
+ }
92
+ }
93
+
94
+ const queryString = dataParams.toString();
95
+
96
+ return queryString ? `${baseUrl}?${queryString}` : baseUrl;
90
97
  }
@@ -5,7 +5,7 @@ import '@/common/report/errors/report/listeners';
5
5
  import { errorReportEmitter } from '@/common/report/errors';
6
6
  import { ErrorData } from '@/common/report/errors/report';
7
7
  import { logger } from '@jolibox/common';
8
- import { applyNative, invokeNative } from '@/native/bootstrap/bridge';
8
+ import { invokeNative } from '@jolibox/native-bridge';
9
9
 
10
10
  errorReportEmitter.on('GLOBAL_ERROR', (error, errorData: ErrorData) => {
11
11
  const extra = {
@@ -4,7 +4,7 @@ export * from '../../common/report/types';
4
4
  import { createTracks, ReportHandler } from '@/common/report';
5
5
  import { context } from '@/common/context';
6
6
  import { NativeTaskTracker } from './task-tracker';
7
- import { invokeNative } from '../bootstrap/bridge';
7
+ import { invokeNative } from '@jolibox/native-bridge';
8
8
 
9
9
  const reportNative: ReportHandler = (event, data, webviewId) => {
10
10
  const _data = data as Record<string, unknown>;
@@ -6,7 +6,7 @@ import { context } from '@/common/context';
6
6
  import { TaskTracker, TaskPoint } from '@/common/report/task-track';
7
7
  import { EventEmitter } from '@jolibox/common';
8
8
  import { innerFetch as fetch } from '../network';
9
- import { applyNative } from '../bootstrap/bridge';
9
+ import { applyNative } from '@jolibox/native-bridge';
10
10
  import type { Track } from '.';
11
11
  import type { TrackEvent } from '@jolibox/types';
12
12