@jolibox/implement 1.2.5-beta.3 → 1.2.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.
Files changed (48) hide show
  1. package/.rush/temp/package-deps_build.json +17 -24
  2. package/dist/common/cache/request-cache-service.d.ts +111 -0
  3. package/dist/common/rewards/cached-fetch-reward.d.ts +2 -2
  4. package/dist/common/rewards/cached-reward-service.d.ts +2 -2
  5. package/dist/common/rewards/index.d.ts +0 -1
  6. package/dist/common/rewards/registers/utils/coins/jolicoin/cached-fetch-balance.d.ts +2 -2
  7. package/dist/common/rewards/registers/utils/coins/joligem/cached-fetch-gem-balance.d.ts +2 -2
  8. package/dist/common/rewards/reward-emitter.d.ts +0 -7
  9. package/dist/common/rewards/reward-helper.d.ts +1 -2
  10. package/dist/common/utils/index.d.ts +0 -18
  11. package/dist/h5/api/platformAdsHandle/JoliboxAdsHandler.d.ts +0 -1
  12. package/dist/index.js +9 -9
  13. package/dist/index.native.js +47 -47
  14. package/dist/native/payment/payment-service.d.ts +1 -1
  15. package/implement.build.log +2 -2
  16. package/package.json +7 -7
  17. package/src/common/cache/__tests__/request-cache-service.test.ts +686 -0
  18. package/src/common/cache/request-cache-service.ts +393 -0
  19. package/src/common/rewards/cached-fetch-reward.ts +2 -19
  20. package/src/common/rewards/cached-reward-service.ts +3 -3
  21. package/src/common/rewards/index.ts +0 -1
  22. package/src/common/rewards/registers/utils/coins/commands/use-payment.ts +8 -0
  23. package/src/common/rewards/registers/utils/coins/jolicoin/cached-fetch-balance.ts +1 -1
  24. package/src/common/rewards/registers/utils/coins/joligem/cached-fetch-gem-balance.ts +1 -1
  25. package/src/common/rewards/reward-emitter.ts +0 -8
  26. package/src/common/rewards/reward-helper.ts +1 -8
  27. package/src/common/utils/index.ts +0 -23
  28. package/src/h5/api/ads.ts +13 -14
  29. package/src/h5/api/platformAdsHandle/JoliboxAdsHandler.ts +1 -25
  30. package/src/h5/bootstrap/index.ts +19 -4
  31. package/src/h5/rewards/index.ts +1 -18
  32. package/src/native/payment/payment-service.ts +1 -1
  33. package/CHANGELOG.json +0 -11
  34. package/CHANGELOG.md +0 -9
  35. package/dist/common/rewards/registers/use-subscription.d.ts +0 -7
  36. package/dist/common/rewards/registers/utils/subscription/commands/index.d.ts +0 -1
  37. package/dist/common/rewards/registers/utils/subscription/commands/use-subscription.d.ts +0 -4
  38. package/dist/common/rewards/registers/utils/subscription/sub-handler.d.ts +0 -13
  39. package/dist/h5/bootstrap/auth/index.d.ts +0 -2
  40. package/dist/h5/bootstrap/auth/sub.d.ts +0 -2
  41. package/src/common/rewards/registers/use-subscription.ts +0 -34
  42. package/src/common/rewards/registers/utils/subscription/commands/index.ts +0 -1
  43. package/src/common/rewards/registers/utils/subscription/commands/use-subscription.ts +0 -29
  44. package/src/common/rewards/registers/utils/subscription/sub-handler.ts +0 -88
  45. package/src/h5/bootstrap/auth/__tests__/auth.test.ts +0 -308
  46. package/src/h5/bootstrap/auth/index.ts +0 -20
  47. package/src/h5/bootstrap/auth/sub.ts +0 -56
  48. /package/dist/{h5/bootstrap/auth/__tests__/auth.test.d.ts → common/cache/__tests__/request-cache-service.test.d.ts} +0 -0
@@ -8,7 +8,6 @@ import {
8
8
  } from '@jolibox/ads';
9
9
  import { IAdsHandler } from '../ads';
10
10
  import { JoliboxHttpClient } from '@/h5/http';
11
- import { context } from '@/common/context';
12
11
 
13
12
  export default class JoliboxAdsHandler implements IAdsHandler {
14
13
  constructor(private ads: JoliboxAdsForGame, private httpClient: JoliboxHttpClient) {}
@@ -26,21 +25,10 @@ export default class JoliboxAdsHandler implements IAdsHandler {
26
25
  }
27
26
 
28
27
  async adBreak(params: IAdBreakParams): Promise<void> {
29
- const isSubUser = context.hostUserInfo?.isSubUser;
30
-
31
- // if is sub user, just call adBreakDone with viewed status
32
- if (isSubUser) {
33
- this.wrapAdBreadDoneViewed(params);
34
- return;
35
- }
36
-
37
28
  if (params.type === 'reward') {
38
29
  try {
39
30
  const rewardsTypes = await rewardsHelper.getRewardsTypes(this.httpClient);
40
- const rewardResult = await rewardsHelper.handleReward(rewardsTypes, params);
41
- if (!rewardResult) {
42
- throw new Error('handleReward failed: fallback to unlock failed');
43
- }
31
+ await rewardsHelper.handleReward(rewardsTypes, params);
44
32
  } catch (e) {
45
33
  console.info('handleReward failed', e);
46
34
  params.adBreakDone?.({
@@ -57,16 +45,4 @@ export default class JoliboxAdsHandler implements IAdsHandler {
57
45
  adUnit(params: IAdUnitParams): void {
58
46
  this.ads.adUnit(params);
59
47
  }
60
-
61
- private wrapAdBreadDoneViewed(params: IAdBreakParams) {
62
- params.adBreakDone?.({
63
- breakType: params.type,
64
- breakName: 'name' in params ? params.name ?? '' : '',
65
- breakFormat: params.type === 'reward' ? 'reward' : 'interstitial',
66
- breakStatus: 'viewed'
67
- });
68
- if ('adViewed' in params) {
69
- params.adViewed?.();
70
- }
71
- }
72
48
  }
@@ -1,8 +1,10 @@
1
- import { hostEmitter } from '@jolibox/common';
1
+ import { hostEmitter, InternalGlobalJSError, UserCustomError } from '@jolibox/common';
2
2
  import { taskTracker, track } from '../report';
3
3
  import { onFCP, onLCP, onTTFB } from 'web-vitals';
4
4
  import { context } from '@/common/context';
5
- import { checkSession, getUserSubStatus } from './auth';
5
+ import { httpClientManager } from '../http';
6
+ import { StandardResponse } from '@jolibox/types';
7
+ import { reportError } from '@/common/report/errors/report';
6
8
 
7
9
  function trackPerformance() {
8
10
  onFCP((metric) => {
@@ -30,11 +32,24 @@ function trackPerformance() {
30
32
  });
31
33
  }
32
34
 
35
+ async function checkSession(): Promise<boolean> {
36
+ const httpClient = httpClientManager.create();
37
+ try {
38
+ const response = await httpClient.get<StandardResponse<void>>('/api/users/info');
39
+ if (response.code !== 'SUCCESS' || !response.data) {
40
+ return false;
41
+ }
42
+ return true;
43
+ } catch (error) {
44
+ reportError(new InternalGlobalJSError(error as Error));
45
+ return false;
46
+ }
47
+ }
48
+
33
49
  function addDomContentLoaded() {
34
50
  hostEmitter.on('onDocumentReady', async (startTime: number) => {
35
51
  const isLogin = await checkSession();
36
- const isSubUser = await getUserSubStatus().catch(() => false);
37
- context.onEnvConfigChanged({ hostUserInfo: { isLogin, isSubUser } });
52
+ context.onEnvConfigChanged({ hostUserInfo: { isLogin } });
38
53
  hostEmitter.emit('LifecycleEvent.onReady', {
39
54
  ...(context.hostUserInfo ? context.hostUserInfo : { isLogin: false })
40
55
  });
@@ -13,9 +13,7 @@ import {
13
13
  IInvokePaymentEvent,
14
14
  IUseModalResultEvent,
15
15
  UseUnloginModalResultEventName,
16
- InvokeUnloginModalEventName,
17
- InvokeSubscriptionEventName,
18
- UseSubscriptionResultEventName
16
+ InvokeUnloginModalEventName
19
17
  } from '@/common/rewards/reward-emitter';
20
18
  import { notifyCustomEvent, onCustomEvent } from '@/common/utils';
21
19
  import { uuidv4 as v4 } from '@jolibox/common';
@@ -110,18 +108,3 @@ rewardsEmitter.on(InvokeUnloginModalEventName, async (type) => {
110
108
  type: type
111
109
  });
112
110
  });
113
-
114
- // subscription
115
- onCustomEvent('ON_JOLIBOX_SUB_RESULT_EVENT', (params) => {
116
- console.log('----on custom event ON_JOLIBOX_SUB_RESULT_EVENT-----', params);
117
- rewardsEmitter.emit(UseSubscriptionResultEventName, {
118
- result: params.result
119
- });
120
- });
121
-
122
- rewardsEmitter.on(InvokeSubscriptionEventName, async (type) => {
123
- console.log('----notify custom event-----', type);
124
- notifyCustomEvent('JOLIBOX_SUB_EVENT', {
125
- sequenceId: v4()
126
- });
127
- });
@@ -5,7 +5,7 @@ import { StandardResponse } from '@jolibox/types';
5
5
  import { applyNative } from '@jolibox/native-bridge';
6
6
  import { innerFetch as fetch } from '@/native/network';
7
7
  import type { PaymentResult } from './payment-helper';
8
- import { RequestCacheService, RequestAdapter } from '@jolibox/common';
8
+ import { RequestCacheService, RequestAdapter } from '@/common/cache/request-cache-service';
9
9
 
10
10
  type PaymentPurchaseType = 'JOLI_COIN' | 'JOLI_GEM';
11
11
 
package/CHANGELOG.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "name": "@jolibox/implement",
3
- "entries": [
4
- {
5
- "version": "1.1.43",
6
- "tag": "@jolibox/implement_v1.1.43",
7
- "date": "Mon, 21 Jul 2025 10:06:33 GMT",
8
- "comments": {}
9
- }
10
- ]
11
- }
package/CHANGELOG.md DELETED
@@ -1,9 +0,0 @@
1
- # Change Log - @jolibox/implement
2
-
3
- This log was last generated on Mon, 21 Jul 2025 10:06:33 GMT and should not be manually modified.
4
-
5
- ## 1.1.43
6
- Mon, 21 Jul 2025 10:06:33 GMT
7
-
8
- _Initial release_
9
-
@@ -1,7 +0,0 @@
1
- import { IHttpClient } from '@/common/http';
2
- import { IAdBreakParams } from '@/common/ads';
3
- export type SubscriptionRewardsHandler = (params: IAdBreakParams) => Promise<boolean>;
4
- export declare const createSubscriptionRewardHandler: (httpClient: IHttpClient, { onSubSuccess, onSubFailed }: {
5
- onSubSuccess?: () => void;
6
- onSubFailed?: () => void;
7
- }) => SubscriptionRewardsHandler;
@@ -1 +0,0 @@
1
- export * from './use-subscription';
@@ -1,4 +0,0 @@
1
- export declare const registerUseSubscriptionCommand: (params: {
2
- showSubscriptionModal: () => Promise<"SUCCESS" | "CANCEL" | "FAILED">;
3
- }) => void;
4
- export declare const createShowSubscriptionModal: (type: "SUBSCRIPTION") => () => Promise<"SUCCESS" | "FAILED" | "CANCEL">;
@@ -1,13 +0,0 @@
1
- import { IHttpClient } from '@/common/http';
2
- import { IAdBreakParams } from '@/common/ads';
3
- import { EventPromiseHandler } from '../event-listener';
4
- import { UnlockOptionsEventName, IUnlockOptionsEvent } from '@/common/rewards/reward-emitter';
5
- import { createShowSubscriptionModal } from './commands/use-subscription';
6
- export declare const createCommonSubscriptionHandler: (type: "SUBSCRIPTION", httpClient: IHttpClient, { handlers: { handleSubSuccess, handleSubFailed, unlockOptionsHandler, showSubscriptionModal } }: {
7
- handlers: {
8
- handleSubSuccess?: () => void;
9
- handleSubFailed?: (params: IAdBreakParams) => void;
10
- unlockOptionsHandler: EventPromiseHandler<IUnlockOptionsEvent, typeof UnlockOptionsEventName>;
11
- showSubscriptionModal: ReturnType<typeof createShowSubscriptionModal>;
12
- };
13
- }) => (params: IAdBreakParams) => Promise<boolean>;
@@ -1,2 +0,0 @@
1
- export declare function checkSession(): Promise<boolean>;
2
- export { getUserSubStatus } from './sub';
@@ -1,2 +0,0 @@
1
- export declare const resetSubState: () => void;
2
- export declare function getUserSubStatus(): Promise<boolean>;
@@ -1,34 +0,0 @@
1
- import { IHttpClient } from '@/common/http';
2
- import { IAdBreakParams } from '@/common/ads';
3
- import { unlockOptionsHandler } from './utils/common';
4
- import { createCommonSubscriptionHandler } from './utils/subscription/sub-handler';
5
- import { createShowSubscriptionModal } from './utils/subscription/commands/use-subscription';
6
-
7
- export type SubscriptionRewardsHandler = (params: IAdBreakParams) => Promise<boolean>;
8
- export const createSubscriptionRewardHandler = (
9
- httpClient: IHttpClient,
10
- {
11
- onSubSuccess,
12
- onSubFailed
13
- }: {
14
- onSubSuccess?: () => void;
15
- onSubFailed?: () => void;
16
- }
17
- ): SubscriptionRewardsHandler => {
18
- const handleUnlockFailed = () => {
19
- onSubFailed?.();
20
- };
21
-
22
- const showSubscriptionModal = createShowSubscriptionModal('SUBSCRIPTION');
23
-
24
- const subscriptionRewardHandler = createCommonSubscriptionHandler('SUBSCRIPTION', httpClient, {
25
- handlers: {
26
- handleSubSuccess: onSubSuccess,
27
- handleSubFailed: handleUnlockFailed,
28
- unlockOptionsHandler,
29
- showSubscriptionModal
30
- }
31
- });
32
-
33
- return subscriptionRewardHandler;
34
- };
@@ -1 +0,0 @@
1
- export * from './use-subscription';
@@ -1,29 +0,0 @@
1
- import { rewardsCommands } from '../../coins/rewards-command';
2
- import { createEventPromiseHandler } from '../../event-listener';
3
- import { IUseSubscriptionResultEvent, UseSubscriptionResultEventName } from '@/common/rewards/reward-emitter';
4
- import { rewardsEmitter, InvokeSubscriptionEventName } from '@/common/rewards/reward-emitter';
5
-
6
- export const registerUseSubscriptionCommand = (params: {
7
- showSubscriptionModal: () => Promise<'SUCCESS' | 'CANCEL' | 'FAILED'>;
8
- }) => {
9
- const { showSubscriptionModal } = params;
10
- rewardsCommands.registerCommand(`Rewards.subscription`, async () => {
11
- const subscriptionResult = await showSubscriptionModal();
12
- return {
13
- result: subscriptionResult === 'SUCCESS' ? 'SUCCESS' : 'FAILED'
14
- };
15
- });
16
- };
17
-
18
- // EVENTS
19
- export const createShowSubscriptionModal = (type: 'SUBSCRIPTION') => {
20
- return async () => {
21
- const modalHandler = createEventPromiseHandler<
22
- IUseSubscriptionResultEvent,
23
- typeof UseSubscriptionResultEventName
24
- >(rewardsEmitter, UseSubscriptionResultEventName);
25
- rewardsEmitter.emit(InvokeSubscriptionEventName, type);
26
- const modalResult = await modalHandler.getFreshData();
27
- return modalResult.result;
28
- };
29
- };
@@ -1,88 +0,0 @@
1
- import { IHttpClient } from '@/common/http';
2
- import { IAdBreakParams } from '@/common/ads';
3
- import { EventPromiseHandler } from '../event-listener';
4
- import { UnlockOptionsEventName, IUnlockOptionsEvent } from '@/common/rewards/reward-emitter';
5
- import { rewardsCommands } from '../coins/rewards-command';
6
- import { RewardsCommandType } from '@jolibox/types';
7
- import { createShowSubscriptionModal } from './commands/use-subscription';
8
- import { registerUseSubscriptionCommand } from './commands';
9
- import { context } from '@/common/context';
10
-
11
- export const createCommonSubscriptionHandler = (
12
- type: 'SUBSCRIPTION',
13
- httpClient: IHttpClient,
14
- {
15
- handlers: { handleSubSuccess, handleSubFailed, unlockOptionsHandler, showSubscriptionModal }
16
- }: {
17
- handlers: {
18
- handleSubSuccess?: () => void;
19
- handleSubFailed?: (params: IAdBreakParams) => void;
20
- unlockOptionsHandler: EventPromiseHandler<IUnlockOptionsEvent, typeof UnlockOptionsEventName>;
21
- showSubscriptionModal: ReturnType<typeof createShowSubscriptionModal>;
22
- };
23
- }
24
- ) => {
25
- // register commands
26
- registerUseSubscriptionCommand({
27
- showSubscriptionModal: showSubscriptionModal
28
- });
29
-
30
- const commands = [`Rewards.subscription`];
31
-
32
- return async (params: IAdBreakParams) => {
33
- try {
34
- let result = true;
35
- for (const command of commands) {
36
- const commandResult = await rewardsCommands.executeCommand(command as RewardsCommandType);
37
- result = result && commandResult.result !== 'FAILED';
38
- if (commandResult.result !== 'CONTINUE') {
39
- break;
40
- }
41
- }
42
-
43
- if (!result) {
44
- handleSubFailed?.(params);
45
- return false;
46
- }
47
-
48
- console.log('-----unlockWithSubscription result-----');
49
- if (result) {
50
- try {
51
- params.adBreakDone?.({
52
- breakType: params.type,
53
- breakName: 'name' in params ? params.name ?? '' : '',
54
- breakFormat: params.type === 'reward' ? 'reward' : 'interstitial',
55
- breakStatus: 'viewed'
56
- });
57
- if ('adViewed' in params) {
58
- params.adViewed?.();
59
- }
60
-
61
- context.onEnvConfigChanged?.({
62
- hostUserInfo: {
63
- ...(context.hostUserInfo ?? { isLogin: false }),
64
- isSubUser: true
65
- }
66
- });
67
- } catch (e) {
68
- console.error('-----unlockWithSubscription adBreakDone error-----', e);
69
- }
70
-
71
- handleSubSuccess?.();
72
- return true;
73
- }
74
- handleSubFailed?.(params);
75
- return false;
76
- } catch (e) {
77
- console.info(`SubscriptionRewardHandler error:`, e);
78
- if (e instanceof Error && e.message == 'CANCEL') {
79
- // Cancel should terminate the reward handler, invoke unlock failed
80
- throw e;
81
- }
82
- handleSubFailed?.(params);
83
- return false;
84
- } finally {
85
- unlockOptionsHandler.clearCache();
86
- }
87
- };
88
- };
@@ -1,308 +0,0 @@
1
- // Mock dependencies first
2
- const mockOnCustomEvent = jest.fn();
3
- const mockNotifyCustomEvent = jest.fn();
4
- const mockUuidv4 = jest.fn();
5
-
6
- jest.mock('@jolibox/common', () => ({
7
- uuidv4: mockUuidv4,
8
- EventEmitter: jest.fn().mockImplementation(() => ({
9
- emit: jest.fn(),
10
- on: jest.fn(),
11
- off: jest.fn(),
12
- once: jest.fn(),
13
- removeAllListeners: jest.fn()
14
- })),
15
- InternalGlobalJSError: jest.fn().mockImplementation((error) => error)
16
- }));
17
-
18
- jest.mock('@/common/utils', () => ({
19
- onCustomEvent: mockOnCustomEvent,
20
- notifyCustomEvent: mockNotifyCustomEvent
21
- }));
22
-
23
- jest.mock('@/common/report/errors/report', () => ({
24
- reportError: jest.fn()
25
- }));
26
-
27
- import { getUserSubStatus, resetSubState } from '../sub';
28
-
29
- // Define event data interface
30
- interface EventData {
31
- sequenceId: string;
32
- isSubUser: boolean;
33
- }
34
-
35
- describe('getUserSubStatus', () => {
36
- beforeEach(() => {
37
- jest.clearAllMocks();
38
- jest.clearAllTimers();
39
- jest.useRealTimers(); // Use real timers to avoid timing issues
40
-
41
- // Reset module state between tests
42
- resetSubState();
43
- });
44
-
45
- afterEach(() => {
46
- jest.restoreAllMocks();
47
- });
48
-
49
- it('should resolve with correct value when event is received', async () => {
50
- let eventHandler: ((data: EventData) => void) | null = null;
51
- mockOnCustomEvent.mockImplementation((eventName: string, handler: (data: EventData) => void) => {
52
- if (eventName === 'ON_GET_USER_SUB_STATUS') {
53
- eventHandler = handler;
54
- }
55
- return jest.fn();
56
- });
57
-
58
- mockUuidv4.mockReturnValue('test-sequence-id');
59
-
60
- const promise = getUserSubStatus();
61
-
62
- // Verify notification was sent
63
- expect(mockNotifyCustomEvent).toHaveBeenCalledWith('JOLIBOX_GET_USER_SUB_STATUS', {
64
- sequenceId: 'test-sequence-id'
65
- });
66
-
67
- // Simulate response immediately
68
- if (eventHandler) {
69
- (eventHandler as any)({
70
- sequenceId: 'test-sequence-id',
71
- isSubUser: true
72
- });
73
- }
74
-
75
- const result = await promise;
76
- expect(result).toBe(true);
77
- });
78
-
79
- it('should resolve with false when isSubUser is false', async () => {
80
- let eventHandler: ((data: EventData) => void) | null = null;
81
- mockOnCustomEvent.mockImplementation((eventName: string, handler: (data: EventData) => void) => {
82
- if (eventName === 'ON_GET_USER_SUB_STATUS') {
83
- eventHandler = handler;
84
- }
85
- return jest.fn();
86
- });
87
-
88
- mockUuidv4.mockReturnValue('test-sequence-id');
89
-
90
- const promise = getUserSubStatus();
91
-
92
- // Simulate response with false
93
- if (eventHandler) {
94
- (eventHandler as any)({
95
- sequenceId: 'test-sequence-id',
96
- isSubUser: false
97
- });
98
- }
99
-
100
- const result = await promise;
101
- expect(result).toBe(false);
102
- });
103
-
104
- it('should reject on timeout', async () => {
105
- mockOnCustomEvent.mockImplementation(() => jest.fn());
106
- mockUuidv4.mockReturnValue('timeout-test-id');
107
-
108
- const promise = getUserSubStatus();
109
-
110
- // Fast forward time to trigger timeout
111
- jest.advanceTimersByTime(3000);
112
-
113
- await expect(promise).rejects.toThrow('Timeout waiting for user sub status response');
114
- });
115
-
116
- it('should initialize event listener only once', async () => {
117
- let eventHandler: ((data: EventData) => void) | null = null;
118
- mockOnCustomEvent.mockImplementation((eventName: string, handler: (data: EventData) => void) => {
119
- if (eventName === 'ON_GET_USER_SUB_STATUS') {
120
- eventHandler = handler;
121
- }
122
- return jest.fn();
123
- });
124
-
125
- // Mock different UUIDs for different calls
126
- mockUuidv4.mockReturnValueOnce('uuid-1').mockReturnValueOnce('uuid-2');
127
-
128
- // First call
129
- const promise1 = getUserSubStatus();
130
-
131
- // Second call
132
- const promise2 = getUserSubStatus();
133
-
134
- // Should only initialize listener once
135
- expect(mockOnCustomEvent).toHaveBeenCalledTimes(1);
136
- expect(mockOnCustomEvent).toHaveBeenCalledWith('ON_GET_USER_SUB_STATUS', expect.any(Function));
137
-
138
- // Should send notification for both calls
139
- expect(mockNotifyCustomEvent).toHaveBeenCalledTimes(2);
140
-
141
- // Simulate responses with different sequence IDs
142
- if (eventHandler) {
143
- (eventHandler as any)({ sequenceId: 'uuid-1', isSubUser: true });
144
- (eventHandler as any)({ sequenceId: 'uuid-2', isSubUser: false });
145
- }
146
-
147
- const [result1, result2] = await Promise.all([promise1, promise2]);
148
- expect(result1).toBe(true);
149
- expect(result2).toBe(false);
150
- });
151
-
152
- it('should ignore responses with different sequenceId', async () => {
153
- let eventHandler: ((data: EventData) => void) | null = null;
154
- mockOnCustomEvent.mockImplementation((eventName: string, handler: (data: EventData) => void) => {
155
- if (eventName === 'ON_GET_USER_SUB_STATUS') {
156
- eventHandler = handler;
157
- }
158
- return jest.fn();
159
- });
160
-
161
- mockUuidv4.mockReturnValue('correct-sequence-id');
162
-
163
- const promise = getUserSubStatus();
164
-
165
- // Simulate response with wrong sequenceId first
166
- if (eventHandler) {
167
- (eventHandler as any)({
168
- sequenceId: 'wrong-sequence-id',
169
- isSubUser: true
170
- });
171
- }
172
-
173
- // Promise should still be pending after wrong sequenceId
174
- let isResolved = false;
175
- let promiseResult: any;
176
- promise
177
- .then((result) => {
178
- isResolved = true;
179
- promiseResult = result;
180
- })
181
- .catch(() => {
182
- isResolved = true;
183
- });
184
-
185
- // Flush microtasks
186
- await Promise.resolve();
187
- expect(isResolved).toBe(false);
188
-
189
- // Now send correct response
190
- if (eventHandler) {
191
- (eventHandler as any)({
192
- sequenceId: 'correct-sequence-id',
193
- isSubUser: true
194
- });
195
- }
196
-
197
- await promise;
198
- expect(promiseResult).toBe(true);
199
- });
200
-
201
- it('should handle multiple concurrent requests with different sequenceIds', async () => {
202
- let eventHandler: ((data: EventData) => void) | null = null;
203
- mockOnCustomEvent.mockImplementation((eventName: string, handler: (data: EventData) => void) => {
204
- if (eventName === 'ON_GET_USER_SUB_STATUS') {
205
- eventHandler = handler;
206
- }
207
- return jest.fn();
208
- });
209
-
210
- // Mock different UUIDs for different calls
211
- mockUuidv4
212
- .mockReturnValueOnce('request-1')
213
- .mockReturnValueOnce('request-2')
214
- .mockReturnValueOnce('request-3');
215
-
216
- const promise1 = getUserSubStatus();
217
- const promise2 = getUserSubStatus();
218
- const promise3 = getUserSubStatus();
219
-
220
- // Should initialize listener only once
221
- expect(mockOnCustomEvent).toHaveBeenCalledTimes(1);
222
-
223
- // Should send three notifications
224
- expect(mockNotifyCustomEvent).toHaveBeenCalledTimes(3);
225
- expect(mockNotifyCustomEvent).toHaveBeenNthCalledWith(1, 'JOLIBOX_GET_USER_SUB_STATUS', {
226
- sequenceId: 'request-1'
227
- });
228
- expect(mockNotifyCustomEvent).toHaveBeenNthCalledWith(2, 'JOLIBOX_GET_USER_SUB_STATUS', {
229
- sequenceId: 'request-2'
230
- });
231
- expect(mockNotifyCustomEvent).toHaveBeenNthCalledWith(3, 'JOLIBOX_GET_USER_SUB_STATUS', {
232
- sequenceId: 'request-3'
233
- });
234
-
235
- // Simulate responses in reverse order
236
- if (eventHandler) {
237
- (eventHandler as any)({ sequenceId: 'request-3', isSubUser: false });
238
- (eventHandler as any)({ sequenceId: 'request-1', isSubUser: true });
239
- (eventHandler as any)({ sequenceId: 'request-2', isSubUser: true });
240
- }
241
-
242
- const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3]);
243
- expect(result1).toBe(true);
244
- expect(result2).toBe(true);
245
- expect(result3).toBe(false);
246
- });
247
-
248
- it('should clean up timeout when response is received', async () => {
249
- let eventHandler: ((data: EventData) => void) | null = null;
250
- mockOnCustomEvent.mockImplementation((eventName: string, handler: (data: EventData) => void) => {
251
- if (eventName === 'ON_GET_USER_SUB_STATUS') {
252
- eventHandler = handler;
253
- }
254
- return jest.fn();
255
- });
256
-
257
- mockUuidv4.mockReturnValue('cleanup-test-id');
258
-
259
- // Spy on clearTimeout
260
- const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
261
-
262
- const promise = getUserSubStatus();
263
-
264
- // Simulate response before timeout
265
- if (eventHandler) {
266
- (eventHandler as any)({
267
- sequenceId: 'cleanup-test-id',
268
- isSubUser: true
269
- });
270
- }
271
-
272
- await promise;
273
-
274
- // Verify timeout was cleared
275
- expect(clearTimeoutSpy).toHaveBeenCalled();
276
-
277
- clearTimeoutSpy.mockRestore();
278
- });
279
-
280
- it('should not resolve after timeout even if response comes later', async () => {
281
- let eventHandler: ((data: EventData) => void) | null = null;
282
- mockOnCustomEvent.mockImplementation((eventName: string, handler: (data: EventData) => void) => {
283
- if (eventName === 'ON_GET_USER_SUB_STATUS') {
284
- eventHandler = handler;
285
- }
286
- return jest.fn();
287
- });
288
-
289
- mockUuidv4.mockReturnValue('late-response-id');
290
-
291
- const promise = getUserSubStatus();
292
-
293
- // Trigger timeout first
294
- jest.advanceTimersByTime(3000);
295
-
296
- await expect(promise).rejects.toThrow('Timeout waiting for user sub status response');
297
-
298
- // Try to send response after timeout
299
- if (eventHandler) {
300
- (eventHandler as any)({
301
- sequenceId: 'late-response-id',
302
- isSubUser: true
303
- });
304
- }
305
-
306
- // Promise should already be rejected, no additional effects
307
- });
308
- });
@@ -1,20 +0,0 @@
1
- import { httpClientManager } from '@/h5/http';
2
- import { StandardResponse } from '@jolibox/types';
3
- import { reportError } from '@/common/report/errors/report';
4
- import { InternalGlobalJSError } from '@jolibox/common';
5
-
6
- export async function checkSession(): Promise<boolean> {
7
- const httpClient = httpClientManager.create();
8
- try {
9
- const response = await httpClient.get<StandardResponse<void>>('/api/users/info');
10
- if (response.code !== 'SUCCESS' || !response.data) {
11
- return false;
12
- }
13
- return true;
14
- } catch (error) {
15
- reportError(new InternalGlobalJSError(error as Error));
16
- return false;
17
- }
18
- }
19
-
20
- export { getUserSubStatus } from './sub';