@lark-apaas/devtool-kits 1.0.4 → 1.0.5-alpha.2

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/error.html CHANGED
@@ -111,6 +111,12 @@
111
111
  let errorData = {
112
112
  message: `{{.errorData.message}}`,
113
113
  };
114
+
115
+ // 探针配置
116
+ const PROBE_INTERVAL = 2000; // 探测间隔 2 秒
117
+ const PROBE_TIMEOUT = 5000; // 单次请求超时 5 秒
118
+ let probeTimer = null;
119
+
114
120
  // 初始化页面
115
121
  function init() {
116
122
  // 通知前端,渲染错误页面已准备就绪
@@ -125,6 +131,73 @@
125
131
  data: errorData,
126
132
  });
127
133
  }
134
+
135
+ // 启动服务恢复探针
136
+ startServiceProbe();
137
+ }
138
+
139
+ // 启动服务恢复探针
140
+ function startServiceProbe() {
141
+ console.log('[Probe] 启动服务恢复探针,间隔:', PROBE_INTERVAL, 'ms');
142
+
143
+ // 立即执行一次探测
144
+ probeService();
145
+
146
+ // 设置定时探测
147
+ probeTimer = setInterval(probeService, PROBE_INTERVAL);
148
+ }
149
+
150
+ // 停止探针
151
+ function stopServiceProbe() {
152
+ if (probeTimer) {
153
+ clearInterval(probeTimer);
154
+ probeTimer = null;
155
+ console.log('[Probe] 服务恢复探针已停止');
156
+ }
157
+ }
158
+
159
+ // 探测服务是否恢复
160
+ async function probeService() {
161
+ try {
162
+ // 获取当前 URL
163
+ const currentUrl = window.location.pathname + window.location.search;
164
+
165
+ console.log('[Probe] 探测服务状态:', currentUrl);
166
+
167
+ // 使用 AbortController 实现超时控制
168
+ const controller = new AbortController();
169
+ const timeoutId = setTimeout(() => controller.abort(), PROBE_TIMEOUT);
170
+
171
+ const response = await fetch(currentUrl, {
172
+ method: 'HEAD', // 使用 HEAD 方法,只检查状态不下载内容
173
+ cache: 'no-cache',
174
+ signal: controller.signal
175
+ });
176
+
177
+ clearTimeout(timeoutId);
178
+
179
+ // 检查响应状态
180
+ if (response.ok && response.status === 200) {
181
+ console.log('[Probe] 服务已恢复,准备刷新页面');
182
+ stopServiceProbe();
183
+
184
+ // 延迟一小段时间后刷新,让日志输出完整
185
+ setTimeout(() => {
186
+ console.log('[Probe] 刷新页面...');
187
+ window.location.href = currentUrl;
188
+ }, 300);
189
+ } else {
190
+ console.log('[Probe] 服务未恢复,状态码:', response.status);
191
+ }
192
+ } catch (error) {
193
+ // 网络错误、超时或服务未恢复
194
+ if (error.name === 'AbortError') {
195
+ console.log('[Probe] 探测超时');
196
+ } else {
197
+ console.log('[Probe] 探测失败:', error.message);
198
+ }
199
+ // 继续下一次探测
200
+ }
128
201
  }
129
202
 
130
203
  // 复制错误信息到剪贴板
@@ -166,6 +239,7 @@
166
239
 
167
240
  // 告诉妙搭修复
168
241
  function handleRepair() {
242
+ console.log('[Render Error Repair] 告诉妙搭修复错误:', errorData);
169
243
  sendPostMessage({
170
244
  type: 'RenderErrorRepair',
171
245
  data: errorData,
@@ -196,19 +270,20 @@
196
270
 
197
271
  // 复制到剪贴板
198
272
  async function copyToClipboard(text) {
199
- try {
200
- // 优先使用现代的 Clipboard API
201
- if (navigator.clipboard && window.isSecureContext) {
273
+ // 优先使用现代的 Clipboard API
274
+ if (navigator.clipboard && window.isSecureContext) {
275
+ try {
202
276
  await navigator.clipboard.writeText(text);
203
277
  return true;
278
+ } catch (error) {
279
+ // 权限被拒绝或其他错误,降级到 execCommand
280
+ console.warn('Clipboard API 失败,降级到 execCommand:', error);
281
+ return fallbackCopyToClipboard(text);
204
282
  }
205
-
206
- // 降级方案:使用传统的 execCommand 方法
207
- return fallbackCopyToClipboard(text);
208
- } catch (error) {
209
- console.error('复制到剪切板失败:', error);
210
- return false;
211
283
  }
284
+
285
+ // 降级方案:使用传统的 execCommand 方法
286
+ return fallbackCopyToClipboard(text);
212
287
  }
213
288
 
214
289
  // 降级复制方案(兼容旧浏览器)
@@ -261,7 +336,7 @@
261
336
  class="error-image"
262
337
  />
263
338
  <p class="title">哎呀,写错代码了</p>
264
- <p class="description">可复制错误信息,或告诉妙搭进行修复</p>
339
+ <p class="description">可复制错误信息,或告诉妙搭进行修复。</p>
265
340
  <div class="button-group">
266
341
  <button class="button button-copy" id="copyBtn" onclick="handleCopy()">
267
342
  复制错误信息
package/dist/index.cjs CHANGED
@@ -28407,7 +28407,52 @@ var import_node_fs2 = __toESM(require("fs"), 1);
28407
28407
  var import_node_path2 = __toESM(require("path"), 1);
28408
28408
  var import_node_fs3 = require("fs");
28409
28409
  var import_node_readline = require("readline");
28410
+ var import_node_net = require("net");
28410
28411
  var errorHtmlTemplate = null;
28412
+ function isConnectionError(err) {
28413
+ const code = err.code;
28414
+ const connectionErrorCodes = [
28415
+ "ECONNREFUSED",
28416
+ "ECONNRESET",
28417
+ "ETIMEDOUT",
28418
+ "ENOTFOUND",
28419
+ "ENETUNREACH"
28420
+ ];
28421
+ return connectionErrorCodes.includes(code || "");
28422
+ }
28423
+ __name(isConnectionError, "isConnectionError");
28424
+ function checkServiceAvailable(host, port, timeout = 1e3) {
28425
+ return new Promise((resolve) => {
28426
+ const socket = (0, import_node_net.connect)(port, host);
28427
+ const timer = setTimeout(() => {
28428
+ socket.destroy();
28429
+ resolve(false);
28430
+ }, timeout);
28431
+ socket.on("connect", () => {
28432
+ clearTimeout(timer);
28433
+ socket.destroy();
28434
+ resolve(true);
28435
+ });
28436
+ socket.on("error", () => {
28437
+ clearTimeout(timer);
28438
+ socket.destroy();
28439
+ resolve(false);
28440
+ });
28441
+ });
28442
+ }
28443
+ __name(checkServiceAvailable, "checkServiceAvailable");
28444
+ async function waitForServiceRecovery(host, port, timeout, interval) {
28445
+ const startTime = Date.now();
28446
+ while (Date.now() - startTime < timeout) {
28447
+ const isAvailable = await checkServiceAvailable(host, port);
28448
+ if (isAvailable) {
28449
+ return true;
28450
+ }
28451
+ await new Promise((resolve) => setTimeout(resolve, interval));
28452
+ }
28453
+ return false;
28454
+ }
28455
+ __name(waitForServiceRecovery, "waitForServiceRecovery");
28411
28456
  function getDirname() {
28412
28457
  return __dirname;
28413
28458
  }
@@ -28436,7 +28481,10 @@ async function readRecentErrorLogs(logDir, maxLogs, fileName) {
28436
28481
  try {
28437
28482
  await import_node_fs2.default.promises.access(logFilePath);
28438
28483
  } catch {
28439
- return [];
28484
+ return {
28485
+ logs: [],
28486
+ hasCompileError: false
28487
+ };
28440
28488
  }
28441
28489
  const allLines = [];
28442
28490
  const stream = (0, import_node_fs3.createReadStream)(logFilePath, {
@@ -28465,20 +28513,26 @@ async function readRecentErrorLogs(logDir, maxLogs, fileName) {
28465
28513
  }
28466
28514
  }
28467
28515
  if (startIndex === -1) {
28468
- return [];
28516
+ return {
28517
+ logs: [],
28518
+ hasCompileError: false
28519
+ };
28469
28520
  }
28470
28521
  let endIndex = allLines.length;
28522
+ let hasCompileError = false;
28471
28523
  for (let i = startIndex; i < allLines.length; i++) {
28472
28524
  if (/Found \d+ errors?\./.test(allLines[i])) {
28473
28525
  endIndex = i + 1;
28526
+ hasCompileError = true;
28474
28527
  break;
28475
28528
  }
28476
28529
  }
28477
28530
  const errorSection = allLines.slice(startIndex, endIndex);
28478
- if (errorSection.length > maxLogs) {
28479
- return errorSection.slice(-maxLogs);
28480
- }
28481
- return errorSection;
28531
+ const logs = errorSection.length > maxLogs ? errorSection.slice(-maxLogs) : errorSection;
28532
+ return {
28533
+ logs,
28534
+ hasCompileError
28535
+ };
28482
28536
  }
28483
28537
  __name(readRecentErrorLogs, "readRecentErrorLogs");
28484
28538
  function injectErrorData(template, errorLogs) {
@@ -28492,8 +28546,8 @@ function injectErrorData(template, errorLogs) {
28492
28546
  ${JSON.stringify(logsText)}`);
28493
28547
  }
28494
28548
  __name(injectErrorData, "injectErrorData");
28495
- function handleDevProxyError(err, _req, res, options) {
28496
- const { logDir = import_node_path2.default.join(process.cwd(), "logs"), maxErrorLogs = 100, logFileName = "server.log" } = options || {};
28549
+ function handleDevProxyError(err, req, res, options) {
28550
+ const { logDir = import_node_path2.default.join(process.cwd(), "logs"), maxErrorLogs = 100, logFileName = "server.log", retryTimeout = 5e3, retryInterval = 500, target } = options || {};
28497
28551
  console.error("[Proxy Error]:", err.message);
28498
28552
  if (res.headersSent) {
28499
28553
  console.error("[Proxy Error]: Headers already sent, cannot send error page");
@@ -28501,7 +28555,31 @@ function handleDevProxyError(err, _req, res, options) {
28501
28555
  }
28502
28556
  (async () => {
28503
28557
  try {
28504
- const errorLogs = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
28558
+ const isConnError = isConnectionError(err);
28559
+ const { logs: errorLogs, hasCompileError } = await readRecentErrorLogs(logDir, maxErrorLogs, logFileName);
28560
+ if (isConnError && !hasCompileError && target) {
28561
+ console.log("[Proxy Error]: Connection error without compile errors, possibly server restarting...");
28562
+ let targetUrl = null;
28563
+ try {
28564
+ targetUrl = new URL(target);
28565
+ } catch (e) {
28566
+ console.error("[Proxy Error]: Invalid target URL:", target);
28567
+ }
28568
+ if (targetUrl) {
28569
+ const host = targetUrl.hostname;
28570
+ const port = targetUrl.port ? parseInt(targetUrl.port) : targetUrl.protocol === "https:" ? 443 : 80;
28571
+ console.log(`[Proxy Error]: Waiting for service recovery at ${host}:${port} (timeout: ${retryTimeout}ms)...`);
28572
+ const recovered = await waitForServiceRecovery(host, port, retryTimeout, retryInterval);
28573
+ if (recovered) {
28574
+ console.log("[Proxy Error]: Service recovered, sending redirect");
28575
+ sendSimpleRedirect(req, res);
28576
+ return;
28577
+ }
28578
+ console.log("[Proxy Error]: Service did not recover within timeout, showing error page");
28579
+ }
28580
+ console.log("[Proxy Error]: Falling back to scenario 2 (detailed error page with probe)");
28581
+ }
28582
+ console.log("[Proxy Error]: Compile error or non-connection error, showing error page");
28505
28583
  const template = getErrorHtmlTemplate();
28506
28584
  const html = injectErrorData(template, errorLogs);
28507
28585
  res.writeHead(502, {
@@ -28510,7 +28588,7 @@ function handleDevProxyError(err, _req, res, options) {
28510
28588
  });
28511
28589
  res.end(html);
28512
28590
  } catch (error) {
28513
- console.error("[Proxy Error]: Failed to send error page:", error);
28591
+ console.error("[Proxy Error]: Failed to handle error:", error);
28514
28592
  if (!res.headersSent) {
28515
28593
  res.writeHead(502, {
28516
28594
  "Content-Type": "text/plain; charset=utf-8"
@@ -28521,6 +28599,17 @@ function handleDevProxyError(err, _req, res, options) {
28521
28599
  })();
28522
28600
  }
28523
28601
  __name(handleDevProxyError, "handleDevProxyError");
28602
+ function sendSimpleRedirect(req, res) {
28603
+ if (res.headersSent) return;
28604
+ const originalUrl = req.url || "/";
28605
+ console.log("[Proxy Error]: Sending 302 redirect to", originalUrl);
28606
+ res.writeHead(302, {
28607
+ "Location": originalUrl,
28608
+ "Cache-Control": "no-cache, no-store, must-revalidate"
28609
+ });
28610
+ res.end();
28611
+ }
28612
+ __name(sendSimpleRedirect, "sendSimpleRedirect");
28524
28613
 
28525
28614
  // src/middlewares/index.ts
28526
28615
  var import_node_path8 = __toESM(require("path"), 1);