@flatbiz/device 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constant.d.ts +15 -0
- package/dist/constant.js +29 -0
- package/dist/helpers/helper-event-id.d.ts +11 -0
- package/dist/helpers/helper-event-id.js +64 -0
- package/dist/helpers/helper-report-device-info.d.ts +9 -0
- package/dist/helpers/helper-report-device-info.js +76 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +28 -0
- package/dist/types/types-device.d.ts +6 -0
- package/dist/types/types-options.d.ts +99 -0
- package/dist/types/types-options.js +45 -1
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** 生产 / 非生产环境设备信息上报地址 */
|
|
2
|
+
export declare const REPORT_DEVICE_INFO_URLS: {
|
|
3
|
+
readonly prod: "https://rcsapi.tcshuke.com/device/collect";
|
|
4
|
+
readonly test: "https://rcsapi.qa.tcshuke.com/device/collect";
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* 组装上报鉴权请求头
|
|
8
|
+
* @param signature 业务侧传入的签名,写入 `X-signature`(勿把密钥写进 npm 包)
|
|
9
|
+
*/
|
|
10
|
+
export declare const getAuthHeaders: (signature?: string) => Record<string, string>;
|
|
11
|
+
/**
|
|
12
|
+
* 获取设备信息上报地址
|
|
13
|
+
* @param isProd 是否生产环境,默认 true
|
|
14
|
+
*/
|
|
15
|
+
export declare const getReportDeviceInfoUrl: (isProd?: boolean) => string;
|
package/dist/constant.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** 生产 / 非生产环境设备信息上报地址 */
|
|
2
|
+
export const REPORT_DEVICE_INFO_URLS = {
|
|
3
|
+
prod: 'https://rcsapi.tcshuke.com/device/collect',
|
|
4
|
+
test: 'https://rcsapi.qa.tcshuke.com/device/collect',
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* 组装上报鉴权请求头
|
|
8
|
+
* @param signature 业务侧传入的签名,写入 `X-signature`(勿把密钥写进 npm 包)
|
|
9
|
+
*/
|
|
10
|
+
export const getAuthHeaders = (signature) => {
|
|
11
|
+
const timestamp = Date.now().toString();
|
|
12
|
+
const requestId = Math.random().toString(36).substring(2, 15);
|
|
13
|
+
if (!signature) {
|
|
14
|
+
console.error('[@flatbiz/device] signature 不能为空,请通过 reportDeviceInfo({ signature }) 传入');
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
'X-app-id': 'cashier',
|
|
18
|
+
'X-requestId': `${requestId}-${timestamp}`,
|
|
19
|
+
'X-timestamp': timestamp,
|
|
20
|
+
'X-signature': signature ?? '',
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 获取设备信息上报地址
|
|
25
|
+
* @param isProd 是否生产环境,默认 true
|
|
26
|
+
*/
|
|
27
|
+
export const getReportDeviceInfoUrl = (isProd = true) => {
|
|
28
|
+
return isProd ? REPORT_DEVICE_INFO_URLS.prod : REPORT_DEVICE_INFO_URLS.test;
|
|
29
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 读取当前 eventId(不会新建)
|
|
3
|
+
* 仅由 reportDeviceInfo 调用 resetEventId 时生成/重置
|
|
4
|
+
*/
|
|
5
|
+
export declare const getEventId: () => string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* 生成并缓存新的 eventId(仅应在 reportDeviceInfo 中调用)
|
|
8
|
+
*/
|
|
9
|
+
export declare const resetEventId: () => string;
|
|
10
|
+
/** 测试或内部清理用 */
|
|
11
|
+
export declare const clearEventId: () => void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const STORAGE_KEY = 'flatbiz_device_eventId';
|
|
2
|
+
let memoryEventId = null;
|
|
3
|
+
const createEventId = () => {
|
|
4
|
+
return 'xxxxxxxx4xxxyxxx'.replace(/[xy]/g, (c) => {
|
|
5
|
+
const r = (Math.random() * 16) | 0;
|
|
6
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
7
|
+
return v.toString(16);
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
const readStorage = () => {
|
|
11
|
+
try {
|
|
12
|
+
if (typeof sessionStorage === 'undefined')
|
|
13
|
+
return null;
|
|
14
|
+
return sessionStorage.getItem(STORAGE_KEY);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const writeStorage = (eventId) => {
|
|
21
|
+
try {
|
|
22
|
+
if (typeof sessionStorage === 'undefined')
|
|
23
|
+
return;
|
|
24
|
+
sessionStorage.setItem(STORAGE_KEY, eventId);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// ignore quota / private mode
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* 读取当前 eventId(不会新建)
|
|
32
|
+
* 仅由 reportDeviceInfo 调用 resetEventId 时生成/重置
|
|
33
|
+
*/
|
|
34
|
+
export const getEventId = () => {
|
|
35
|
+
if (memoryEventId)
|
|
36
|
+
return memoryEventId;
|
|
37
|
+
const cached = readStorage();
|
|
38
|
+
if (cached) {
|
|
39
|
+
memoryEventId = cached;
|
|
40
|
+
return cached;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* 生成并缓存新的 eventId(仅应在 reportDeviceInfo 中调用)
|
|
46
|
+
*/
|
|
47
|
+
export const resetEventId = () => {
|
|
48
|
+
const eventId = createEventId();
|
|
49
|
+
memoryEventId = eventId;
|
|
50
|
+
writeStorage(eventId);
|
|
51
|
+
return eventId;
|
|
52
|
+
};
|
|
53
|
+
/** 测试或内部清理用 */
|
|
54
|
+
export const clearEventId = () => {
|
|
55
|
+
memoryEventId = null;
|
|
56
|
+
try {
|
|
57
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
58
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// ignore
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FlatbizDevice } from '../types/types-device.js';
|
|
2
|
+
import type { ReportDeviceInfoOptions } from '../types/types-options.js';
|
|
3
|
+
/** 格式化为 yyyy-MM-dd HH:mm:ss */
|
|
4
|
+
export declare const formatCollectTime: (date?: Date) => string;
|
|
5
|
+
/**
|
|
6
|
+
* 将设备信息 + 业务埋点字段上报到业务接口
|
|
7
|
+
* @returns 上报是否成功
|
|
8
|
+
*/
|
|
9
|
+
export declare const reportDeviceInfoRequest: (device: FlatbizDevice, options: ReportDeviceInfoOptions) => Promise<boolean>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { getAuthHeaders, getReportDeviceInfoUrl } from '../constant.js';
|
|
2
|
+
const pad2 = (n) => String(n).padStart(2, '0');
|
|
3
|
+
/** 格式化为 yyyy-MM-dd HH:mm:ss */
|
|
4
|
+
export const formatCollectTime = (date = new Date()) => {
|
|
5
|
+
const y = date.getFullYear();
|
|
6
|
+
const m = pad2(date.getMonth() + 1);
|
|
7
|
+
const d = pad2(date.getDate());
|
|
8
|
+
const hh = pad2(date.getHours());
|
|
9
|
+
const mm = pad2(date.getMinutes());
|
|
10
|
+
const ss = pad2(date.getSeconds());
|
|
11
|
+
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
|
|
12
|
+
};
|
|
13
|
+
const REPORT_SUCCESS_CODE = '0000';
|
|
14
|
+
/** 响应体 DeviceDataResp:code === "0000" 表示成功 */
|
|
15
|
+
const defaultIsSuccess = (_response, data) => {
|
|
16
|
+
if (data && typeof data === 'object' && 'code' in data) {
|
|
17
|
+
return String(data.code) === REPORT_SUCCESS_CODE;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* 将设备信息 + 业务埋点字段上报到业务接口
|
|
23
|
+
* @returns 上报是否成功
|
|
24
|
+
*/
|
|
25
|
+
export const reportDeviceInfoRequest = async (device, options) => {
|
|
26
|
+
const { isDebug = false, isProd = true, signature, ...bizParams } = options;
|
|
27
|
+
try {
|
|
28
|
+
const authHeaders = getAuthHeaders(signature);
|
|
29
|
+
const deviceSdkInfo = { ...device };
|
|
30
|
+
if ('ip' in deviceSdkInfo) {
|
|
31
|
+
delete deviceSdkInfo.ip;
|
|
32
|
+
}
|
|
33
|
+
const response = await fetch(getReportDeviceInfoUrl(isProd), {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: {
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
...authHeaders,
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
deviceSdkInfo,
|
|
41
|
+
orderInfo: {
|
|
42
|
+
eventType: 'pageLoad',
|
|
43
|
+
collectTime: formatCollectTime(),
|
|
44
|
+
...bizParams,
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
if (isDebug) {
|
|
50
|
+
console.error('上报设备信息失败', response.status, response.statusText);
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
let data = undefined;
|
|
55
|
+
const contentType = response.headers.get('content-type') || '';
|
|
56
|
+
if (contentType.includes('application/json')) {
|
|
57
|
+
try {
|
|
58
|
+
data = await response.json();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
data = undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const success = defaultIsSuccess(response, data);
|
|
65
|
+
if (!success && isDebug) {
|
|
66
|
+
if (isDebug)
|
|
67
|
+
console.error('上报设备信息业务失败', data);
|
|
68
|
+
}
|
|
69
|
+
return success;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (isDebug)
|
|
73
|
+
console.error('上报设备信息异常', error?.message);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { FlatbizDevice } from './types/types-device.js';
|
|
2
|
-
import type { DeviceInfoOptions } from './types/types-options.js';
|
|
2
|
+
import type { DeviceInfoOptions, ReportDeviceInfoOptions } from './types/types-options.js';
|
|
3
|
+
export type { FlatbizDevice, FlatbizDeviceInfo, FlatbizUaParserResult, } from './types/types-device.js';
|
|
4
|
+
export type { DeviceInfoOptions, DeviceReportAuthInfo, ReportDeviceBizParams, ReportDeviceInfoOptions, ReportPageName, } from './types/types-options.js';
|
|
5
|
+
export { ReportDeviceType, ReportRegionType, ReportUserSys, } from './types/types-options.js';
|
|
3
6
|
/**
|
|
4
7
|
* 获取设备信息
|
|
5
8
|
* @returns FlatbizDevice
|
|
6
9
|
*/
|
|
7
10
|
export declare const deviceInfo: (options?: DeviceInfoOptions) => Promise<FlatbizDevice>;
|
|
11
|
+
/**
|
|
12
|
+
* 上报设备信息
|
|
13
|
+
* 调用时会先重置并缓存 eventId,再采集设备信息并写入 deviceSdkInfo
|
|
14
|
+
*/
|
|
15
|
+
export declare const reportDeviceInfo: (options: ReportDeviceInfoOptions) => Promise<boolean>;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getDeviceInfoH5 } from './helpers/helper-device-info.js';
|
|
2
|
+
import { getEventId, resetEventId } from './helpers/helper-event-id.js';
|
|
3
|
+
import { reportDeviceInfoRequest } from './helpers/helper-report-device-info.js';
|
|
2
4
|
import { getUaParserResult } from './helpers/helper-ua-parser.js';
|
|
5
|
+
export { ReportDeviceType, ReportRegionType, ReportUserSys, } from './types/types-options.js';
|
|
3
6
|
/**
|
|
4
7
|
* 获取设备信息
|
|
5
8
|
* @returns FlatbizDevice
|
|
@@ -14,9 +17,11 @@ export const deviceInfo = async (options) => {
|
|
|
14
17
|
}
|
|
15
18
|
const deviceInfo = await getDeviceInfoH5({ ip, autoGetIp });
|
|
16
19
|
const uaParserResult = getUaParserResult();
|
|
20
|
+
const eventId = getEventId();
|
|
17
21
|
return {
|
|
18
22
|
...deviceInfo,
|
|
19
23
|
...uaParserResult,
|
|
24
|
+
...(eventId ? { eventId } : {}),
|
|
20
25
|
};
|
|
21
26
|
}
|
|
22
27
|
catch (error) {
|
|
@@ -25,3 +30,26 @@ export const deviceInfo = async (options) => {
|
|
|
25
30
|
return {};
|
|
26
31
|
}
|
|
27
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* 上报设备信息
|
|
35
|
+
* 调用时会先重置并缓存 eventId,再采集设备信息并写入 deviceSdkInfo
|
|
36
|
+
*/
|
|
37
|
+
export const reportDeviceInfo = async (options) => {
|
|
38
|
+
const { isDebug = false } = options || {};
|
|
39
|
+
try {
|
|
40
|
+
if (typeof window === 'undefined') {
|
|
41
|
+
if (isDebug)
|
|
42
|
+
console.error('请在浏览器环境中使用');
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// 仅在上报时生成/重置 eventId
|
|
46
|
+
resetEventId();
|
|
47
|
+
const info = await deviceInfo({ isDebug });
|
|
48
|
+
return reportDeviceInfoRequest(info, options);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (isDebug)
|
|
52
|
+
console.error('上报设备信息异常', error?.message);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -42,6 +42,12 @@ export interface FlatbizDeviceInfo {
|
|
|
42
42
|
pluginNum: number;
|
|
43
43
|
/** 浏览器是否启用 cookies */
|
|
44
44
|
cookiesEnable: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* 事件 ID
|
|
47
|
+
* 仅在调用 reportDeviceInfo 时生成/重置并缓存;
|
|
48
|
+
* deviceInfo 读取当前缓存值一并返回(尚未上报过时可能为空)
|
|
49
|
+
*/
|
|
50
|
+
eventId?: string;
|
|
45
51
|
}
|
|
46
52
|
/**
|
|
47
53
|
* FlatbizUaParserResult
|
|
@@ -20,3 +20,102 @@ export type DeviceInfoOptions = {
|
|
|
20
20
|
*/
|
|
21
21
|
isDebug?: boolean;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* 鉴权信息:
|
|
25
|
+
* - string:直接作为 `Authorization` 请求头
|
|
26
|
+
* - 对象:`token` 写入 Authorization,`headers` 合并进请求头
|
|
27
|
+
*/
|
|
28
|
+
export type DeviceReportAuthInfo = string | {
|
|
29
|
+
token?: string;
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
};
|
|
32
|
+
/** 埋点采集页面 */
|
|
33
|
+
export type ReportPageName = 'detail' | 'order' | 'publicCashier' | 'nativeCashier';
|
|
34
|
+
/**
|
|
35
|
+
* 会员体系
|
|
36
|
+
* - 01: 同程会员体系
|
|
37
|
+
* - 02: HopeGoo 会员体系
|
|
38
|
+
* - 03: Tabigo 会员体系
|
|
39
|
+
*/
|
|
40
|
+
export declare enum ReportUserSys {
|
|
41
|
+
/** 同程会员体系 */
|
|
42
|
+
Tongcheng = "01",
|
|
43
|
+
/** HopeGoo 会员体系 */
|
|
44
|
+
HopeGoo = "02",
|
|
45
|
+
/** Tabigo 会员体系 */
|
|
46
|
+
Tabigo = "03"
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 区域类型
|
|
50
|
+
* - 01: 国内
|
|
51
|
+
* - 02: 国外
|
|
52
|
+
*/
|
|
53
|
+
export declare enum ReportRegionType {
|
|
54
|
+
/** 国内 */
|
|
55
|
+
Domestic = "01",
|
|
56
|
+
/** 国外 */
|
|
57
|
+
Overseas = "02"
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 设备类型
|
|
61
|
+
* - 1: PC
|
|
62
|
+
* - 3: H5
|
|
63
|
+
* - 4: HopeGoo APP
|
|
64
|
+
* - 5: Tabigo APP
|
|
65
|
+
*/
|
|
66
|
+
export declare enum ReportDeviceType {
|
|
67
|
+
/** PC */
|
|
68
|
+
PC = "1",
|
|
69
|
+
/** H5 */
|
|
70
|
+
H5 = "3",
|
|
71
|
+
/** HopeGoo APP */
|
|
72
|
+
HopeGooApp = "4",
|
|
73
|
+
/** Tabigo APP */
|
|
74
|
+
TabigoApp = "5"
|
|
75
|
+
}
|
|
76
|
+
/** 上报业务入参(不含设备指纹字段) */
|
|
77
|
+
export type ReportDeviceBizParams = {
|
|
78
|
+
/**
|
|
79
|
+
* 埋点采集页面
|
|
80
|
+
* - detail:产品详情页面
|
|
81
|
+
* - order:创单页面
|
|
82
|
+
* - publicCashier:公共收银台
|
|
83
|
+
* - nativeCashier:原生收银台
|
|
84
|
+
*/
|
|
85
|
+
pageName: ReportPageName;
|
|
86
|
+
/**
|
|
87
|
+
* 项目订单号
|
|
88
|
+
* pageName=detail 时无该字段值
|
|
89
|
+
*/
|
|
90
|
+
orderNum: string;
|
|
91
|
+
/** 区域类型 */
|
|
92
|
+
regionType: ReportRegionType;
|
|
93
|
+
/** 设备类型 */
|
|
94
|
+
deviceType: ReportDeviceType;
|
|
95
|
+
/** 用户 ID */
|
|
96
|
+
userId?: string;
|
|
97
|
+
/** 会员体系 */
|
|
98
|
+
userSys?: ReportUserSys;
|
|
99
|
+
/**
|
|
100
|
+
* 事件类型
|
|
101
|
+
* - pageLoad:页面加载
|
|
102
|
+
* - click:点击事件
|
|
103
|
+
* - 其他:自定义事件
|
|
104
|
+
*/
|
|
105
|
+
eventType?: 'pageLoad' | 'click' | string;
|
|
106
|
+
};
|
|
107
|
+
export type ReportDeviceInfoOptions = Pick<DeviceInfoOptions, 'isDebug'> & ReportDeviceBizParams & {
|
|
108
|
+
/**
|
|
109
|
+
* 鉴权签名,写入请求头 `X-signature`
|
|
110
|
+
* 由业务侧传入
|
|
111
|
+
*/
|
|
112
|
+
signature: string;
|
|
113
|
+
/**
|
|
114
|
+
* 是否生产环境
|
|
115
|
+
* 默认 true
|
|
116
|
+
*
|
|
117
|
+
* - true:使用生产上报地址
|
|
118
|
+
* - false:使用非生产上报地址
|
|
119
|
+
*/
|
|
120
|
+
isProd?: boolean;
|
|
121
|
+
};
|
|
@@ -1 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 会员体系
|
|
3
|
+
* - 01: 同程会员体系
|
|
4
|
+
* - 02: HopeGoo 会员体系
|
|
5
|
+
* - 03: Tabigo 会员体系
|
|
6
|
+
*/
|
|
7
|
+
export var ReportUserSys;
|
|
8
|
+
(function (ReportUserSys) {
|
|
9
|
+
/** 同程会员体系 */
|
|
10
|
+
ReportUserSys["Tongcheng"] = "01";
|
|
11
|
+
/** HopeGoo 会员体系 */
|
|
12
|
+
ReportUserSys["HopeGoo"] = "02";
|
|
13
|
+
/** Tabigo 会员体系 */
|
|
14
|
+
ReportUserSys["Tabigo"] = "03";
|
|
15
|
+
})(ReportUserSys || (ReportUserSys = {}));
|
|
16
|
+
/**
|
|
17
|
+
* 区域类型
|
|
18
|
+
* - 01: 国内
|
|
19
|
+
* - 02: 国外
|
|
20
|
+
*/
|
|
21
|
+
export var ReportRegionType;
|
|
22
|
+
(function (ReportRegionType) {
|
|
23
|
+
/** 国内 */
|
|
24
|
+
ReportRegionType["Domestic"] = "01";
|
|
25
|
+
/** 国外 */
|
|
26
|
+
ReportRegionType["Overseas"] = "02";
|
|
27
|
+
})(ReportRegionType || (ReportRegionType = {}));
|
|
28
|
+
/**
|
|
29
|
+
* 设备类型
|
|
30
|
+
* - 1: PC
|
|
31
|
+
* - 3: H5
|
|
32
|
+
* - 4: HopeGoo APP
|
|
33
|
+
* - 5: Tabigo APP
|
|
34
|
+
*/
|
|
35
|
+
export var ReportDeviceType;
|
|
36
|
+
(function (ReportDeviceType) {
|
|
37
|
+
/** PC */
|
|
38
|
+
ReportDeviceType["PC"] = "1";
|
|
39
|
+
/** H5 */
|
|
40
|
+
ReportDeviceType["H5"] = "3";
|
|
41
|
+
/** HopeGoo APP */
|
|
42
|
+
ReportDeviceType["HopeGooApp"] = "4";
|
|
43
|
+
/** Tabigo APP */
|
|
44
|
+
ReportDeviceType["TabigoApp"] = "5";
|
|
45
|
+
})(ReportDeviceType || (ReportDeviceType = {}));
|