befly-shared 2.28.0 → 2.30.0

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.
@@ -7,10 +7,7 @@ async function returnPayload(payload) {
7
7
  export function assertReportRequest(options, operation) {
8
8
  if (!options || (typeof options.request !== "function" && typeof options.apiPath !== "string")) {
9
9
  throw new Error(`${operation}.options 必须提供 request 函数或 apiPath 字符串`, {
10
- cause: null,
11
- code: "validation",
12
- subsystem: "shared",
13
- operation: operation
10
+ cause: { code: "validation", subsystem: "shared", operation: operation }
14
11
  });
15
12
  }
16
13
  }
@@ -34,9 +31,9 @@ export function copyReportData(target, source) {
34
31
  return;
35
32
  }
36
33
 
37
- for (const key of Object.keys(source)) {
38
- if (source[key] !== undefined) {
39
- target[key] = source[key];
34
+ for (const [key, value] of Object.entries(source)) {
35
+ if (value !== undefined) {
36
+ target[key] = value;
40
37
  }
41
38
  }
42
39
  }
@@ -68,16 +65,8 @@ export function getRouteReportData(route) {
68
65
 
69
66
  export function getCurrentRouterData(router) {
70
67
  const currentRoute = router?.currentRoute?.value || {};
71
- let fallbackPath = "";
72
- let fallbackTitle = "";
73
-
74
- if (typeof globalThis.window !== "undefined") {
75
- fallbackPath = globalThis.window.location.hash || globalThis.window.location.pathname || "";
76
- }
77
-
78
- if (typeof globalThis.document !== "undefined") {
79
- fallbackTitle = globalThis.document.title || "";
80
- }
68
+ const fallbackPath = typeof globalThis.window !== "undefined" ? globalThis.window.location.hash || globalThis.window.location.pathname || "" : "";
69
+ const fallbackTitle = typeof globalThis.document !== "undefined" ? globalThis.document.title || "" : "";
81
70
 
82
71
  return {
83
72
  pagePath: String(currentRoute.fullPath || currentRoute.path || fallbackPath),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly-shared",
3
- "version": "2.28.0",
3
+ "version": "2.30.0",
4
4
  "gitHead": "70c48ac058875255ac8b54c5f4b5987b997c3bfd",
5
5
  "private": false,
6
6
  "description": "Befly Shared - 通用工具函数库",
@@ -1,11 +1,11 @@
1
1
  import { appendPageAndExtraData, createCommonReportData, getReportRequest } from "../internal/reportUtil.js";
2
2
 
3
- const ONLINE_REPORT_PATH = "/core/tongJi/onlineReport";
4
- const ONLINE_REPORT_MIN_GAP = 15 * 1000;
5
- const ONLINE_REPORT_HEARTBEAT_INTERVAL = 60 * 1000;
6
- const ONLINE_REPORT_CLIENT_ID_KEY = "befly-online-client-id";
3
+ const DAILY_REPORT_PATH = "/core/tongJi/dailyReport";
4
+ const DAILY_REPORT_MIN_GAP = 15 * 1000;
5
+ const DAILY_REPORT_HEARTBEAT_INTERVAL = 10 * 60 * 1000;
6
+ const DAILY_REPORT_CLIENT_ID_KEY = "befly-daily-client-id";
7
7
 
8
- function generateOnlineClientId() {
8
+ function generateDailyClientId() {
9
9
  return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
10
10
  }
11
11
 
@@ -48,8 +48,8 @@ function isDocumentActive() {
48
48
  return true;
49
49
  }
50
50
 
51
- export function createOnlineReport(options) {
52
- const request = getReportRequest(options, "createOnlineReport");
51
+ export function createDailyReport(options) {
52
+ const request = getReportRequest(options, "createDailyReport");
53
53
 
54
54
  let heartbeatStarted = false;
55
55
  let lastReportTime = 0;
@@ -60,13 +60,13 @@ export function createOnlineReport(options) {
60
60
  return String(options.getClientId() || "");
61
61
  }
62
62
 
63
- const storedClientId = readStoredClientId(ONLINE_REPORT_CLIENT_ID_KEY);
63
+ const storedClientId = readStoredClientId(DAILY_REPORT_CLIENT_ID_KEY);
64
64
  if (storedClientId) {
65
65
  return storedClientId;
66
66
  }
67
67
 
68
- const clientId = generateOnlineClientId();
69
- writeStoredClientId(ONLINE_REPORT_CLIENT_ID_KEY, clientId);
68
+ const clientId = generateDailyClientId();
69
+ writeStoredClientId(DAILY_REPORT_CLIENT_ID_KEY, clientId);
70
70
  return clientId;
71
71
  }
72
72
 
@@ -88,29 +88,29 @@ export function createOnlineReport(options) {
88
88
  clearHeartbeatTimer();
89
89
 
90
90
  timeoutId = globalThis.setTimeout(() => {
91
- void triggerOnlineReport();
91
+ void triggerDailyReport();
92
92
  queueHeartbeat();
93
- }, ONLINE_REPORT_HEARTBEAT_INTERVAL);
93
+ }, DAILY_REPORT_HEARTBEAT_INTERVAL);
94
94
  }
95
95
 
96
- async function reportOnline(extraData = {}) {
97
- lastReportTime = Date.now();
98
- return request(ONLINE_REPORT_PATH, buildReportData(options, getClientId(), extraData));
96
+ async function reportDaily(extraData = {}) {
97
+ return request(DAILY_REPORT_PATH, buildReportData(options, getClientId(), extraData));
99
98
  }
100
99
 
101
- async function triggerOnlineReport(force = false, extraData = {}) {
100
+ async function triggerDailyReport(force = false, extraData = {}) {
102
101
  if (!isDocumentActive()) {
103
102
  return false;
104
103
  }
105
104
 
106
105
  const now = Date.now();
107
106
 
108
- if (!force && now - lastReportTime < ONLINE_REPORT_MIN_GAP) {
107
+ if (!force && now - lastReportTime < DAILY_REPORT_MIN_GAP) {
109
108
  return false;
110
109
  }
111
110
 
112
111
  try {
113
- await reportOnline(extraData);
112
+ await reportDaily(extraData);
113
+ lastReportTime = Date.now();
114
114
  return true;
115
115
  } catch {
116
116
  return false;
@@ -126,30 +126,30 @@ export function createOnlineReport(options) {
126
126
 
127
127
  if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
128
128
  window.addEventListener("focus", () => {
129
- void triggerOnlineReport();
129
+ void triggerDailyReport();
130
130
  });
131
131
  }
132
132
 
133
133
  if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
134
134
  document.addEventListener("visibilitychange", () => {
135
- void triggerOnlineReport();
135
+ void triggerDailyReport();
136
136
  });
137
137
  }
138
138
 
139
- void triggerOnlineReport(true);
139
+ void triggerDailyReport(true);
140
140
  queueHeartbeat();
141
141
  }
142
142
 
143
143
  return {
144
- reportOnline: reportOnline,
144
+ reportDaily: reportDaily,
145
145
  startHeartbeat: startHeartbeat
146
146
  };
147
147
  }
148
148
 
149
- export function setupOnlineReport(options) {
150
- const onlineReporter = createOnlineReport(options);
149
+ export function setupDailyReport(options) {
150
+ const dailyReporter = createDailyReport(options);
151
151
 
152
- onlineReporter.startHeartbeat();
152
+ dailyReporter.startHeartbeat();
153
153
 
154
- return onlineReporter;
154
+ return dailyReporter;
155
155
  }
@@ -48,9 +48,9 @@ export function getErrorDetail(error) {
48
48
  export function createErrorReport(options) {
49
49
  const request = getReportRequest(options, "createErrorReport");
50
50
 
51
- let reporting = false;
52
51
  let lastErrorKey = "";
53
52
  let lastErrorTime = 0;
53
+ const pendingKeys = new Set();
54
54
 
55
55
  function shouldSkipError(errorType, message, detail) {
56
56
  const now = Date.now();
@@ -75,11 +75,11 @@ export function createErrorReport(options) {
75
75
  return false;
76
76
  }
77
77
 
78
- if (reporting) {
78
+ if (pendingKeys.has(safeErrorType)) {
79
79
  return false;
80
80
  }
81
81
 
82
- reporting = true;
82
+ pendingKeys.add(safeErrorType);
83
83
 
84
84
  try {
85
85
  await request(ERROR_REPORT_PATH, buildReportData(options, safeErrorType, safeMessage, safeDetail, extraData));
@@ -87,7 +87,7 @@ export function createErrorReport(options) {
87
87
  } catch {
88
88
  return false;
89
89
  } finally {
90
- reporting = false;
90
+ pendingKeys.delete(safeErrorType);
91
91
  }
92
92
  }
93
93
  };
@@ -115,55 +115,7 @@ export function setupErrorReport(options) {
115
115
 
116
116
  if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
117
117
  if (options.reportWindow !== false) {
118
- window.addEventListener(
119
- "error",
120
- (event) => {
121
- const target = event.target;
122
-
123
- if (target && target !== window) {
124
- if (options.reportResource === false) {
125
- return;
126
- }
127
-
128
- const resourceName = target.tagName ? `${String(target.tagName).toLowerCase()} 资源加载失败` : "资源加载失败";
129
- const parts = [];
130
-
131
- if (target?.tagName) {
132
- parts.push(`tag: ${String(target.tagName).toLowerCase()}`);
133
- }
134
-
135
- if (target?.src) {
136
- parts.push(`src: ${target.src}`);
137
- }
138
-
139
- if (target?.href) {
140
- parts.push(`href: ${target.href}`);
141
- }
142
-
143
- reportClientError("resource", resourceName, parts.join("\n"));
144
- return;
145
- }
146
-
147
- const parts = [];
148
-
149
- if (event.filename) {
150
- parts.push(`file: ${event.filename}`);
151
- }
152
-
153
- if (event.lineno || event.colno) {
154
- parts.push(`line: ${event.lineno || 0}, col: ${event.colno || 0}`);
155
- }
156
-
157
- const stack = getErrorDetail(event.error);
158
-
159
- if (stack) {
160
- parts.push(stack);
161
- }
162
-
163
- reportClientError("window", getErrorMessage(event.message || event.error), parts.join("\n"));
164
- },
165
- true
166
- );
118
+ window.addEventListener("error", (event) => createWindowErrorHandler(options, reportClientError, event), true);
167
119
  }
168
120
 
169
121
  if (options.reportPromise !== false) {
@@ -187,6 +139,60 @@ export function setupErrorReport(options) {
187
139
  };
188
140
  }
189
141
 
142
+ function createWindowErrorHandler(options, reportClientError, event) {
143
+ const target = event.target;
144
+
145
+ if (target && target !== window) {
146
+ if (options.reportResource === false) {
147
+ return;
148
+ }
149
+
150
+ reportResourceError(reportClientError, target);
151
+ return;
152
+ }
153
+
154
+ reportWindowError(reportClientError, event);
155
+ }
156
+
157
+ function reportResourceError(reportClientError, target) {
158
+ const resourceName = target.tagName ? `${String(target.tagName).toLowerCase()} 资源加载失败` : "资源加载失败";
159
+ const parts = [];
160
+
161
+ if (target?.tagName) {
162
+ parts.push(`tag: ${String(target.tagName).toLowerCase()}`);
163
+ }
164
+
165
+ if (target?.src) {
166
+ parts.push(`src: ${target.src}`);
167
+ }
168
+
169
+ if (target?.href) {
170
+ parts.push(`href: ${target.href}`);
171
+ }
172
+
173
+ reportClientError("resource", resourceName, parts.join("\n"));
174
+ }
175
+
176
+ function reportWindowError(reportClientError, event) {
177
+ const parts = [];
178
+
179
+ if (event.filename) {
180
+ parts.push(`file: ${event.filename}`);
181
+ }
182
+
183
+ if (event.lineno || event.colno) {
184
+ parts.push(`line: ${event.lineno || 0}, col: ${event.colno || 0}`);
185
+ }
186
+
187
+ const stack = getErrorDetail(event.error);
188
+
189
+ if (stack) {
190
+ parts.push(stack);
191
+ }
192
+
193
+ reportClientError("window", getErrorMessage(event.message || event.error), parts.join("\n"));
194
+ }
195
+
190
196
  function setupFetchErrorReport(options, reportClientError) {
191
197
  if (fetchWrapped) {
192
198
  return;
@@ -198,7 +204,12 @@ function setupFetchErrorReport(options, reportClientError) {
198
204
 
199
205
  const originalFetch = window.fetch.bind(window);
200
206
 
201
- window.fetch = async (...args) => {
207
+ window.fetch = createWrappedFetch(options, reportClientError, originalFetch);
208
+ fetchWrapped = true;
209
+ }
210
+
211
+ function createWrappedFetch(options, reportClientError, originalFetch) {
212
+ return async (...args) => {
202
213
  const input = args[0];
203
214
  const url = typeof input === "string" ? input : input && typeof input.url === "string" ? input.url : "";
204
215
  const apiPath = String(options.apiPath || "");
@@ -209,74 +220,86 @@ function setupFetchErrorReport(options, reportClientError) {
209
220
  const response = await originalFetch(...args);
210
221
 
211
222
  if (isApiRequest && !isErrorReportRequest) {
212
- if (!response.ok) {
213
- const parts = [];
214
-
215
- if (url) {
216
- parts.push(`url: ${url}`);
217
- }
218
-
219
- if (response.status) {
220
- parts.push(`status: ${response.status}`);
221
- }
222
-
223
- reportClientError("http", `请求失败:HTTP ${response.status}`, parts.join("\n"));
224
- } else {
225
- try {
226
- const payload = await response.clone().json();
227
-
228
- if (payload?.code !== 0) {
229
- const parts = [];
230
-
231
- if (url) {
232
- parts.push(`url: ${url}`);
233
- }
234
-
235
- if (response.status) {
236
- parts.push(`status: ${response.status}`);
237
- }
238
-
239
- if (payload?.code !== undefined) {
240
- parts.push(`code: ${payload.code}`);
241
- }
242
-
243
- if (payload?.msg) {
244
- parts.push(`msg: ${payload.msg}`);
245
- }
246
-
247
- if (payload?.detail) {
248
- parts.push(`detail: ${payload.detail}`);
249
- }
250
-
251
- reportClientError("http", payload.msg || "请求失败", parts.join("\n"));
252
- }
253
- } catch {
254
- // ignore
255
- }
256
- }
223
+ reportHttpResponseError(reportClientError, url, response);
257
224
  }
258
225
 
259
226
  return response;
260
227
  } catch (error) {
261
228
  if (isApiRequest && !isErrorReportRequest) {
262
- const parts = [];
229
+ reportHttpFetchError(reportClientError, url, error);
230
+ }
263
231
 
264
- if (url) {
265
- parts.push(`url: ${url}`);
266
- }
232
+ throw error;
233
+ }
234
+ };
235
+ }
267
236
 
268
- const errorDetail = getErrorDetail(error);
237
+ async function reportHttpResponseError(reportClientError, url, response) {
238
+ if (!response.ok) {
239
+ const parts = buildUrlParts(url);
269
240
 
270
- if (errorDetail) {
271
- parts.push(`detail: ${errorDetail}`);
272
- }
241
+ if (response.status) {
242
+ parts.push(`status: ${response.status}`);
243
+ }
273
244
 
274
- reportClientError("http", getErrorMessage(error), parts.join("\n"));
275
- }
245
+ reportClientError("http", `请求失败:HTTP ${response.status}`, parts.join("\n"));
246
+ return;
247
+ }
276
248
 
277
- throw error;
249
+ try {
250
+ const payload = await response.clone().json();
251
+
252
+ if (payload?.code !== 0) {
253
+ reportClientError("http", payload.msg || "请求失败", buildHttpPayloadParts(url, response, payload).join("\n"));
278
254
  }
279
- };
255
+ } catch {
256
+ // ignore
257
+ }
258
+ }
280
259
 
281
- fetchWrapped = true;
260
+ function reportHttpFetchError(reportClientError, url, error) {
261
+ const parts = buildUrlParts(url);
262
+ const errorDetail = getErrorDetail(error);
263
+
264
+ if (errorDetail) {
265
+ parts.push(`detail: ${errorDetail}`);
266
+ }
267
+
268
+ reportClientError("http", getErrorMessage(error), parts.join("\n"));
269
+ }
270
+
271
+ function buildUrlParts(url) {
272
+ const parts = [];
273
+
274
+ if (url) {
275
+ parts.push(`url: ${url}`);
276
+ }
277
+
278
+ return parts;
279
+ }
280
+
281
+ function buildHttpPayloadParts(url, response, payload) {
282
+ const parts = buildUrlParts(url);
283
+
284
+ if (response.status) {
285
+ parts.push(`status: ${response.status}`);
286
+ }
287
+
288
+ if (payload?.code !== undefined) {
289
+ parts.push(`code: ${payload.code}`);
290
+ }
291
+
292
+ if (payload?.msg) {
293
+ parts.push(`msg: ${payload.msg}`);
294
+ }
295
+
296
+ if (payload?.detail) {
297
+ parts.push(`detail: ${payload.detail}`);
298
+ }
299
+
300
+ return parts;
301
+ }
302
+
303
+ export function resetFetchWrapper() {
304
+ fetchWrapped = false;
282
305
  }
@@ -1,31 +0,0 @@
1
- import { appendPageAndExtraData, createCommonReportData, getReportRequest, getRouteReportData } from "../internal/reportUtil.js";
2
-
3
- const INFO_REPORT_PATH = "/core/tongJi/infoReport";
4
-
5
- function buildReportData(options, extraData) {
6
- return appendPageAndExtraData(createCommonReportData(options), options, extraData);
7
- }
8
-
9
- export function createInfoReport(options) {
10
- const request = getReportRequest(options, "createInfoReport");
11
-
12
- return {
13
- reportInfo: function (extraData = {}) {
14
- return request(INFO_REPORT_PATH, buildReportData(options, extraData));
15
- }
16
- };
17
- }
18
-
19
- export function setupInfoReport(options) {
20
- const infoReporter = createInfoReport(options);
21
-
22
- if (options.router && typeof options.router.afterEach === "function") {
23
- options.router.afterEach((to) => {
24
- void infoReporter.reportInfo(getRouteReportData(to)).catch(() => {
25
- // ignore
26
- });
27
- });
28
- }
29
-
30
- return infoReporter;
31
- }