@jolibox/implement 1.1.4-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 (195) hide show
  1. package/.eslintrc.js +13 -0
  2. package/.rush/temp/package-deps_build.json +105 -0
  3. package/.rush/temp/shrinkwrap-deps.json +79 -0
  4. package/README.md +1 -0
  5. package/dist/common/api-factory/index.d.ts +21 -0
  6. package/dist/common/api-factory/validator/__tests__/validate/any.test.d.ts +1 -0
  7. package/dist/common/api-factory/validator/__tests__/validate/array.test.d.ts +1 -0
  8. package/dist/common/api-factory/validator/__tests__/validate/arraybuffer.test.d.ts +1 -0
  9. package/dist/common/api-factory/validator/__tests__/validate/boolean.test.d.ts +1 -0
  10. package/dist/common/api-factory/validator/__tests__/validate/enum.test.d.ts +1 -0
  11. package/dist/common/api-factory/validator/__tests__/validate/function.test.d.ts +1 -0
  12. package/dist/common/api-factory/validator/__tests__/validate/literal.test.d.ts +1 -0
  13. package/dist/common/api-factory/validator/__tests__/validate/nullish.test.d.ts +1 -0
  14. package/dist/common/api-factory/validator/__tests__/validate/number.test.d.ts +1 -0
  15. package/dist/common/api-factory/validator/__tests__/validate/object.test.d.ts +1 -0
  16. package/dist/common/api-factory/validator/__tests__/validate/or.test.d.ts +1 -0
  17. package/dist/common/api-factory/validator/__tests__/validate/record.test.d.ts +1 -0
  18. package/dist/common/api-factory/validator/__tests__/validate/string.test.d.ts +1 -0
  19. package/dist/common/api-factory/validator/__tests__/validate/symbol.test.d.ts +1 -0
  20. package/dist/common/api-factory/validator/__tests__/validate/tuple.test.d.ts +1 -0
  21. package/dist/common/api-factory/validator/__tests__/validate/type-asserts.test.d.ts +1 -0
  22. package/dist/common/api-factory/validator/__tests__/validate/utils.test.d.ts +1 -0
  23. package/dist/common/api-factory/validator/index.d.ts +29 -0
  24. package/dist/common/api-factory/validator/validate.d.ts +119 -0
  25. package/dist/common/can-i-use.d.ts +2 -0
  26. package/dist/common/context/index.d.ts +16 -0
  27. package/dist/common/context/types.d.ts +5 -0
  28. package/dist/common/context/url-parse.d.ts +22 -0
  29. package/dist/common/http/index.d.ts +13 -0
  30. package/dist/common/http/uuid.d.ts +2 -0
  31. package/dist/common/http/xua.d.ts +17 -0
  32. package/dist/common/report/base-tracker.d.ts +13 -0
  33. package/dist/common/report/create-trace.d.ts +7 -0
  34. package/dist/common/report/errors/error-types.d.ts +122 -0
  35. package/dist/common/report/errors/index.d.ts +13 -0
  36. package/dist/common/report/errors/report/index.d.ts +51 -0
  37. package/dist/common/report/errors/report/listeners.d.ts +1 -0
  38. package/dist/common/report/index.d.ts +3 -0
  39. package/dist/common/report/task-track/index.d.ts +25 -0
  40. package/dist/common/report/track.d.ts +3 -0
  41. package/dist/common/report/types.d.ts +75 -0
  42. package/dist/h5/ads/ads-action-detection.d.ts +6 -0
  43. package/dist/h5/ads/anti-cheating.d.ts +61 -0
  44. package/dist/h5/ads/index.d.ts +275 -0
  45. package/dist/h5/api/base.d.ts +13 -0
  46. package/dist/h5/api/get-system-info.d.ts +1 -0
  47. package/dist/h5/api/index.d.ts +4 -0
  48. package/dist/h5/api/lifecycle.d.ts +1 -0
  49. package/dist/h5/api/storage.d.ts +27 -0
  50. package/dist/h5/api/task.d.ts +1 -0
  51. package/dist/h5/bootstrap/index.d.ts +1 -0
  52. package/dist/h5/http/index.d.ts +33 -0
  53. package/dist/h5/http/utils/__tests__/uuid.test.d.ts +1 -0
  54. package/dist/h5/http/utils/__tests__/xua.test.d.ts +1 -0
  55. package/dist/h5/http/utils/index.d.ts +14 -0
  56. package/dist/h5/http/utils/session.d.ts +1 -0
  57. package/dist/h5/report/errors/index.d.ts +4 -0
  58. package/dist/h5/report/event-tracker.d.ts +8 -0
  59. package/dist/h5/report/index.d.ts +10 -0
  60. package/dist/h5/report/task-tracker.d.ts +18 -0
  61. package/dist/index.d.ts +3 -0
  62. package/dist/index.js +13 -0
  63. package/dist/index.native.d.ts +2 -0
  64. package/dist/index.native.js +3 -0
  65. package/dist/native/api/ads.d.ts +1 -0
  66. package/dist/native/api/base.d.ts +13 -0
  67. package/dist/native/api/get-system-info.d.ts +1 -0
  68. package/dist/native/api/index.d.ts +8 -0
  69. package/dist/native/api/keyboard.d.ts +9 -0
  70. package/dist/native/api/lifecycle.d.ts +1 -0
  71. package/dist/native/api/login.d.ts +1 -0
  72. package/dist/native/api/request.d.ts +1 -0
  73. package/dist/native/api/storage.d.ts +25 -0
  74. package/dist/native/api/task.d.ts +1 -0
  75. package/dist/native/bootstrap/bridge.d.ts +4 -0
  76. package/dist/native/bootstrap/index.d.ts +1 -0
  77. package/dist/native/js-bridge/const.d.ts +5 -0
  78. package/dist/native/js-bridge/index.d.ts +2 -0
  79. package/dist/native/js-bridge/invoke.d.ts +21 -0
  80. package/dist/native/js-bridge/js-bridge.d.ts +6 -0
  81. package/dist/native/js-bridge/report.d.ts +63 -0
  82. package/dist/native/js-bridge/subscribe.d.ts +8 -0
  83. package/dist/native/js-bridge/types.d.ts +14 -0
  84. package/dist/native/js-bridge/utils.d.ts +17 -0
  85. package/dist/native/js-core/index.d.ts +3 -0
  86. package/dist/native/js-core/jolibox-js-core.d.ts +45 -0
  87. package/dist/native/js-core/message-port.d.ts +12 -0
  88. package/dist/native/js-core/utils.d.ts +7 -0
  89. package/dist/native/network/create-fetch.d.ts +27 -0
  90. package/dist/native/network/index.d.ts +11 -0
  91. package/dist/native/network/report.d.ts +15 -0
  92. package/dist/native/network/types.d.ts +61 -0
  93. package/dist/native/network/utils.d.ts +9 -0
  94. package/dist/native/report/errors/index.d.ts +4 -0
  95. package/dist/native/report/index.d.ts +10 -0
  96. package/dist/native/report/task-tracker.d.ts +24 -0
  97. package/dist/utils/index.d.ts +0 -0
  98. package/esbuild.config.js +66 -0
  99. package/implement.build.log +9 -0
  100. package/package.json +30 -0
  101. package/src/common/api-factory/index.ts +188 -0
  102. package/src/common/api-factory/validator/__tests__/validate/any.test.ts +68 -0
  103. package/src/common/api-factory/validator/__tests__/validate/array.test.ts +402 -0
  104. package/src/common/api-factory/validator/__tests__/validate/arraybuffer.test.ts +48 -0
  105. package/src/common/api-factory/validator/__tests__/validate/boolean.test.ts +27 -0
  106. package/src/common/api-factory/validator/__tests__/validate/enum.test.ts +106 -0
  107. package/src/common/api-factory/validator/__tests__/validate/function.test.ts +54 -0
  108. package/src/common/api-factory/validator/__tests__/validate/literal.test.ts +130 -0
  109. package/src/common/api-factory/validator/__tests__/validate/nullish.test.ts +41 -0
  110. package/src/common/api-factory/validator/__tests__/validate/number.test.ts +147 -0
  111. package/src/common/api-factory/validator/__tests__/validate/object.test.ts +131 -0
  112. package/src/common/api-factory/validator/__tests__/validate/or.test.ts +96 -0
  113. package/src/common/api-factory/validator/__tests__/validate/record.test.ts +274 -0
  114. package/src/common/api-factory/validator/__tests__/validate/string.test.ts +187 -0
  115. package/src/common/api-factory/validator/__tests__/validate/symbol.test.ts +23 -0
  116. package/src/common/api-factory/validator/__tests__/validate/tuple.test.ts +86 -0
  117. package/src/common/api-factory/validator/__tests__/validate/type-asserts.test.ts +13 -0
  118. package/src/common/api-factory/validator/__tests__/validate/utils.test.ts +44 -0
  119. package/src/common/api-factory/validator/index.ts +107 -0
  120. package/src/common/api-factory/validator/validate.ts +641 -0
  121. package/src/common/can-i-use.ts +19 -0
  122. package/src/common/context/index.ts +85 -0
  123. package/src/common/context/types.ts +5 -0
  124. package/src/common/context/url-parse.ts +63 -0
  125. package/src/common/http/index.ts +29 -0
  126. package/src/common/http/uuid.ts +11 -0
  127. package/src/common/http/xua.ts +79 -0
  128. package/src/common/report/base-tracker.ts +134 -0
  129. package/src/common/report/create-trace.ts +17 -0
  130. package/src/common/report/errors/error-types.ts +206 -0
  131. package/src/common/report/errors/index.ts +20 -0
  132. package/src/common/report/errors/report/index.ts +63 -0
  133. package/src/common/report/errors/report/listeners.ts +80 -0
  134. package/src/common/report/index.ts +3 -0
  135. package/src/common/report/task-track/index.ts +102 -0
  136. package/src/common/report/track.ts +49 -0
  137. package/src/common/report/types.ts +90 -0
  138. package/src/h5/ads/ads-action-detection.ts +31 -0
  139. package/src/h5/ads/anti-cheating.ts +244 -0
  140. package/src/h5/ads/index.ts +658 -0
  141. package/src/h5/api/base.ts +9 -0
  142. package/src/h5/api/get-system-info.ts +59 -0
  143. package/src/h5/api/index.ts +4 -0
  144. package/src/h5/api/lifecycle.ts +95 -0
  145. package/src/h5/api/storage.ts +173 -0
  146. package/src/h5/api/task.ts +190 -0
  147. package/src/h5/bootstrap/index.ts +16 -0
  148. package/src/h5/http/index.ts +189 -0
  149. package/src/h5/http/utils/__tests__/uuid.test.ts +16 -0
  150. package/src/h5/http/utils/__tests__/xua.test.ts +27 -0
  151. package/src/h5/http/utils/index.ts +19 -0
  152. package/src/h5/http/utils/session.ts +10 -0
  153. package/src/h5/report/errors/index.ts +40 -0
  154. package/src/h5/report/event-tracker.ts +40 -0
  155. package/src/h5/report/index.ts +56 -0
  156. package/src/h5/report/task-tracker.ts +42 -0
  157. package/src/index.native.ts +7 -0
  158. package/src/index.ts +9 -0
  159. package/src/native/api/ads.ts +52 -0
  160. package/src/native/api/base.ts +8 -0
  161. package/src/native/api/get-system-info.ts +44 -0
  162. package/src/native/api/index.ts +8 -0
  163. package/src/native/api/keyboard.ts +75 -0
  164. package/src/native/api/lifecycle.ts +76 -0
  165. package/src/native/api/login.ts +73 -0
  166. package/src/native/api/request.ts +154 -0
  167. package/src/native/api/storage.ts +287 -0
  168. package/src/native/api/task.ts +227 -0
  169. package/src/native/bootstrap/bridge.ts +59 -0
  170. package/src/native/bootstrap/index.ts +59 -0
  171. package/src/native/js-bridge/const.ts +11 -0
  172. package/src/native/js-bridge/index.ts +2 -0
  173. package/src/native/js-bridge/invoke.ts +210 -0
  174. package/src/native/js-bridge/js-bridge.ts +23 -0
  175. package/src/native/js-bridge/report.ts +311 -0
  176. package/src/native/js-bridge/subscribe.ts +50 -0
  177. package/src/native/js-bridge/types.ts +26 -0
  178. package/src/native/js-bridge/utils.ts +116 -0
  179. package/src/native/js-core/index.ts +4 -0
  180. package/src/native/js-core/jolibox-js-core.ts +188 -0
  181. package/src/native/js-core/message-port.ts +52 -0
  182. package/src/native/js-core/utils.ts +9 -0
  183. package/src/native/network/create-fetch.ts +237 -0
  184. package/src/native/network/index.ts +15 -0
  185. package/src/native/network/report.ts +58 -0
  186. package/src/native/network/types.ts +77 -0
  187. package/src/native/network/utils.ts +90 -0
  188. package/src/native/report/errors/index.ts +27 -0
  189. package/src/native/report/index.ts +51 -0
  190. package/src/native/report/task-tracker.ts +72 -0
  191. package/src/native/types/global.d.ts +26 -0
  192. package/src/native/types/native-method-map.d.ts +282 -0
  193. package/src/native/types/native-method.d.ts +30 -0
  194. package/src/utils/index.ts +0 -0
  195. package/tsconfig.json +18 -0
@@ -0,0 +1,52 @@
1
+ import { normalizeParams } from './utils';
2
+ import { AnyFunction } from '@jolibox/types';
3
+
4
+ declare const globalThis: {
5
+ onmessage?: (msg: MessageEvent) => void;
6
+ ttJSBridge: {
7
+ invokeHandler: AnyFunction;
8
+ subscribeHandler: AnyFunction;
9
+ };
10
+ };
11
+
12
+ interface MessageEvent {
13
+ data: string;
14
+ ports: MessagePort[];
15
+ }
16
+
17
+ interface MessagePort {
18
+ onmessage: (msg: MessageEvent) => void;
19
+ postMessage: (msg: string) => void;
20
+ publish: (event: string, data: Record<string, unknown>) => void;
21
+ }
22
+
23
+ type PublishEvent = ['publish', string, string];
24
+ type InvokeEvent = ['invoke', number, string];
25
+
26
+ export let messagePort: MessagePort | undefined;
27
+
28
+ export function initMessagePort() {
29
+ globalThis.onmessage = function (msg) {
30
+ if (msg.data !== 'msg:setup') {
31
+ return;
32
+ }
33
+
34
+ [messagePort] = msg.ports;
35
+ messagePort.onmessage = function (message) {
36
+ const data: PublishEvent | InvokeEvent = JSON.parse(message.data);
37
+ if (data[0] === 'publish') {
38
+ globalThis.ttJSBridge.subscribeHandler(data[1], data[2]);
39
+ } else if (data[0] === 'invoke') {
40
+ globalThis.ttJSBridge.invokeHandler(data[1], data[2]);
41
+ }
42
+ };
43
+
44
+ messagePort.publish = function (event, params) {
45
+ const data = normalizeParams(params as Record<string, unknown>, 'messagePort');
46
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
47
+ messagePort!.postMessage(JSON.stringify(['publish', event, JSON.stringify(data)]));
48
+ };
49
+
50
+ messagePort.postMessage('msg:setup:ok');
51
+ };
52
+ }
@@ -0,0 +1,9 @@
1
+ export function normalizeParams(params: Record<string, unknown>, type: string) {
2
+ return {
3
+ params,
4
+ __extra: {
5
+ startTime: Date.now(),
6
+ type
7
+ }
8
+ };
9
+ }
@@ -0,0 +1,237 @@
1
+ import { isString, logger, Deferred, isObject, hostEmitter } from '@jolibox/common';
2
+ import { reportNetworkAPI } from './report';
3
+ import { RequestFrom } from './types';
4
+ import { FetchResponse, FetchOptions } from './types';
5
+ import { invokeNative, onNative } from '@/native/bootstrap/bridge';
6
+ import { AnyFunction } from '@jolibox/types';
7
+ import { reportError } from '@/common/report/errors/report';
8
+ import { createAPIError } from '@/common/report/errors';
9
+
10
+ type Fetch = <T>(
11
+ url: string,
12
+ innerOptions?: FetchOptions & {
13
+ baseUrl?: string;
14
+ }
15
+ ) => Promise<{
16
+ url: string;
17
+ timeout?: number;
18
+ method: string;
19
+ response: FetchResponse<T>;
20
+ }>;
21
+
22
+ type PublicFetch = <T>(
23
+ url: string,
24
+ options?: FetchOptions
25
+ ) => Promise<{
26
+ url: string;
27
+ timeout?: number;
28
+ method: string;
29
+ response: FetchResponse<T>;
30
+ }>;
31
+
32
+ const processUrl = (url: string, options?: FetchOptions & { baseUrl?: string }): string => {
33
+ if (/^https?:\/\//.test(url)) {
34
+ return url;
35
+ }
36
+ const query = options?.query;
37
+ const searchParams = new URLSearchParams(query);
38
+ const search = searchParams.toString();
39
+ return options?.baseUrl
40
+ ? `${options.baseUrl}${url}${search ? `?${search}` : ''}`
41
+ : `${url}${search ? `?${search}` : ''}`;
42
+ };
43
+
44
+ export function createFetch(
45
+ createMethod: 'createRequestTaskSync',
46
+ operateMethod: 'operateRequestTaskSync',
47
+ options?: {
48
+ baseUrl?: string;
49
+ type: 'public';
50
+ }
51
+ ): PublicFetch;
52
+
53
+ export function createFetch(
54
+ createMethod: 'createRequestTaskSync',
55
+ operateMethod: 'operateRequestTaskSync',
56
+ options?: {
57
+ baseUrl?: string;
58
+ type: 'inner';
59
+ defaultHeaders?: Record<string, string>;
60
+ }
61
+ ): Fetch;
62
+
63
+ export function createFetch(
64
+ createMethod: 'createRequestTaskSync',
65
+ operateMethod: 'operateRequestTaskSync',
66
+ options?: {
67
+ baseUrl?: string;
68
+ type: 'inner' | 'public';
69
+ defaultHeaders?: Record<string, string>;
70
+ }
71
+ ) {
72
+ const promiseMap = new Map<string, { resolve: AnyFunction; reject: AnyFunction }>();
73
+ const baseUrl = options?.baseUrl ?? `https://api.jolibox.com`;
74
+ const type = options?.type ?? 'public';
75
+ const defaultHeaders = options?.defaultHeaders ?? {};
76
+
77
+ onNative('onRequestTaskStateChange', (res) => {
78
+ if (typeof res.requestTaskId !== 'string') return;
79
+ const task = promiseMap.get(res.requestTaskId);
80
+ if (!task) return;
81
+
82
+ if (res.state === 'success') {
83
+ const { requestTaskId, state, ...rest } = res;
84
+ task.resolve(rest);
85
+ promiseMap.delete(requestTaskId);
86
+ } else if (res.state === 'fail') {
87
+ const { requestTaskId, state, ...rest } = res;
88
+ task.reject(rest);
89
+ promiseMap.delete(requestTaskId);
90
+ } else {
91
+ logger.warn(`onRequestTaskStateChange unknown event`, res);
92
+ }
93
+ });
94
+
95
+ hostEmitter.on('onLoginComplete', ({ isLogin, token }) => {
96
+ if (isLogin && token) {
97
+ defaultHeaders['X-JOLI-TOKEN'] = token;
98
+ }
99
+ });
100
+
101
+ const fetch: Fetch = async (originUrl, options = {}) => {
102
+ const startTime = Date.now();
103
+
104
+ const url = processUrl(originUrl, { ...options, baseUrl });
105
+ const {
106
+ method = 'GET',
107
+ responseType = 'text',
108
+ dataType = 'json',
109
+ timeout,
110
+ enableCache = false,
111
+ appendHostCookie = true
112
+ } = options;
113
+
114
+ const header = Object.assign({ 'content-type': 'application/json' }, defaultHeaders, options.header);
115
+
116
+ // modify params
117
+ const params = {
118
+ url,
119
+ method,
120
+ header,
121
+ enableCache,
122
+ responseType,
123
+ appendHostCookie
124
+ } as any;
125
+
126
+ if (method == 'POST' || method == 'PUT') {
127
+ const data = normalizeData(options.data, header['content-type']);
128
+ params['data'] = data;
129
+ }
130
+
131
+ if (timeout) {
132
+ Object.assign(params, { timeout });
133
+ }
134
+ const {
135
+ data: { requestTaskId }
136
+ } = invokeNative(createMethod, params);
137
+
138
+ if (typeof requestTaskId !== 'string') {
139
+ throw createAPIError({
140
+ code: -1,
141
+ msg: 'requestTaskId is not a string'
142
+ });
143
+ }
144
+
145
+ const report = (state: 'success' | 'fail', response: unknown) => {
146
+ reportNetworkAPI('request', url, {
147
+ state,
148
+ startTime,
149
+ endTime: Date.now(),
150
+ params,
151
+ response: response as FetchResponse,
152
+ requestId: requestTaskId,
153
+ requestFrom: type == 'public' ? RequestFrom.PUBLIC : RequestFrom.INNER
154
+ });
155
+ };
156
+
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ const { resolve, reject, promise } = new Deferred<FetchResponse<any>>();
159
+ promiseMap.set(requestTaskId, { resolve, reject });
160
+
161
+ try {
162
+ const res = await promise;
163
+ report('success', res);
164
+ let { data } = res;
165
+ if (dataType === 'json' && typeof data === 'string') {
166
+ data = parseJSON(data);
167
+ }
168
+ return {
169
+ url,
170
+ timeout,
171
+ method,
172
+ response: { ...res, data }
173
+ };
174
+ } catch (err: unknown) {
175
+ const error = err as {
176
+ errMsg?: string;
177
+ errNo?: number;
178
+ errorType?: string;
179
+ errorCode?: number;
180
+ prefetchDetail?: number;
181
+ isPrefetch?: boolean;
182
+ };
183
+
184
+ if (isObject(error)) {
185
+ const { errMsg, prefetchDetail, isPrefetch, errNo } = error;
186
+ if (isString(errMsg)) {
187
+ report('fail', {
188
+ errMsg,
189
+ isPrefetch,
190
+ prefetchDetail
191
+ });
192
+ reportError(createAPIError({ code: -1, msg: errMsg }, { errNo: errNo as number, errMsg }));
193
+ }
194
+ }
195
+ throw err;
196
+ }
197
+ };
198
+
199
+ return fetch;
200
+ }
201
+
202
+ export function parseJSON(data: string) {
203
+ try {
204
+ //
205
+ // 之所以要 trim, 是因为有些开发者不是按照标准的 application/json 返回的, 而是使用 text/html 返回
206
+ // 而 text/html 返回的时候前后有可能出现 &#65279 字符, 导致 JSON.parse 失败
207
+ return JSON.parse(data.trim());
208
+ } catch {
209
+ return data;
210
+ }
211
+ }
212
+
213
+ export function normalizeData(data: unknown, contentType = 'application/json') {
214
+ const type = contentType.toLowerCase();
215
+
216
+ if (data === undefined) {
217
+ return '';
218
+ }
219
+
220
+ if (typeof data === 'string' || data instanceof ArrayBuffer) {
221
+ return data;
222
+ }
223
+
224
+ if (type.includes('application/x-www-form-urlencoded')) {
225
+ return JSON.stringify(data as JSON);
226
+ }
227
+
228
+ if (type.includes('application/json')) {
229
+ return JSON.stringify(data);
230
+ }
231
+
232
+ if (typeof data === 'object') {
233
+ return JSON.stringify(data);
234
+ }
235
+
236
+ return String(data);
237
+ }
@@ -0,0 +1,15 @@
1
+ import { xUserAgent } from '@/common/http/xua';
2
+ import { createFetch } from './create-fetch';
3
+ import { context } from '@/common/context';
4
+
5
+ /**
6
+ * inner fetch
7
+ */
8
+ export const innerFetch = createFetch('createRequestTaskSync', 'operateRequestTaskSync', {
9
+ type: 'inner',
10
+ baseUrl: context.testMode ? 'https://stg-api.jolibox.com' : 'https://api.jolibox.com',
11
+ defaultHeaders: {
12
+ 'x-user-agent': xUserAgent(),
13
+ 'X-JOLI-TOKEN': context.hostUserInfo?.token ?? ''
14
+ }
15
+ });
@@ -0,0 +1,58 @@
1
+ import { FetchResponse, DownLoadResponse, UpLoadResponse, RequestFrom } from './types';
2
+
3
+ import { track } from '@/native/report';
4
+
5
+ type NetworkResponse = FetchResponse | DownLoadResponse | UpLoadResponse;
6
+ interface ReportNetworkOptions {
7
+ state: 'success' | 'fail';
8
+ params: Record<string, unknown>;
9
+ response: NetworkResponse;
10
+ startTime: number;
11
+ endTime: number;
12
+ callbackStartTime?: number;
13
+ callbackEndTime?: number;
14
+ requestId?: string;
15
+ requestFrom?: RequestFrom; // 区分内部/外部请求
16
+ }
17
+
18
+ interface RequestResultData {
19
+ duration: number;
20
+ request_type: string;
21
+ url: string;
22
+ result: string;
23
+ error_msg: unknown;
24
+ socket_reused?: number; // reused
25
+ joli_request_number?: number; // 请求序号
26
+ request_from?: string; // 'public' / 'inner'
27
+ }
28
+ // report max count = 10
29
+ let requestNumber = 0;
30
+ const requestNumberLimit = 10;
31
+
32
+ export function reportNetworkAPI(method: string, url: string, options: ReportNetworkOptions): void {
33
+ const { response, startTime, endTime, requestFrom } = options;
34
+ const { errMsg = '', profile = {} } = response as FetchResponse;
35
+
36
+ const duration = endTime - startTime;
37
+
38
+ const trackData: RequestResultData = {
39
+ duration,
40
+ request_type: method,
41
+ url: url.split('?')[0],
42
+ result: options.state,
43
+ error_msg: options.state === 'fail' ? errMsg : '',
44
+ socket_reused: profile.socketReused ? 1 : 0,
45
+ request_from: requestFrom
46
+ };
47
+
48
+ // 用户请求上报最多10个,内部的全部上报
49
+ if (
50
+ (requestFrom === RequestFrom.PUBLIC && requestNumber < requestNumberLimit) ||
51
+ requestFrom === RequestFrom.INNER
52
+ ) {
53
+ trackData.joli_request_number = requestNumber++;
54
+ setTimeout(() => {
55
+ track('joliboxNetRequestResult', trackData as unknown as Record<string, unknown>);
56
+ }, 500);
57
+ }
58
+ }
@@ -0,0 +1,77 @@
1
+ interface Profile {
2
+ socketReused?: boolean; // 是否复用连接
3
+ }
4
+
5
+ export interface DownLoadOptions {
6
+ url: string;
7
+ header: Record<string, unknown>;
8
+ filePath?: string;
9
+ appendHostCookie?: boolean;
10
+ timeout?: number;
11
+ }
12
+
13
+ export interface DownLoadResponse {
14
+ statusCode?: number;
15
+ tempFilePath?: string;
16
+ errMsg?: string;
17
+ profile?: Profile;
18
+ filePath?: string;
19
+ header?: Record<string, string>;
20
+ }
21
+
22
+ export type HTTPMethod =
23
+ | 'GET'
24
+ | 'OPTIONS'
25
+ | 'HEAD'
26
+ | 'POST'
27
+ | 'PUT'
28
+ | 'DELETE'
29
+ | 'TRACE'
30
+ | 'CONNECT'
31
+ | 'PATCH';
32
+
33
+ export interface FetchOptions {
34
+ method?: HTTPMethod;
35
+ data?: Record<string, unknown> | string | ArrayBuffer;
36
+ query?: Record<string, string>;
37
+ header?: Record<string, string>;
38
+ dataType?: string; // 'json'
39
+ timeout?: number;
40
+ enableCache?: boolean;
41
+ responseType?: string; // 'text' | 'arraybuffer';
42
+ appendHostCookie?: boolean;
43
+ }
44
+
45
+ export interface FetchResponse<T = string | JSON | ArrayBuffer | undefined> {
46
+ header?: Record<string, string>;
47
+ code?: string;
48
+ message?: string;
49
+ data: T;
50
+ errMsg?: string;
51
+ prefetchDetail?: number;
52
+ size?: number;
53
+ profile?: Profile;
54
+ }
55
+
56
+ export interface UpLoadOptions {
57
+ url: string;
58
+ filePath: string;
59
+ name: string;
60
+ header: Record<string, unknown>;
61
+ formData: Record<string, unknown>;
62
+ appendHostCookie?: boolean;
63
+ timeout?: number;
64
+ }
65
+
66
+ export interface UpLoadResponse<T = string | Record<string, unknown> | ArrayBuffer | undefined> {
67
+ statusCode?: number;
68
+ data: T;
69
+ errMsg?: string;
70
+ progress?: number;
71
+ profile?: Profile;
72
+ }
73
+
74
+ export enum RequestFrom {
75
+ PUBLIC = 'public',
76
+ INNER = 'inner'
77
+ }
@@ -0,0 +1,90 @@
1
+ import { isObject } from '@jolibox/common';
2
+
3
+ export type HTTPMethod =
4
+ | 'GET'
5
+ | 'OPTIONS'
6
+ | 'HEAD'
7
+ | 'POST'
8
+ | 'PUT'
9
+ | 'DELETE'
10
+ | 'TRACE'
11
+ | 'CONNECT'
12
+ | 'PATCH';
13
+
14
+ export function normalizeHeader(header: unknown) {
15
+ const normalized: Record<string, string> = {};
16
+
17
+ if (!isObject(header)) {
18
+ return normalized;
19
+ }
20
+ for (const [_key, value] of Object.entries(header)) {
21
+ const key = _key.toLocaleLowerCase();
22
+ if (!value) {
23
+ normalized[key] = '';
24
+ } else if (typeof value === 'object') {
25
+ normalized[key] = Object.prototype.toString.call(value);
26
+ } else {
27
+ normalized[key] = String(value);
28
+ }
29
+ }
30
+
31
+ return normalized;
32
+ }
33
+
34
+ export function normalizeFormData(formData: unknown) {
35
+ const normalized: Record<string, string> = {};
36
+
37
+ if (!isObject(formData)) {
38
+ return normalized;
39
+ }
40
+
41
+ for (const [key, value] of Object.entries(formData)) {
42
+ if (!value) {
43
+ normalized[key] = '';
44
+ } else if (typeof value === 'object') {
45
+ normalized[key] = Object.prototype.toString.call(value);
46
+ } else {
47
+ normalized[key] = String(value);
48
+ }
49
+ }
50
+
51
+ return normalized;
52
+ }
53
+
54
+ export function normalizeMethod(method = 'GET') {
55
+ if (method === '') {
56
+ return 'POST';
57
+ }
58
+
59
+ const _method = method.toUpperCase();
60
+
61
+ if (['GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'PATCH'].includes(_method)) {
62
+ return _method as HTTPMethod;
63
+ }
64
+
65
+ return 'GET';
66
+ }
67
+
68
+ export function normalizeTimeout(time: number | undefined, unlimited = false) {
69
+ if (!time) return undefined;
70
+ if (unlimited || (time > 0 && time <= 60e3)) return Math.ceil(time);
71
+ return 60e3;
72
+ }
73
+
74
+ /**
75
+ * modify url
76
+ */
77
+ export function dirtyURL(url: string, method: HTTPMethod, data?: unknown) {
78
+ if (['POST', 'PUT', 'PATCH'].includes(method)) return url;
79
+
80
+ if (!isObject(data)) return url;
81
+
82
+ const [_url, search = ''] = url.split('?');
83
+
84
+ const query = JSON.stringify({
85
+ ...JSON.parse(search),
86
+ ...data
87
+ });
88
+
89
+ return `${_url}?${query}`;
90
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 端侧上报的实现逻辑
3
+ */
4
+ import '@/common/report/errors/report/listeners';
5
+ import { errorReportEmitter } from '@/common/report/errors';
6
+ import { ErrorData } from '@/common/report/errors/report';
7
+ import { logger } from '@jolibox/common';
8
+ import { applyNative, invokeNative } from '@/native/bootstrap/bridge';
9
+
10
+ errorReportEmitter.on('GLOBAL_ERROR', (error, errorData: ErrorData) => {
11
+ const extra = {
12
+ message: error.message,
13
+ stack: error.stack ?? '',
14
+ errorType: error.name,
15
+ source: 0
16
+ };
17
+
18
+ invokeNative('trackAsync', {
19
+ event: 'reportJsError',
20
+ data: extra
21
+ });
22
+ });
23
+
24
+ errorReportEmitter.on('GLOBAL_USER_ERROR', (error, errorData: ErrorData) => {
25
+ // TODO: User Error
26
+ logger.log('UserError', error, errorData);
27
+ });
@@ -0,0 +1,51 @@
1
+ import './errors';
2
+ import { createCommands, isObject, isString, EventEmitter } from '@jolibox/common';
3
+ export * from '../../common/report/types';
4
+ import { createTracks, EProject, ReportHandler } from '@/common/report';
5
+ import { context } from '@/common/context';
6
+ import { NativeTaskTracker } from './task-tracker';
7
+ import { invokeNative } from '../bootstrap/bridge';
8
+
9
+ const reportNative: ReportHandler = (event, data, webviewId) => {
10
+ const _data = data as Record<string, unknown>;
11
+ const originExtra = isObject(_data.extra)
12
+ ? _data.extra
13
+ : isString(_data.extra)
14
+ ? JSON.parse(_data.extra)
15
+ : {};
16
+ const extra = {
17
+ ...originExtra,
18
+ mp_id: (_data.mp_id as string) ?? '',
19
+ mp_version: (_data.mp_version as string) ?? ''
20
+ };
21
+ invokeNative('trackAsync', {
22
+ event,
23
+ data: extra,
24
+ webviewId
25
+ });
26
+ };
27
+
28
+ const { track, trackPerformance } = createTracks(reportNative, {
29
+ type: EProject.AppSDK,
30
+ platform: 'native',
31
+ jssdk_version: context.sdkInfo?.jssdkVersion ?? '1.0.0',
32
+ mp_id: context.mpId,
33
+ mp_version: context.mpVersion
34
+ });
35
+
36
+ export { track, trackPerformance };
37
+
38
+ const commands = createCommands();
39
+
40
+ commands.registerCommand('ReportSDK.traceSystemTimeline', ({ event, duration }) => {
41
+ trackPerformance(event, duration);
42
+ });
43
+
44
+ commands.registerCommand('ReportSDK.traceSystem', ({ event, info }) => {
45
+ track(event, info);
46
+ });
47
+
48
+ // 任务上报
49
+
50
+ export const nativeTaskEmitter = new EventEmitter<{ visible: [boolean] }>();
51
+ export const taskTracker = new NativeTaskTracker(track, nativeTaskEmitter);
@@ -0,0 +1,72 @@
1
+ /**
2
+ * 任务上报
3
+ */
4
+
5
+ import { context } from '@/common/context';
6
+ import { TaskTracker, TaskPoint } from '@/common/report/task-track';
7
+ import { EventEmitter } from '@jolibox/common';
8
+ import { innerFetch as fetch } from '../network';
9
+ import { applyNative } from '../bootstrap/bridge';
10
+ import type { Track } from '.';
11
+ import type { TrackEvent } from '@jolibox/types';
12
+
13
+ type NativeTaskPointEvent =
14
+ | 'OpenGame'
15
+ | 'CloseGame'
16
+ | 'LevelFinished'
17
+ | 'TaskFinished'
18
+ | 'LevelUpgrade'
19
+ | 'HistoryUserLevel'
20
+ | 'HistoryUserScore'
21
+ | 'UseGameItem'
22
+ | 'TaskEvent';
23
+
24
+ export type NativeTaskPoint = {
25
+ event: NativeTaskPointEvent;
26
+ params: Record<string, unknown>;
27
+ };
28
+ export class NativeTaskTracker extends TaskTracker {
29
+ private gameId: string;
30
+ private sessionId: string;
31
+ private track: Track;
32
+
33
+ constructor(track: Track, eventEmitter: EventEmitter<{ visible: [boolean] }>, interval?: number) {
34
+ super(eventEmitter, interval);
35
+ this.gameId = context.mpId;
36
+ this.sessionId = context.sessionId;
37
+ this.track = track;
38
+ }
39
+ async reporter(point: TaskPoint): Promise<void> {
40
+ const { event, params } = point;
41
+ const reportTasks = [];
42
+ reportTasks.push(
43
+ fetch(`/api/base/app-event`, {
44
+ method: 'POST',
45
+ data: {
46
+ eventType: event,
47
+ gameInfo: {
48
+ gameId: this.gameId,
49
+ sessionId: this.sessionId,
50
+ ...params
51
+ }
52
+ }
53
+ })
54
+ );
55
+ await Promise.all(reportTasks);
56
+ }
57
+
58
+ async reportToNative(point: NativeTaskPoint) {
59
+ const { event, params } = point;
60
+ await applyNative('userTrackAsync', {
61
+ event: event,
62
+ params: {
63
+ ...params
64
+ },
65
+ gameId: this.gameId
66
+ });
67
+ }
68
+
69
+ tracker(event: TrackEvent, info: Record<string, unknown> | null = null) {
70
+ this.track(event, info);
71
+ }
72
+ }