@jolibox/implement 1.1.13-beta.1 → 1.1.13-beta.3
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 +16 -33
- package/.rush/temp/shrinkwrap-deps.json +1 -1
- package/dist/index.js +3 -3
- package/dist/index.native.js +6 -6
- package/dist/native/api/keyboard.d.ts +1 -1
- package/implement.build.log +2 -2
- package/package.json +4 -3
- package/src/common/ads/index.ts +51 -33
- package/src/native/api/ads.ts +1 -1
- package/src/native/api/get-system-info.ts +1 -1
- package/src/native/api/keyboard.ts +1 -1
- package/src/native/api/lifecycle.ts +1 -1
- package/src/native/api/login.ts +1 -1
- package/src/native/api/navigate.ts +1 -1
- package/src/native/api/storage.ts +1 -1
- package/src/native/bootstrap/index.ts +13 -6
- package/src/native/network/create-fetch.ts +1 -1
- package/src/native/report/errors/index.ts +1 -1
- package/src/native/report/index.ts +1 -1
- package/src/native/report/task-tracker.ts +1 -1
- package/src/native/ui/retention.ts +1 -1
- package/dist/native/bootstrap/bridge.d.ts +0 -4
- package/dist/native/js-bridge/const.d.ts +0 -6
- package/dist/native/js-bridge/index.d.ts +0 -2
- package/dist/native/js-bridge/invoke.d.ts +0 -21
- package/dist/native/js-bridge/js-bridge.d.ts +0 -6
- package/dist/native/js-bridge/publish.d.ts +0 -1
- package/dist/native/js-bridge/report.d.ts +0 -63
- package/dist/native/js-bridge/subscribe.d.ts +0 -10
- package/dist/native/js-bridge/types.d.ts +0 -18
- package/dist/native/js-bridge/utils.d.ts +0 -17
- package/dist/native/js-core/index.d.ts +0 -3
- package/dist/native/js-core/jolibox-js-core.d.ts +0 -50
- package/dist/native/js-core/message-port.d.ts +0 -12
- package/dist/native/js-core/utils.d.ts +0 -7
- package/src/native/bootstrap/bridge.ts +0 -68
- package/src/native/js-bridge/const.ts +0 -13
- package/src/native/js-bridge/index.ts +0 -2
- package/src/native/js-bridge/invoke.ts +0 -208
- package/src/native/js-bridge/js-bridge.ts +0 -28
- package/src/native/js-bridge/publish.ts +0 -44
- package/src/native/js-bridge/report.ts +0 -311
- package/src/native/js-bridge/subscribe.ts +0 -74
- package/src/native/js-bridge/types.ts +0 -36
- package/src/native/js-bridge/utils.ts +0 -116
- package/src/native/js-core/index.ts +0 -4
- package/src/native/js-core/jolibox-js-core.ts +0 -192
- package/src/native/js-core/message-port.ts +0 -52
- package/src/native/js-core/utils.ts +0 -9
- package/src/native/types/global.d.ts +0 -27
- package/src/native/types/native-method-map.d.ts +0 -329
- package/src/native/types/native-method.d.ts +0 -30
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { pack, unpack } from './utils';
|
|
2
|
-
import { Deferred, isObject, isPromiseLike, InternalApplyNativeError, logger } from '@jolibox/common';
|
|
3
|
-
import { formatErrorCode } from '../../common/report/errors';
|
|
4
|
-
import { BACKGROUND_FORBIDDEN_METHODS, BUFFER_METHODS, SYNC_METHODS } from './const';
|
|
5
|
-
import { createReportInvokeMetrics } from './report';
|
|
6
|
-
import { InvokeHandler, On } from './types';
|
|
7
|
-
|
|
8
|
-
import { AnyFunction } from '@jolibox/types';
|
|
9
|
-
|
|
10
|
-
interface Invokes {
|
|
11
|
-
/** 暴露给宿主, 触发调用的方法 */
|
|
12
|
-
invokeHandler: InvokeHandler;
|
|
13
|
-
/** 基础库内部, 调用宿主的方法, 返回 native 返回对象 */
|
|
14
|
-
invokeNative: (method: string, args?: Record<string, unknown>, webviewId?: number) => any;
|
|
15
|
-
/** 基础库内部, 调用宿主的方法, 会根据 errMsg throwError */
|
|
16
|
-
applyNative: (method: string, args?: Record<string, unknown>, webviewId?: number) => any;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type Response = {
|
|
20
|
-
errMsg: string;
|
|
21
|
-
errNo?: number;
|
|
22
|
-
__timing?: {
|
|
23
|
-
receiveJSInvoke: number;
|
|
24
|
-
invokeCallback: number;
|
|
25
|
-
};
|
|
26
|
-
} & Record<string, unknown>;
|
|
27
|
-
|
|
28
|
-
// 后台期间积压的请求
|
|
29
|
-
const asyncInvokes: {
|
|
30
|
-
method: string;
|
|
31
|
-
args?: Record<string, unknown>;
|
|
32
|
-
resolve: AnyFunction;
|
|
33
|
-
}[] = [];
|
|
34
|
-
|
|
35
|
-
//TODO: support arrayBuffer
|
|
36
|
-
const NOT_SUPPORT_ARRAY_BUFFER = false;
|
|
37
|
-
|
|
38
|
-
export function createInvoke(jsCore: jsb.JSCore, onNative: On): Invokes {
|
|
39
|
-
let resolveId = 0;
|
|
40
|
-
let IS_FOREGROUND = true;
|
|
41
|
-
|
|
42
|
-
const resolveMap = new Map<number, AnyFunction>();
|
|
43
|
-
const reportInvokeMetrics = createReportInvokeMetrics(jsCore, onNative);
|
|
44
|
-
|
|
45
|
-
onNative('onJoliboxEnterBackground', () => {
|
|
46
|
-
IS_FOREGROUND = false;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
onNative('onJoliboxEnterForeground', () => {
|
|
50
|
-
IS_FOREGROUND = true;
|
|
51
|
-
// 从后台进入前台后, 处理之前在后台挤压的任务
|
|
52
|
-
asyncInvokes.forEach(async ({ method, args, resolve }) => {
|
|
53
|
-
const res = await invokeNative(method, args);
|
|
54
|
-
resolve(res);
|
|
55
|
-
});
|
|
56
|
-
// 清空积压
|
|
57
|
-
asyncInvokes.length = 0;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const invokeHandler: InvokeHandler = (resolveId, data): void => {
|
|
61
|
-
/**
|
|
62
|
-
* Android resolveId 是 `string`
|
|
63
|
-
* iOS 上待确认, IDE 上是 `number`
|
|
64
|
-
*/
|
|
65
|
-
const resolve = resolveMap.get(Number(resolveId));
|
|
66
|
-
if (!resolve) return;
|
|
67
|
-
const response = unpack(data);
|
|
68
|
-
resolve(response);
|
|
69
|
-
resolveMap.delete(Number(resolveId));
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const invokeNative = (
|
|
73
|
-
method: string,
|
|
74
|
-
args?: Record<string, unknown>
|
|
75
|
-
): Record<string, unknown> | Promise<Record<string, unknown>> => {
|
|
76
|
-
const startTime = Date.now();
|
|
77
|
-
const isForeground = `${IS_FOREGROUND}`;
|
|
78
|
-
|
|
79
|
-
resolveId += 1;
|
|
80
|
-
|
|
81
|
-
const deferred = new Deferred<Response>();
|
|
82
|
-
resolveMap.set(resolveId, deferred.resolve);
|
|
83
|
-
|
|
84
|
-
if (!IS_FOREGROUND && BACKGROUND_FORBIDDEN_METHODS.includes(method)) {
|
|
85
|
-
// `FOREGROUND_FORBIDDEN_EVENTS` all async API
|
|
86
|
-
return new Promise<Record<string, unknown>>((resolve) => {
|
|
87
|
-
asyncInvokes.push({ method, args, resolve });
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const basicReportParam = {
|
|
92
|
-
method,
|
|
93
|
-
startTime,
|
|
94
|
-
args,
|
|
95
|
-
isForeground
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const invokeReportParam = {
|
|
99
|
-
...basicReportParam,
|
|
100
|
-
invokeType: 'js-bridge'
|
|
101
|
-
} as const;
|
|
102
|
-
|
|
103
|
-
const useArrayBuffer = !NOT_SUPPORT_ARRAY_BUFFER && BUFFER_METHODS.includes(method);
|
|
104
|
-
|
|
105
|
-
const payload = pack(args, useArrayBuffer);
|
|
106
|
-
const params = useArrayBuffer ? payload : JSON.stringify(payload);
|
|
107
|
-
|
|
108
|
-
/** 调用结果 */
|
|
109
|
-
let response;
|
|
110
|
-
|
|
111
|
-
if (method.endsWith('Sync') || SYNC_METHODS.includes(method)) {
|
|
112
|
-
response = jsCore.call?.(method, payload, resolveId);
|
|
113
|
-
} else {
|
|
114
|
-
if (typeof params === 'string') {
|
|
115
|
-
response = jsCore.invoke(method, params, resolveId);
|
|
116
|
-
} else {
|
|
117
|
-
response = jsCore.call?.(method, params, resolveId);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* 如果 invoke 返回了结果,则为同步调用
|
|
123
|
-
* - iOS 返回 `undefined`
|
|
124
|
-
* - Android 返回空字符串 `""`
|
|
125
|
-
*/
|
|
126
|
-
if (response) {
|
|
127
|
-
try {
|
|
128
|
-
if (typeof response === 'string') {
|
|
129
|
-
response = JSON.parse(response);
|
|
130
|
-
}
|
|
131
|
-
response = unpack(response);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
logger.error(error);
|
|
134
|
-
}
|
|
135
|
-
resolveMap.delete(resolveId);
|
|
136
|
-
if (response.errorCode) {
|
|
137
|
-
response.errorCode = formatErrorCode(method, response.errorCode);
|
|
138
|
-
}
|
|
139
|
-
reportInvokeMetrics({ ...invokeReportParam, ...response });
|
|
140
|
-
|
|
141
|
-
return response;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (method.endsWith('Sync')) {
|
|
145
|
-
resolveMap.delete(resolveId);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return deferred.promise.then((response) => {
|
|
149
|
-
if (response.errorCode) {
|
|
150
|
-
response.errorCode = formatErrorCode(method, response.errorCode as number | undefined);
|
|
151
|
-
}
|
|
152
|
-
reportInvokeMetrics({ ...invokeReportParam, ...response });
|
|
153
|
-
return response;
|
|
154
|
-
});
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const applyNative = (method: string, arg?: Record<string, unknown>) => {
|
|
158
|
-
const res = invokeNative(method, arg);
|
|
159
|
-
if (isPromiseLike(res)) {
|
|
160
|
-
return res.then((r) =>
|
|
161
|
-
ifThrowError(
|
|
162
|
-
method,
|
|
163
|
-
r as {
|
|
164
|
-
errMsg?: string;
|
|
165
|
-
errNo?: number;
|
|
166
|
-
errorType?: string;
|
|
167
|
-
errorCode?: number;
|
|
168
|
-
}
|
|
169
|
-
)
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
return ifThrowError(method, res);
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
invokeNative,
|
|
177
|
-
applyNative,
|
|
178
|
-
invokeHandler
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function ifThrowError(
|
|
183
|
-
method: string,
|
|
184
|
-
res: {
|
|
185
|
-
errMsg?: string;
|
|
186
|
-
errNo?: number;
|
|
187
|
-
errorType?: string;
|
|
188
|
-
errorCode?: number;
|
|
189
|
-
}
|
|
190
|
-
) {
|
|
191
|
-
if (!isObject(res)) {
|
|
192
|
-
logger.warn(`[Jolibox SDK]${method} no response value`);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
const { errMsg, errNo, errorType, errorCode, ...data } = res;
|
|
196
|
-
|
|
197
|
-
if (errMsg && errMsg !== `${method}:ok`) {
|
|
198
|
-
throw new InternalApplyNativeError(errMsg, errNo, errorType, errorCode);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return data;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export class ApplyNativeError extends Error {
|
|
205
|
-
constructor(message: string, readonly errNo?: number) {
|
|
206
|
-
super(message);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { createInvoke } from './invoke';
|
|
2
|
-
import { createSubscribe } from './subscribe';
|
|
3
|
-
import { JSBridge } from './types';
|
|
4
|
-
import { createPublish } from './publish';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* build js-bridge
|
|
8
|
-
* @param jsCore jsCore function inject by native
|
|
9
|
-
*/
|
|
10
|
-
export function createBridge(jsCore: jsb.JSCore): JSBridge {
|
|
11
|
-
const { subscribeHandler, onNative, offNative, subscribe, unsubscribe } = createSubscribe(jsCore);
|
|
12
|
-
|
|
13
|
-
const { invokeNative, invokeHandler, applyNative } = createInvoke(jsCore, onNative);
|
|
14
|
-
const publish = createPublish(jsCore);
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
// 宿主调用
|
|
18
|
-
invokeHandler,
|
|
19
|
-
subscribeHandler,
|
|
20
|
-
applyNative,
|
|
21
|
-
invokeNative,
|
|
22
|
-
onNative,
|
|
23
|
-
offNative,
|
|
24
|
-
publish,
|
|
25
|
-
subscribe,
|
|
26
|
-
unsubscribe
|
|
27
|
-
};
|
|
28
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { CUSTOM_EVENT_PREFIX, BATCH_EVENT } from './const';
|
|
2
|
-
|
|
3
|
-
type BatchEvent = [event: string, data: unknown];
|
|
4
|
-
type BatchEvents = BatchEvent[];
|
|
5
|
-
|
|
6
|
-
export function createPublish(jsCore: jsb.JSCore) {
|
|
7
|
-
const eventsMap = new Map<number | undefined, BatchEvents>();
|
|
8
|
-
let batchTask: Promise<void> | undefined;
|
|
9
|
-
const publish = (event: string, data: Record<string, unknown>, webviewId?: number, force?: boolean) => {
|
|
10
|
-
if (force) {
|
|
11
|
-
const ids = webviewId ? [webviewId] : '*';
|
|
12
|
-
jsCore.publish(`${CUSTOM_EVENT_PREFIX}${event}`, data, ids);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!batchTask) {
|
|
17
|
-
batchTask = Promise.resolve().then(() => {
|
|
18
|
-
eventsMap.forEach((data, webviewId) => {
|
|
19
|
-
try {
|
|
20
|
-
const ids = webviewId ? [webviewId] : '*';
|
|
21
|
-
jsCore.publish(BATCH_EVENT, data, ids);
|
|
22
|
-
} catch {
|
|
23
|
-
// 避免一组 webview publish 的报错影响其他 webview publish
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// reset
|
|
28
|
-
batchTask = undefined;
|
|
29
|
-
eventsMap.clear();
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let events = eventsMap.get(webviewId);
|
|
34
|
-
|
|
35
|
-
if (!events) {
|
|
36
|
-
events = [];
|
|
37
|
-
eventsMap.set(webviewId, events);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
events.push([`${CUSTOM_EVENT_PREFIX}${event}`, data]);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return publish;
|
|
44
|
-
}
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { On } from './types';
|
|
2
|
-
import { isObject, isString, InternalMetricReportError } from '@jolibox/common';
|
|
3
|
-
|
|
4
|
-
type MetricReporter = (points: unknown) => unknown;
|
|
5
|
-
interface MetricCache {
|
|
6
|
-
[props: string]: MetricCache | (number | string)[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface PVMetricCache {
|
|
10
|
-
[props: string]: PVMetricCache | number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type MetricCacheType = MetricCache | PVMetricCache;
|
|
14
|
-
type MetricType = 'pv' | 'default';
|
|
15
|
-
type Prettier<T> = T extends Record<string, unknown> ? { [K in keyof T]: T[K] } : T;
|
|
16
|
-
type MetricPoint<Tag extends string, Name extends string> = Prettier<
|
|
17
|
-
{
|
|
18
|
-
[k in Tag]: string;
|
|
19
|
-
} & {
|
|
20
|
-
[K in Name]: number | string;
|
|
21
|
-
}
|
|
22
|
-
>;
|
|
23
|
-
|
|
24
|
-
interface PVPoint {
|
|
25
|
-
[props: string]: string | JSON;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const getType = (type: unknown): MetricType => {
|
|
29
|
-
if (type === 'pv') return type;
|
|
30
|
-
return 'default';
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
interface MetricConfig<Tag extends string, Name extends string, Type extends string> {
|
|
34
|
-
reporter: MetricReporter;
|
|
35
|
-
interval?: number;
|
|
36
|
-
eventName: string;
|
|
37
|
-
tagNameOrder: Tag[];
|
|
38
|
-
metricName: Name;
|
|
39
|
-
type?: Type;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export class MetricsMonitor<Tag extends string, Name extends string, Type extends string> {
|
|
43
|
-
reporter: MetricReporter;
|
|
44
|
-
interval = 30;
|
|
45
|
-
lastReportTime = 0;
|
|
46
|
-
cache: MetricCacheType = {};
|
|
47
|
-
eventName: string;
|
|
48
|
-
tagNameOrder: Tag[];
|
|
49
|
-
metricName: Name;
|
|
50
|
-
type: MetricType;
|
|
51
|
-
|
|
52
|
-
constructor(config: MetricConfig<Tag, Name, Type>) {
|
|
53
|
-
const { reporter, interval = 30, eventName, tagNameOrder, metricName, type } = config;
|
|
54
|
-
this.reporter = reporter;
|
|
55
|
-
this.interval = interval;
|
|
56
|
-
this.eventName = eventName;
|
|
57
|
-
this.tagNameOrder = tagNameOrder;
|
|
58
|
-
this.metricName = metricName;
|
|
59
|
-
this.type = getType(type);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
report(point: Type extends 'pv' ? PVPoint : MetricPoint<Tag, Name>) {
|
|
63
|
-
if (this.type === 'pv') {
|
|
64
|
-
this.addPVPoint(point);
|
|
65
|
-
} else {
|
|
66
|
-
this.addPoint(point as MetricPoint<Tag, Name>);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
this.tryReport(false);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
flush() {
|
|
73
|
-
this.tryReport(true);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private tryReport(force: boolean) {
|
|
77
|
-
if (!Object.keys(this.cache).length) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (force) {
|
|
82
|
-
this.reporter(this.processPoints(this.cache));
|
|
83
|
-
this.clearPoints();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
this.lastReportTime = this.lastReportTime || Date.now();
|
|
88
|
-
|
|
89
|
-
if (Date.now() - this.lastReportTime >= this.interval * 1000) {
|
|
90
|
-
this.reporter(this.processPoints(this.cache));
|
|
91
|
-
this.clearPoints();
|
|
92
|
-
this.lastReportTime = Date.now();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
private addPoint(point: MetricPoint<Tag, Name>) {
|
|
97
|
-
const safeSetVal = (
|
|
98
|
-
target: MetricCache,
|
|
99
|
-
props: Tag[],
|
|
100
|
-
metricName: Name,
|
|
101
|
-
point: MetricPoint<Tag, Name>
|
|
102
|
-
) => {
|
|
103
|
-
const currentProp = props.shift();
|
|
104
|
-
if (currentProp) {
|
|
105
|
-
const currentVal = point[currentProp];
|
|
106
|
-
if (!target[currentVal]) target[currentVal] = {};
|
|
107
|
-
|
|
108
|
-
safeSetVal(target[currentVal] as MetricCache, props, metricName, point);
|
|
109
|
-
} else {
|
|
110
|
-
if (!target[metricName]) target[metricName] = [];
|
|
111
|
-
(target[metricName] as unknown[]).push(point[metricName]);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
safeSetVal(this.cache as MetricCache, [...this.tagNameOrder], this.metricName, point);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private addPVPoint(point: PVPoint) {
|
|
119
|
-
const safeSetVal = (
|
|
120
|
-
target: Record<string, unknown>,
|
|
121
|
-
props: Tag[],
|
|
122
|
-
metricName: Name,
|
|
123
|
-
relation: unknown
|
|
124
|
-
) => {
|
|
125
|
-
const currentProp = props.shift();
|
|
126
|
-
|
|
127
|
-
if (!currentProp && isObject(relation)) {
|
|
128
|
-
throw new InternalMetricReportError(
|
|
129
|
-
`report value failed to match tagNameOrder, ${JSON.stringify(originParams)}`
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (currentProp) {
|
|
134
|
-
const keys: string[] = [];
|
|
135
|
-
if (isObject(relation)) {
|
|
136
|
-
keys.push(...Object.keys(relation));
|
|
137
|
-
} else if (isString(relation)) {
|
|
138
|
-
if (props.length > 0) {
|
|
139
|
-
throw new InternalMetricReportError(
|
|
140
|
-
`report value failed to match tagNameOrder, ${JSON.stringify(originParams)}`
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
keys.push(relation);
|
|
145
|
-
} else {
|
|
146
|
-
throw new InternalMetricReportError(
|
|
147
|
-
`expected metric value to be a string, but got a(n) ${typeof relation}, ${JSON.stringify(
|
|
148
|
-
originParams
|
|
149
|
-
)}`
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
keys.forEach((key) => {
|
|
154
|
-
if (!target[key]) target[key] = {};
|
|
155
|
-
const value = isObject(relation) ? relation[key] : relation;
|
|
156
|
-
safeSetVal(target[key] as MetricCache, [...props], metricName, value);
|
|
157
|
-
});
|
|
158
|
-
} else {
|
|
159
|
-
if (!target[metricName]) {
|
|
160
|
-
target[metricName] = 1;
|
|
161
|
-
} else {
|
|
162
|
-
(target[metricName] as number)++;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const originParams = {
|
|
168
|
-
point,
|
|
169
|
-
metricName: this.metricName,
|
|
170
|
-
tagNameOrder: this.tagNameOrder
|
|
171
|
-
};
|
|
172
|
-
safeSetVal(this.cache, [...this.tagNameOrder], this.metricName, point);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
private processPoints(points: MetricCacheType) {
|
|
176
|
-
return {
|
|
177
|
-
eventName: this.eventName,
|
|
178
|
-
tagNameOrder: this.tagNameOrder,
|
|
179
|
-
metricName: this.metricName,
|
|
180
|
-
metricAggregateType: this.type === 'pv' ? 'itoa' : 'arr',
|
|
181
|
-
data: points
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private clearPoints() {
|
|
186
|
-
this.cache = {};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
interface InvokeMetrics {
|
|
191
|
-
errMsg: string;
|
|
192
|
-
errNo?: number;
|
|
193
|
-
__timing?: {
|
|
194
|
-
receiveJSInvoke: number;
|
|
195
|
-
invokeCallback: number;
|
|
196
|
-
};
|
|
197
|
-
method: string;
|
|
198
|
-
startTime: number;
|
|
199
|
-
args?: Record<string, unknown>;
|
|
200
|
-
isForeground: string;
|
|
201
|
-
|
|
202
|
-
[prop: string]: unknown;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
let monitorHasInitialized = false;
|
|
206
|
-
let invokeMonitor: MetricsMonitor<
|
|
207
|
-
'status' | 'method' | 'errNo' | 'errMsg' | 'isForeground',
|
|
208
|
-
'duration',
|
|
209
|
-
'default'
|
|
210
|
-
>;
|
|
211
|
-
let invokeJSToNativeMonitor: MetricsMonitor<'status' | 'method' | 'isForeground', 'duration', 'default'>;
|
|
212
|
-
let invokeNativeToJSMonitor: MetricsMonitor<'status' | 'method' | 'isForeground', 'duration', 'default'>;
|
|
213
|
-
|
|
214
|
-
export type reportInvokeFn = (invokeMetrics: InvokeMetrics) => void;
|
|
215
|
-
|
|
216
|
-
export function createReportInvokeMetrics(jsCore: jsb.JSCore, onNative: On): reportInvokeFn {
|
|
217
|
-
if (!monitorHasInitialized) {
|
|
218
|
-
/** invoke 埋点 */
|
|
219
|
-
invokeMonitor = new MetricsMonitor({
|
|
220
|
-
eventName: 'jolibox_invoke',
|
|
221
|
-
tagNameOrder: ['status', 'method', 'errNo', 'errMsg', 'isForeground'],
|
|
222
|
-
metricName: 'duration',
|
|
223
|
-
reporter(data) {
|
|
224
|
-
jsCore.invoke('track', JSON.stringify({ event: 'reportMetrics', data }), -1);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
/** invoke js -> native */
|
|
229
|
-
invokeJSToNativeMonitor = new MetricsMonitor({
|
|
230
|
-
eventName: 'jolibox_invoke_js2native',
|
|
231
|
-
tagNameOrder: ['status', 'method', 'isForeground'],
|
|
232
|
-
metricName: 'duration',
|
|
233
|
-
reporter(data) {
|
|
234
|
-
jsCore.invoke('track', JSON.stringify({ event: 'reportMetrics', data }), -1);
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
/** invoke native -> js */
|
|
239
|
-
invokeNativeToJSMonitor = new MetricsMonitor({
|
|
240
|
-
eventName: 'jolibox_invoke_native2js',
|
|
241
|
-
tagNameOrder: ['status', 'method', 'isForeground'],
|
|
242
|
-
metricName: 'duration',
|
|
243
|
-
reporter(data) {
|
|
244
|
-
jsCore.invoke('track', JSON.stringify({ event: 'reportMetrics', data }), -1);
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
onNative('onJoliboxEnterBackground', () => {
|
|
249
|
-
invokeMonitor.flush();
|
|
250
|
-
invokeJSToNativeMonitor.flush();
|
|
251
|
-
invokeNativeToJSMonitor.flush();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
monitorHasInitialized = true;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function report(invokeMetrics: InvokeMetrics) {
|
|
258
|
-
const {
|
|
259
|
-
method,
|
|
260
|
-
args,
|
|
261
|
-
startTime,
|
|
262
|
-
isForeground,
|
|
263
|
-
errMsg = `${method}:fail no errMsg`,
|
|
264
|
-
errNo = 0,
|
|
265
|
-
__timing = {
|
|
266
|
-
receiveJSInvoke: 0,
|
|
267
|
-
invokeCallback: 0
|
|
268
|
-
}
|
|
269
|
-
} = invokeMetrics;
|
|
270
|
-
const { receiveJSInvoke, invokeCallback } = __timing;
|
|
271
|
-
const status = errMsg.match(/\S:(\S+)\s?/)?.[1] ?? 'fail';
|
|
272
|
-
|
|
273
|
-
// report sync
|
|
274
|
-
invokeMonitor.report({
|
|
275
|
-
status,
|
|
276
|
-
method: normalizeMethod(method, args),
|
|
277
|
-
errNo: `${errNo}`,
|
|
278
|
-
errMsg,
|
|
279
|
-
isForeground,
|
|
280
|
-
duration: Date.now() - startTime
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
if (startTime && receiveJSInvoke && invokeCallback) {
|
|
284
|
-
invokeJSToNativeMonitor.report({
|
|
285
|
-
status,
|
|
286
|
-
method,
|
|
287
|
-
isForeground,
|
|
288
|
-
duration: Number((receiveJSInvoke - startTime).toFixed(3))
|
|
289
|
-
});
|
|
290
|
-
invokeNativeToJSMonitor.report({
|
|
291
|
-
status,
|
|
292
|
-
method,
|
|
293
|
-
isForeground,
|
|
294
|
-
duration: Number((Date.now() - invokeCallback).toFixed(3))
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function normalizeMethod(method: string, args?: Record<string, unknown>) {
|
|
300
|
-
// Reserve an interface;
|
|
301
|
-
// an API might be provided later to directly expose client-side methods to users.
|
|
302
|
-
if (method === 'callHostMethod' || method === 'callHostMethodSync') {
|
|
303
|
-
if (args?.method) {
|
|
304
|
-
return `${method}-${args.method}`;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return method;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return report;
|
|
311
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { EventEmitter, logger } from '@jolibox/common';
|
|
2
|
-
import { DataObj, unpack } from './utils';
|
|
3
|
-
import { Off, On } from './types';
|
|
4
|
-
import { AnyFunction } from '@jolibox/types';
|
|
5
|
-
import { MetricsMonitor } from './report';
|
|
6
|
-
import { BATCH_EVENT, CUSTOM_EVENT_PREFIX } from './const';
|
|
7
|
-
|
|
8
|
-
export interface Subscribes {
|
|
9
|
-
onNative: On;
|
|
10
|
-
offNative: Off;
|
|
11
|
-
subscribeHandler: AnyFunction;
|
|
12
|
-
subscribe: On;
|
|
13
|
-
unsubscribe: Off;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function createSubscribe(jsCore: jsb.JSCore): Subscribes {
|
|
17
|
-
const nativeEmitter = new EventEmitter();
|
|
18
|
-
const customEmitter = new EventEmitter();
|
|
19
|
-
const publishMonitor = new MetricsMonitor({
|
|
20
|
-
eventName: 'jolibox_publish',
|
|
21
|
-
tagNameOrder: ['type', 'event'],
|
|
22
|
-
metricName: 'duration',
|
|
23
|
-
reporter(data) {
|
|
24
|
-
jsCore.invoke('track', JSON.stringify({ event: 'reportMetrics', data }), -1);
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
nativeEmitter.on('onJoliboxEnterBackground', () => {
|
|
29
|
-
publishMonitor.flush();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
onNative: nativeEmitter.on.bind(nativeEmitter),
|
|
34
|
-
offNative: nativeEmitter.off.bind(nativeEmitter),
|
|
35
|
-
subscribe: customEmitter.on.bind(customEmitter),
|
|
36
|
-
unsubscribe: customEmitter.off.bind(customEmitter),
|
|
37
|
-
subscribeHandler(event, data, webviewId) {
|
|
38
|
-
// ios: jsc 端基于系统方法,前端接受到的 data 总是 string 类型, webview 基于 evaluateJavascript,前端接受到的 data 总是 object 类型
|
|
39
|
-
// android: 传入为 string 则接收到 string,传入为可序列化成功的 object,则接收到 object
|
|
40
|
-
const unpackedData = unpack(data);
|
|
41
|
-
let originalParams: undefined | DataObj;
|
|
42
|
-
if (unpackedData.__extra) {
|
|
43
|
-
originalParams = unpackedData.params;
|
|
44
|
-
const { type, startTime } = unpackedData.__extra;
|
|
45
|
-
publishMonitor.report({
|
|
46
|
-
type,
|
|
47
|
-
event,
|
|
48
|
-
duration: Date.now() - startTime // ms
|
|
49
|
-
});
|
|
50
|
-
} else {
|
|
51
|
-
originalParams = unpackedData;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (event === BATCH_EVENT) {
|
|
55
|
-
const list = originalParams as [event: string, data: unknown][];
|
|
56
|
-
list.forEach((item) => {
|
|
57
|
-
const [_event, _data] = item;
|
|
58
|
-
try {
|
|
59
|
-
customEmitter.emit(_event.slice(CUSTOM_EVENT_PREFIX.length), _data, webviewId);
|
|
60
|
-
} catch {
|
|
61
|
-
// 忽略
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (event.startsWith(CUSTOM_EVENT_PREFIX)) {
|
|
68
|
-
customEmitter.emit(event.slice(CUSTOM_EVENT_PREFIX.length), originalParams, webviewId);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
nativeEmitter.emit(event, originalParams, webviewId);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
|
-
export type Listener = (...args: any[]) => any;
|
|
3
|
-
|
|
4
|
-
export type On = <T extends string>(event: T, handler: Listener) => void;
|
|
5
|
-
|
|
6
|
-
export type Off = <T extends string>(event: T, handler: Listener) => void;
|
|
7
|
-
|
|
8
|
-
export type InvokeHandler = (callbackId: string | number, data: string | Record<string, unknown>) => void;
|
|
9
|
-
|
|
10
|
-
export type Publish = (
|
|
11
|
-
event: string,
|
|
12
|
-
data: Record<string, unknown>,
|
|
13
|
-
webviewId?: number,
|
|
14
|
-
force?: boolean
|
|
15
|
-
) => void;
|
|
16
|
-
|
|
17
|
-
export type SubscribeHandler = (
|
|
18
|
-
event: string,
|
|
19
|
-
data: string | Record<string, unknown>,
|
|
20
|
-
webviewId?: number
|
|
21
|
-
) => void;
|
|
22
|
-
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
-
export type AnyFunction = (...args: any[]) => any;
|
|
25
|
-
|
|
26
|
-
export interface JSBridge {
|
|
27
|
-
invokeHandler: InvokeHandler;
|
|
28
|
-
subscribeHandler: SubscribeHandler;
|
|
29
|
-
invokeNative: jsb.service.InvokeNative;
|
|
30
|
-
applyNative: jsb.service.ApplyNative;
|
|
31
|
-
onNative: jsb.service.OnNative;
|
|
32
|
-
offNative: jsb.service.OffNative;
|
|
33
|
-
publish: Publish;
|
|
34
|
-
subscribe: On;
|
|
35
|
-
unsubscribe: Off;
|
|
36
|
-
}
|