agora-foundation 3.9.1 → 3.10.0-beta

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.
@@ -11,9 +11,6 @@ require("core-js/modules/es.array.concat.js");
11
11
  require("core-js/modules/es.array.for-each.js");
12
12
  require("core-js/modules/es.function.name.js");
13
13
  require("core-js/modules/es.object.to-string.js");
14
- require("core-js/modules/esnext.iterator.constructor.js");
15
- require("core-js/modules/esnext.iterator.for-each.js");
16
- require("core-js/modules/web.dom-collections.for-each.js");
17
14
  function validateParams(handlerError) {
18
15
  return function () {
19
16
  for (var _len = arguments.length, schemas = new Array(_len), _key = 0; _key < _len; _key++) {
@@ -22,7 +22,7 @@ export declare const uint8ArrayToImageData: (target: {
22
22
  buffer?: Uint8Array;
23
23
  width?: number;
24
24
  height?: number;
25
- isWindows: boolean;
25
+ needsColorSwap: boolean;
26
26
  }) => string;
27
27
  export declare const renderMeetingId: (meetingId: string | undefined, roomId: string) => string;
28
28
  export {};
@@ -117,14 +117,17 @@ var uint8ArrayToImageData = exports.uint8ArrayToImageData = function uint8ArrayT
117
117
  var srow = row;
118
118
  var imageData = ctx.createImageData(width, 1);
119
119
  var start = srow * width * 4;
120
- if (target.isWindows) {
120
+ if (target.needsColorSwap) {
121
+ // BGRA -> RGBA 转换:交换 B 和 R 通道
122
+ // Windows 和 Linux 平台的 buffer 格式是 BGRA,需要转换为 RGBA
121
123
  for (var i = 0; i < rowBytes; i += 4) {
122
- imageData.data[i] = target.buffer[start + i + 2];
123
- imageData.data[i + 1] = target.buffer[start + i + 1];
124
- imageData.data[i + 2] = target.buffer[start + i];
125
- imageData.data[i + 3] = target.buffer[start + i + 3];
124
+ imageData.data[i] = target.buffer[start + i + 2]; // R
125
+ imageData.data[i + 1] = target.buffer[start + i + 1]; // G
126
+ imageData.data[i + 2] = target.buffer[start + i]; // B
127
+ imageData.data[i + 3] = target.buffer[start + i + 3]; // A
126
128
  }
127
129
  } else {
130
+ // Mac 平台直接复制(已经是 RGBA 格式)
128
131
  for (var _i = 0; _i < rowBytes; ++_i) {
129
132
  imageData.data[_i] = target.buffer[start + _i];
130
133
  }
@@ -3,16 +3,20 @@ export declare const zipDir: (dirPath: string, options?: {
3
3
  filePattern?: RegExp;
4
4
  dirPattern?: RegExp;
5
5
  clean?: boolean;
6
+ fileExtensionPattern?: RegExp;
7
+ zipFileName?: string;
6
8
  }) => Promise<File | null>;
7
9
  /**
8
10
  * 删除目录中不符合规则的旧文件和目录
9
11
  * @param dirPath 目录路径
10
12
  * @param options.filePattern 文件名匹配规则(不符合的删除)
11
13
  * @param options.dirPattern 目录名匹配规则(不符合的删除)
14
+ * @param options.fileExtensionPattern 文件扩展名匹配规则(如 /\.(log|txt)$/),不传则不过滤
12
15
  */
13
16
  export declare const cleanDir: (dirPath: string, options?: {
14
17
  filePattern?: RegExp;
15
18
  dirPattern?: RegExp;
19
+ fileExtensionPattern?: RegExp;
16
20
  }) => Promise<void>;
17
21
  export declare const createMultiEntryZip: () => {
18
22
  addFile: (fileName: string, content: string) => void;
@@ -42,7 +42,7 @@ var COMPRESSION_LEVEL = 9;
42
42
  var zipDir = exports.zipDir = function zipDir(dirPath, options) {
43
43
  return new Promise(/*#__PURE__*/function () {
44
44
  var _ref = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee2(resolve) {
45
- var fs, recursive, files, zip, _iterator, _step, _step$value, relativePath, absolutePath, logsStr, blob, buffer, now, datefmt, file, _t2;
45
+ var fs, recursive, files, zip, _iterator, _step, _step$value, relativePath, absolutePath, logsStr, blob, buffer, now, datefmt, fileName, file, _t2;
46
46
  return _regenerator["default"].wrap(function (_context2) {
47
47
  while (1) switch (_context2.prev = _context2.next) {
48
48
  case 0:
@@ -57,7 +57,8 @@ var zipDir = exports.zipDir = function zipDir(dirPath, options) {
57
57
  _context2.next = 1;
58
58
  return cleanDir(dirPath, {
59
59
  filePattern: options.filePattern,
60
- dirPattern: options.dirPattern
60
+ dirPattern: options.dirPattern,
61
+ fileExtensionPattern: options.fileExtensionPattern
61
62
  });
62
63
  case 1:
63
64
  if (!recursive) {
@@ -67,7 +68,8 @@ var zipDir = exports.zipDir = function zipDir(dirPath, options) {
67
68
  _context2.next = 2;
68
69
  return _collectFiles(dirPath, {
69
70
  filePattern: options === null || options === void 0 ? void 0 : options.filePattern,
70
- dirPattern: options === null || options === void 0 ? void 0 : options.dirPattern
71
+ dirPattern: options === null || options === void 0 ? void 0 : options.dirPattern,
72
+ fileExtensionPattern: options === null || options === void 0 ? void 0 : options.fileExtensionPattern
71
73
  });
72
74
  case 2:
73
75
  files = _context2.sent;
@@ -107,7 +109,8 @@ var zipDir = exports.zipDir = function zipDir(dirPath, options) {
107
109
  buffer = _context2.sent;
108
110
  now = new Date();
109
111
  datefmt = now.toISOString().replace(/\.\d{3}Z$/, 'Z') + 'Z';
110
- file = new File([buffer], "logs-".concat(datefmt, ".zip"), {
112
+ fileName = (options === null || options === void 0 ? void 0 : options.zipFileName) || "logs-".concat(datefmt);
113
+ file = new File([buffer], "".concat(fileName, ".zip"), {
111
114
  type: 'application/zip'
112
115
  });
113
116
  resolve(file);
@@ -117,7 +120,7 @@ var zipDir = exports.zipDir = function zipDir(dirPath, options) {
117
120
  // 原有逻辑:只处理顶层文件
118
121
  fs.readdir(dirPath, /*#__PURE__*/function () {
119
122
  var _ref2 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(err, files) {
120
- var zip, _iterator2, _step2, _file, filePath, _logsStr, blob, buffer, now, datefmt, file, _t;
123
+ var zip, _iterator2, _step2, _file, filePath, _logsStr, blob, buffer, now, datefmt, fileName, file, _t;
121
124
  return _regenerator["default"].wrap(function (_context) {
122
125
  while (1) switch (_context.prev = _context.next) {
123
126
  case 0:
@@ -142,42 +145,48 @@ var zipDir = exports.zipDir = function zipDir(dirPath, options) {
142
145
  _iterator2.s();
143
146
  case 4:
144
147
  if ((_step2 = _iterator2.n()).done) {
145
- _context.next = 8;
148
+ _context.next = 9;
146
149
  break;
147
150
  }
148
151
  _file = _step2.value;
149
- if (!(options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(_file))) {
152
+ if (!(options !== null && options !== void 0 && options.fileExtensionPattern && !options.fileExtensionPattern.test(_file))) {
150
153
  _context.next = 5;
151
154
  break;
152
155
  }
153
- return _context.abrupt("continue", 7);
156
+ return _context.abrupt("continue", 8);
154
157
  case 5:
158
+ if (!(options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(_file))) {
159
+ _context.next = 6;
160
+ break;
161
+ }
162
+ return _context.abrupt("continue", 8);
163
+ case 6:
155
164
  filePath = "".concat(dirPath, "/").concat(_file);
156
- _context.next = 6;
165
+ _context.next = 7;
157
166
  return isFile(filePath);
158
- case 6:
167
+ case 7:
159
168
  if (!_context.sent) {
160
- _context.next = 7;
169
+ _context.next = 8;
161
170
  break;
162
171
  }
163
172
  _logsStr = fs.readFileSync(filePath, 'utf8');
164
173
  zip.file(_file, _logsStr);
165
- case 7:
166
- _context.next = 4;
167
- break;
168
174
  case 8:
169
- _context.next = 10;
175
+ _context.next = 4;
170
176
  break;
171
177
  case 9:
172
- _context.prev = 9;
173
- _t = _context["catch"](3);
174
- _iterator2.e(_t);
178
+ _context.next = 11;
179
+ break;
175
180
  case 10:
176
181
  _context.prev = 10;
177
- _iterator2.f();
178
- return _context.finish(10);
182
+ _t = _context["catch"](3);
183
+ _iterator2.e(_t);
179
184
  case 11:
180
- _context.next = 12;
185
+ _context.prev = 11;
186
+ _iterator2.f();
187
+ return _context.finish(11);
188
+ case 12:
189
+ _context.next = 13;
181
190
  return zip.generateAsync({
182
191
  type: 'blob',
183
192
  compression: 'DEFLATE',
@@ -185,23 +194,24 @@ var zipDir = exports.zipDir = function zipDir(dirPath, options) {
185
194
  level: COMPRESSION_LEVEL
186
195
  }
187
196
  });
188
- case 12:
197
+ case 13:
189
198
  blob = _context.sent;
190
- _context.next = 13;
199
+ _context.next = 14;
191
200
  return blob.arrayBuffer();
192
- case 13:
201
+ case 14:
193
202
  buffer = _context.sent;
194
203
  now = new Date();
195
204
  datefmt = now.toISOString().replace(/\.\d{3}Z$/, 'Z') + 'Z';
196
- file = new File([buffer], "logs-".concat(datefmt, ".zip"), {
205
+ fileName = (options === null || options === void 0 ? void 0 : options.zipFileName) || "logs-".concat(datefmt);
206
+ file = new File([buffer], "".concat(fileName, ".zip"), {
197
207
  type: 'application/zip'
198
208
  });
199
209
  resolve(file);
200
- case 14:
210
+ case 15:
201
211
  case "end":
202
212
  return _context.stop();
203
213
  }
204
- }, _callee, null, [[3, 9, 10, 11]]);
214
+ }, _callee, null, [[3, 10, 11, 12]]);
205
215
  }));
206
216
  return function (_x2, _x3) {
207
217
  return _ref2.apply(this, arguments);
@@ -286,6 +296,7 @@ var isDirectory = /*#__PURE__*/function () {
286
296
  * @param dirPath 目录路径
287
297
  * @param options.filePattern 文件名匹配规则(不符合的删除)
288
298
  * @param options.dirPattern 目录名匹配规则(不符合的删除)
299
+ * @param options.fileExtensionPattern 文件扩展名匹配规则(如 /\.(log|txt)$/),不传则不过滤
289
300
  */
290
301
  var cleanDir = exports.cleanDir = function cleanDir(dirPath, options) {
291
302
  return new Promise(function (resolve) {
@@ -295,7 +306,7 @@ var cleanDir = exports.cleanDir = function cleanDir(dirPath, options) {
295
306
  withFileTypes: true
296
307
  }, /*#__PURE__*/function () {
297
308
  var _ref5 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee5(err, items) {
298
- var _iterator3, _step3, item, itemPath, dirMatches, _t3;
309
+ var _iterator3, _step3, item, itemPath, matchesExtension, matchesPattern, keep, dirMatches, _t3;
299
310
  return _regenerator["default"].wrap(function (_context5) {
300
311
  while (1) switch (_context5.prev = _context5.next) {
301
312
  case 0:
@@ -328,8 +339,11 @@ var cleanDir = exports.cleanDir = function cleanDir(dirPath, options) {
328
339
  _context5.next = 5;
329
340
  break;
330
341
  }
331
- // 文件:检查 filePattern
332
- if (options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(item.name)) {
342
+ // 文件:检查扩展名和 filePattern
343
+ matchesExtension = options !== null && options !== void 0 && options.fileExtensionPattern ? options.fileExtensionPattern.test(item.name) : true;
344
+ matchesPattern = options !== null && options !== void 0 && options.filePattern ? options.filePattern.test(item.name) : true;
345
+ keep = matchesExtension && matchesPattern;
346
+ if (!keep) {
333
347
  fs.unlinkSync(itemPath);
334
348
  console.log("[cleanDir] deleted file: ".concat(itemPath));
335
349
  }
@@ -469,7 +483,12 @@ var _cleanDirRecursively = /*#__PURE__*/function () {
469
483
 
470
484
  /**
471
485
  * 递归遍历目录并收集所有文件
472
- * filePattern 只对顶层目录的文件生效,匹配 dirPattern 的子目录会收集所有文件
486
+ * @param dirPath 目录路径
487
+ * @param fileExtensionPattern 文件扩展名匹配规则(对所有目录生效)
488
+ * @param options.filePattern 文件名匹配规则(只对顶层目录生效)
489
+ * @param options.dirPattern 目录名匹配规则
490
+ * @param baseDir 基础目录路径
491
+ * @param skipFileFilter 是否跳过 filePattern 验证(子目录中使用)
473
492
  */
474
493
  var _collectFiles = /*#__PURE__*/function () {
475
494
  var _ref7 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee7(dirPath, options) {
@@ -481,6 +500,8 @@ var _collectFiles = /*#__PURE__*/function () {
481
500
  _iterator5,
482
501
  _step5,
483
502
  entry,
503
+ patternFail,
504
+ extFail,
484
505
  relativePath,
485
506
  subFiles,
486
507
  _args7 = arguments,
@@ -527,7 +548,13 @@ var _collectFiles = /*#__PURE__*/function () {
527
548
  _context7.next = 6;
528
549
  break;
529
550
  }
530
- if (!(!skipFileFilter && options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(entry.name))) {
551
+ if (skipFileFilter) {
552
+ _context7.next = 5;
553
+ break;
554
+ }
555
+ patternFail = (options === null || options === void 0 ? void 0 : options.filePattern) && !options.filePattern.test(entry.name);
556
+ extFail = (options === null || options === void 0 ? void 0 : options.fileExtensionPattern) && !options.fileExtensionPattern.test(entry.name);
557
+ if (!(patternFail || extFail)) {
531
558
  _context7.next = 5;
532
559
  break;
533
560
  }
@@ -555,7 +582,9 @@ var _collectFiles = /*#__PURE__*/function () {
555
582
  return _context7.abrupt("continue", 10);
556
583
  case 8:
557
584
  _context7.next = 9;
558
- return _collectFiles(entry.path, options, baseDir, true);
585
+ return _collectFiles(entry.path, {
586
+ fileExtensionPattern: options === null || options === void 0 ? void 0 : options.fileExtensionPattern
587
+ }, baseDir, false);
559
588
  case 9:
560
589
  subFiles = _context7.sent;
561
590
  result.push.apply(result, (0, _toConsumableArray2["default"])(subFiles));
@@ -25,6 +25,7 @@ Object.defineProperty(exports, "__esModule", {
25
25
  exports.collectLogsOnFs = exports.clearTempLogs = void 0;
26
26
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
27
27
  require("core-js/modules/es.array.concat.js");
28
+ require("core-js/modules/es.string.ends-with.js");
28
29
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
29
30
  var _jszip = _interopRequireDefault(require("jszip"));
30
31
  var _constants = require("../constants");
@@ -43,7 +44,7 @@ var collectLogsOnFs = exports.collectLogsOnFs = /*#__PURE__*/function () {
43
44
  fs = window.require('fs');
44
45
  fs.readdir(logDirPath, /*#__PURE__*/function () {
45
46
  var _ref2 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(err, files) {
46
- var zip, _iterator, _step, file, filePath, logFileData, blob, buffer;
47
+ var zip, _iterator, _step, file, filePath, logFileData, blob, buffer, _t;
47
48
  return _regenerator["default"].wrap(function (_context) {
48
49
  while (1) switch (_context.prev = _context.next) {
49
50
  case 0:
@@ -55,7 +56,7 @@ var collectLogsOnFs = exports.collectLogsOnFs = /*#__PURE__*/function () {
55
56
  (0, _reply.reply)(self, callId, _constants.WorkerDirectives.COLLECT_LOGS_ON_FS, {
56
57
  isSuccess: false
57
58
  });
58
- _context.next = 5;
59
+ _context.next = 13;
59
60
  break;
60
61
  case 1:
61
62
  if (!(files.length === 0)) {
@@ -66,42 +67,62 @@ var collectLogsOnFs = exports.collectLogsOnFs = /*#__PURE__*/function () {
66
67
  isSuccess: true,
67
68
  buffer: null
68
69
  });
69
- _context.next = 5;
70
+ _context.next = 13;
70
71
  break;
71
72
  case 2:
72
73
  zip = new _jszip["default"]();
73
74
  _iterator = _createForOfIteratorHelper(files);
74
- try {
75
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
76
- file = _step.value;
77
- filePath = "".concat(logDirPath, "/").concat(file);
78
- logFileData = fs.readFileSync(filePath);
79
- zip.file(file, logFileData);
80
- }
81
- } catch (err) {
82
- _iterator.e(err);
83
- } finally {
84
- _iterator.f();
75
+ _context.prev = 3;
76
+ _iterator.s();
77
+ case 4:
78
+ if ((_step = _iterator.n()).done) {
79
+ _context.next = 7;
80
+ break;
81
+ }
82
+ file = _step.value;
83
+ if (file.endsWith('.log')) {
84
+ _context.next = 5;
85
+ break;
85
86
  }
86
- _context.next = 3;
87
+ return _context.abrupt("continue", 6);
88
+ case 5:
89
+ filePath = "".concat(logDirPath, "/").concat(file);
90
+ logFileData = fs.readFileSync(filePath);
91
+ zip.file(file, logFileData);
92
+ case 6:
93
+ _context.next = 4;
94
+ break;
95
+ case 7:
96
+ _context.next = 9;
97
+ break;
98
+ case 8:
99
+ _context.prev = 8;
100
+ _t = _context["catch"](3);
101
+ _iterator.e(_t);
102
+ case 9:
103
+ _context.prev = 9;
104
+ _iterator.f();
105
+ return _context.finish(9);
106
+ case 10:
107
+ _context.next = 11;
87
108
  return zip.generateAsync({
88
109
  type: 'blob'
89
110
  });
90
- case 3:
111
+ case 11:
91
112
  blob = _context.sent;
92
- _context.next = 4;
113
+ _context.next = 12;
93
114
  return blob.arrayBuffer();
94
- case 4:
115
+ case 12:
95
116
  buffer = _context.sent;
96
117
  (0, _reply.reply)(self, callId, _constants.WorkerDirectives.COLLECT_LOGS_ON_FS, {
97
118
  isSuccess: true,
98
119
  buffer: buffer
99
120
  });
100
- case 5:
121
+ case 13:
101
122
  case "end":
102
123
  return _context.stop();
103
124
  }
104
- }, _callee);
125
+ }, _callee, null, [[3, 8, 9, 10]]);
105
126
  }));
106
127
  return function (_x4, _x5) {
107
128
  return _ref2.apply(this, arguments);
@@ -109,7 +130,7 @@ var collectLogsOnFs = exports.collectLogsOnFs = /*#__PURE__*/function () {
109
130
  }());
110
131
  } catch (e) {
111
132
  console.error("[logger] failed to get buffer.", e);
112
- (0, _reply.reply)(self, callId, _constants.WorkerDirectives.GET_BUFFER, {
133
+ (0, _reply.reply)(self, callId, _constants.WorkerDirectives.COLLECT_LOGS_ON_FS, {
113
134
  isSuccess: false
114
135
  });
115
136
  }
@@ -125,7 +146,7 @@ var collectLogsOnFs = exports.collectLogsOnFs = /*#__PURE__*/function () {
125
146
  }();
126
147
  var clearTempLogs = exports.clearTempLogs = /*#__PURE__*/function () {
127
148
  var _ref3 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3(self, callId) {
128
- var _t;
149
+ var _t2;
129
150
  return _regenerator["default"].wrap(function (_context3) {
130
151
  while (1) switch (_context3.prev = _context3.next) {
131
152
  case 0:
@@ -140,8 +161,8 @@ var clearTempLogs = exports.clearTempLogs = /*#__PURE__*/function () {
140
161
  break;
141
162
  case 2:
142
163
  _context3.prev = 2;
143
- _t = _context3["catch"](0);
144
- console.error("[logger] failed to clear temp logs.", _t);
164
+ _t2 = _context3["catch"](0);
165
+ console.error("[logger] failed to clear temp logs.", _t2);
145
166
  (0, _reply.reply)(self, callId, _constants.WorkerDirectives.CLEAR_TEMP_LOGS, {
146
167
  isSuccess: false
147
168
  });
@@ -21,6 +21,7 @@ require("core-js/modules/es.array.map.js");
21
21
  require("core-js/modules/es.array.push.js");
22
22
  require("core-js/modules/es.array.slice.js");
23
23
  require("core-js/modules/es.array.sort.js");
24
+ require("core-js/modules/es.array.splice.js");
24
25
  require("core-js/modules/es.date.to-string.js");
25
26
  require("core-js/modules/es.object.to-string.js");
26
27
  require("core-js/modules/es.string.ends-with.js");
@@ -70,10 +71,6 @@ var createRotateTransport = exports.createRotateTransport = function createRotat
70
71
  _this.path = require('path');
71
72
  return _this;
72
73
  }
73
-
74
- /**
75
- * 生成唯一的日志文件名,避免同一秒多次轮转导致的覆盖
76
- */
77
74
  (0, _inherits2["default"])(CustomRotateTransport, _WinstonTransport);
78
75
  return (0, _createClass2["default"])(CustomRotateTransport, [{
79
76
  key: "generateLogFilename",
@@ -87,10 +84,6 @@ var createRotateTransport = exports.createRotateTransport = function createRotat
87
84
  }
88
85
  return this.rotateIndex === 0 ? "".concat(this.filenamePrefix, "-").concat(timestamp, ".log") : "".concat(this.filenamePrefix, "-").concat(timestamp, "-").concat(this.rotateIndex, ".log");
89
86
  }
90
-
91
- /**
92
- * 创建新日志文件,并清理旧文件
93
- */
94
87
  }, {
95
88
  key: "createNewLogFile",
96
89
  value: function createNewLogFile() {
@@ -109,11 +102,15 @@ var createRotateTransport = exports.createRotateTransport = function createRotat
109
102
  this.currentStream = this.fs.createWriteStream(this.currentFile, {
110
103
  flags: 'a'
111
104
  });
112
- this.currentSize = this.fs.existsSync(this.currentFile) ? this.fs.statSync(this.currentFile).size : 0;
105
+ // 新文件必然从 0 开始,用内存计数器替代 statSync
106
+ this.currentSize = 0;
107
+ this.cleanupOldFiles(this.maxFiles - 1);
113
108
  }
114
109
 
115
110
  /**
116
- * 只允许每个 buffer 触发一次轮转,避免多余文件
111
+ * 与老版本结构完全一致,只做一处改动:
112
+ * 原来在写入回调里用 statSync 读磁盘更新 currentSize,
113
+ * 改为用内存计数器 currentSize += buffer.length,消除每条日志的磁盘读操作。
117
114
  */
118
115
  }, {
119
116
  key: "processQueue",
@@ -125,86 +122,91 @@ var createRotateTransport = exports.createRotateTransport = function createRotat
125
122
  buffer = _ref.buffer,
126
123
  callback = _ref.callback;
127
124
 
128
- // 1. 单条日志大于 maxSize,直接新建新文件并写入,不要理会 currentStream
125
+ // 1. 单条日志大于 maxSize,直接新建新文件并写入
129
126
  if (buffer.length > this.maxSize) {
130
127
  this.createNewLogFile();
131
128
  console.log("[CustomRotateTransport] buffer.length (".concat(buffer.length, ") > maxSize (").concat(this.maxSize, "), force rotate."));
132
129
  this.currentStream.write(buffer, function () {
133
- try {
134
- _this2.currentSize = _this2.fs.statSync(_this2.currentFile).size;
135
- } catch (_unused) {
136
- _this2.currentSize += buffer.length;
137
- }
130
+ _this2.currentSize += buffer.length; // ← 替代 statSync
138
131
  callback();
139
132
  _this2.writing = false;
140
133
  _this2.processQueue();
141
- _this2.cleanupOldFiles();
142
134
  });
143
135
  return;
144
136
  }
145
137
 
146
- // 2. buffer.length <= maxSize,才需要判断 currentStream 是否为空
138
+ // 2. buffer.length <= maxSize,判断 currentStream 是否为空
147
139
  if (!this.currentStream) {
148
140
  this.createNewLogFile();
149
141
  }
150
- var actualSize = 0;
151
- try {
152
- actualSize = this.fs.existsSync(this.currentFile) ? this.fs.statSync(this.currentFile).size : 0;
153
- } catch (_unused2) {
154
- actualSize = this.currentSize;
155
- }
156
- if (actualSize + buffer.length > this.maxSize) {
157
- console.log("[CustomRotateTransport] actualSize (".concat(actualSize, ") + buffer.length (").concat(buffer.length, ") > maxSize (").concat(this.maxSize, "), rotate."));
142
+
143
+ // 用内存计数器替代 existsSync + statSync 判断是否需要轮转
144
+ if (this.currentSize + buffer.length > this.maxSize) {
158
145
  this.createNewLogFile();
159
146
  }
160
147
  this.currentStream.write(buffer, function () {
161
- try {
162
- _this2.currentSize = _this2.fs.statSync(_this2.currentFile).size;
163
- } catch (_unused3) {
164
- _this2.currentSize += buffer.length;
165
- }
148
+ _this2.currentSize += buffer.length;
166
149
  callback();
167
150
  _this2.writing = false;
168
151
  _this2.processQueue();
169
- _this2.cleanupOldFiles();
170
152
  });
171
153
  }
172
154
 
173
155
  /**
174
- * 清理旧日志文件,只保留 maxFiles
156
+ * 只把同步 I/O(readdirSync / statSync / unlinkSync)改为异步回调,
157
+ * 避免阻塞 Worker 线程导致写入队列积压。
175
158
  */
176
159
  }, {
177
160
  key: "cleanupOldFiles",
178
161
  value: function cleanupOldFiles() {
179
162
  var _this3 = this;
180
- try {
181
- var files = this.fs.readdirSync(this.logFolderPath).filter(function (file) {
182
- return file.startsWith(_this3.filenamePrefix) && file.endsWith('.log');
183
- }).map(function (file) {
184
- return {
185
- name: file,
186
- path: _this3.path.join(_this3.logFolderPath, file),
187
- stat: _this3.fs.statSync(_this3.path.join(_this3.logFolderPath, file))
188
- };
189
- }).sort(function (a, b) {
190
- return b.stat.mtime.getTime() - a.stat.mtime.getTime();
163
+ var keepFiles = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.maxFiles;
164
+ var prefix = this.filenamePrefix;
165
+ var logDir = this.logFolderPath;
166
+ console.log("[CustomRotateTransport] cleanupOldFiles: ".concat(logDir));
167
+ this.fs.readdir(logDir, function (err, entries) {
168
+ if (err) {
169
+ console.warn('[CustomRotateTransport] Error reading log folder:', err);
170
+ return;
171
+ }
172
+ var files = entries.filter(function (file) {
173
+ return file.startsWith(prefix) && file.endsWith('.log');
191
174
  });
192
- console.log("[CustomRotateTransport] Found ".concat(files.length, " log files, maxFiles: ").concat(this.maxFiles));
193
- if (files.length > this.maxFiles) {
194
- var filesToDelete = files.slice(this.maxFiles);
195
- console.log("[CustomRotateTransport] Will delete ".concat(filesToDelete.length, " old files"));
196
- filesToDelete.forEach(function (file) {
197
- try {
198
- _this3.fs.unlinkSync(file.path);
199
- console.log("[CustomRotateTransport] Deleted old log file: ".concat(file.path));
200
- } catch (err) {
201
- console.warn("[CustomRotateTransport] Failed to delete log file: ".concat(file.path), err);
175
+ console.log("[CustomRotateTransport] Found ".concat(files.length, " log files, maxKeepFiles: ").concat(keepFiles));
176
+ if (files.length <= keepFiles) return;
177
+
178
+ // 并行 stat mtime,全部完成后按时间排序删除最旧的
179
+ var pending = files.length;
180
+ var fileInfos = [];
181
+ files.forEach(function (file) {
182
+ var filePath = _this3.path.join(logDir, file);
183
+ _this3.fs.stat(filePath, function (statErr, stat) {
184
+ if (!statErr) {
185
+ fileInfos.push({
186
+ name: file,
187
+ path: filePath,
188
+ stat: stat
189
+ });
190
+ }
191
+ pending--;
192
+ if (pending === 0) {
193
+ fileInfos.sort(function (a, b) {
194
+ return b.stat.mtime.getTime() - a.stat.mtime.getTime();
195
+ });
196
+ var filesToDelete = fileInfos.slice(keepFiles);
197
+ filesToDelete.forEach(function (f) {
198
+ _this3.fs.unlink(f.path, function (unlinkErr) {
199
+ if (unlinkErr) {
200
+ console.warn("[CustomRotateTransport] Failed to delete log file: ".concat(f.path), unlinkErr);
201
+ } else {
202
+ console.log("[CustomRotateTransport] Deleted old log file: ".concat(f.path));
203
+ }
204
+ });
205
+ });
202
206
  }
203
207
  });
204
- }
205
- } catch (err) {
206
- console.warn('[CustomRotateTransport] Error cleaning up old log files:', err);
207
- }
208
+ });
209
+ });
208
210
  }
209
211
  }, {
210
212
  key: "log",
@@ -224,6 +226,21 @@ var createRotateTransport = exports.createRotateTransport = function createRotat
224
226
  }, {
225
227
  key: "close",
226
228
  value: function close() {
229
+ // 同步刷入 writeQueue 中还未写入的日志,防止进程退出时丢失
230
+ var remaining = this.writeQueue.splice(0);
231
+ if (remaining.length > 0 && this.currentFile) {
232
+ try {
233
+ var data = Buffer.concat(remaining.map(function (item) {
234
+ return item.buffer;
235
+ }));
236
+ this.fs.appendFileSync(this.currentFile, data);
237
+ } catch (_unused) {
238
+ // 关闭阶段忽略写入错误
239
+ }
240
+ remaining.forEach(function (item) {
241
+ return item.callback();
242
+ });
243
+ }
227
244
  if (this.currentStream) {
228
245
  this.currentStream.end();
229
246
  this.currentStream = null;
@@ -151,7 +151,7 @@ var collectLogs = exports.collectLogs = /*#__PURE__*/function () {
151
151
  return _regenerator["default"].wrap(function (_context3) {
152
152
  while (1) switch (_context3.prev = _context3.next) {
153
153
  case 0:
154
- buffer = new ArrayBuffer();
154
+ buffer = new ArrayBuffer(0);
155
155
  lastId = 0;
156
156
  _context3.prev = 1;
157
157
  _createMultiEntryZip = (0, _zip.createMultiEntryZip)(), addFile = _createMultiEntryZip.addFile, finish = _createMultiEntryZip.finish;
@@ -4,9 +4,6 @@ import "core-js/modules/es.array.concat.js";
4
4
  import "core-js/modules/es.array.for-each.js";
5
5
  import "core-js/modules/es.function.name.js";
6
6
  import "core-js/modules/es.object.to-string.js";
7
- import "core-js/modules/esnext.iterator.constructor.js";
8
- import "core-js/modules/esnext.iterator.for-each.js";
9
- import "core-js/modules/web.dom-collections.for-each.js";
10
7
  function validateParams(handlerError) {
11
8
  return function () {
12
9
  for (var _len = arguments.length, schemas = new Array(_len), _key = 0; _key < _len; _key++) {
@@ -107,14 +107,17 @@ export var uint8ArrayToImageData = function uint8ArrayToImageData(target) {
107
107
  var srow = row;
108
108
  var imageData = ctx.createImageData(width, 1);
109
109
  var start = srow * width * 4;
110
- if (target.isWindows) {
110
+ if (target.needsColorSwap) {
111
+ // BGRA -> RGBA 转换:交换 B 和 R 通道
112
+ // Windows 和 Linux 平台的 buffer 格式是 BGRA,需要转换为 RGBA
111
113
  for (var i = 0; i < rowBytes; i += 4) {
112
- imageData.data[i] = target.buffer[start + i + 2];
113
- imageData.data[i + 1] = target.buffer[start + i + 1];
114
- imageData.data[i + 2] = target.buffer[start + i];
115
- imageData.data[i + 3] = target.buffer[start + i + 3];
114
+ imageData.data[i] = target.buffer[start + i + 2]; // R
115
+ imageData.data[i + 1] = target.buffer[start + i + 1]; // G
116
+ imageData.data[i + 2] = target.buffer[start + i]; // B
117
+ imageData.data[i + 3] = target.buffer[start + i + 3]; // A
116
118
  }
117
119
  } else {
120
+ // Mac 平台直接复制(已经是 RGBA 格式)
118
121
  for (var _i = 0; _i < rowBytes; ++_i) {
119
122
  imageData.data[_i] = target.buffer[start + _i];
120
123
  }
@@ -34,7 +34,7 @@ var COMPRESSION_LEVEL = 9;
34
34
  export var zipDir = function zipDir(dirPath, options) {
35
35
  return new Promise(/*#__PURE__*/function () {
36
36
  var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2(resolve) {
37
- var fs, recursive, files, zip, _iterator, _step, _step$value, relativePath, absolutePath, logsStr, blob, buffer, now, datefmt, file, _t2;
37
+ var fs, recursive, files, zip, _iterator, _step, _step$value, relativePath, absolutePath, logsStr, blob, buffer, now, datefmt, fileName, file, _t2;
38
38
  return _regeneratorRuntime.wrap(function (_context2) {
39
39
  while (1) switch (_context2.prev = _context2.next) {
40
40
  case 0:
@@ -49,7 +49,8 @@ export var zipDir = function zipDir(dirPath, options) {
49
49
  _context2.next = 1;
50
50
  return cleanDir(dirPath, {
51
51
  filePattern: options.filePattern,
52
- dirPattern: options.dirPattern
52
+ dirPattern: options.dirPattern,
53
+ fileExtensionPattern: options.fileExtensionPattern
53
54
  });
54
55
  case 1:
55
56
  if (!recursive) {
@@ -59,7 +60,8 @@ export var zipDir = function zipDir(dirPath, options) {
59
60
  _context2.next = 2;
60
61
  return _collectFiles(dirPath, {
61
62
  filePattern: options === null || options === void 0 ? void 0 : options.filePattern,
62
- dirPattern: options === null || options === void 0 ? void 0 : options.dirPattern
63
+ dirPattern: options === null || options === void 0 ? void 0 : options.dirPattern,
64
+ fileExtensionPattern: options === null || options === void 0 ? void 0 : options.fileExtensionPattern
63
65
  });
64
66
  case 2:
65
67
  files = _context2.sent;
@@ -99,7 +101,8 @@ export var zipDir = function zipDir(dirPath, options) {
99
101
  buffer = _context2.sent;
100
102
  now = new Date();
101
103
  datefmt = now.toISOString().replace(/\.\d{3}Z$/, 'Z') + 'Z';
102
- file = new File([buffer], "logs-".concat(datefmt, ".zip"), {
104
+ fileName = (options === null || options === void 0 ? void 0 : options.zipFileName) || "logs-".concat(datefmt);
105
+ file = new File([buffer], "".concat(fileName, ".zip"), {
103
106
  type: 'application/zip'
104
107
  });
105
108
  resolve(file);
@@ -109,7 +112,7 @@ export var zipDir = function zipDir(dirPath, options) {
109
112
  // 原有逻辑:只处理顶层文件
110
113
  fs.readdir(dirPath, /*#__PURE__*/function () {
111
114
  var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(err, files) {
112
- var zip, _iterator2, _step2, _file, filePath, _logsStr, blob, buffer, now, datefmt, file, _t;
115
+ var zip, _iterator2, _step2, _file, filePath, _logsStr, blob, buffer, now, datefmt, fileName, file, _t;
113
116
  return _regeneratorRuntime.wrap(function (_context) {
114
117
  while (1) switch (_context.prev = _context.next) {
115
118
  case 0:
@@ -134,42 +137,48 @@ export var zipDir = function zipDir(dirPath, options) {
134
137
  _iterator2.s();
135
138
  case 4:
136
139
  if ((_step2 = _iterator2.n()).done) {
137
- _context.next = 8;
140
+ _context.next = 9;
138
141
  break;
139
142
  }
140
143
  _file = _step2.value;
141
- if (!(options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(_file))) {
144
+ if (!(options !== null && options !== void 0 && options.fileExtensionPattern && !options.fileExtensionPattern.test(_file))) {
142
145
  _context.next = 5;
143
146
  break;
144
147
  }
145
- return _context.abrupt("continue", 7);
148
+ return _context.abrupt("continue", 8);
146
149
  case 5:
150
+ if (!(options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(_file))) {
151
+ _context.next = 6;
152
+ break;
153
+ }
154
+ return _context.abrupt("continue", 8);
155
+ case 6:
147
156
  filePath = "".concat(dirPath, "/").concat(_file);
148
- _context.next = 6;
157
+ _context.next = 7;
149
158
  return isFile(filePath);
150
- case 6:
159
+ case 7:
151
160
  if (!_context.sent) {
152
- _context.next = 7;
161
+ _context.next = 8;
153
162
  break;
154
163
  }
155
164
  _logsStr = fs.readFileSync(filePath, 'utf8');
156
165
  zip.file(_file, _logsStr);
157
- case 7:
158
- _context.next = 4;
159
- break;
160
166
  case 8:
161
- _context.next = 10;
167
+ _context.next = 4;
162
168
  break;
163
169
  case 9:
164
- _context.prev = 9;
165
- _t = _context["catch"](3);
166
- _iterator2.e(_t);
170
+ _context.next = 11;
171
+ break;
167
172
  case 10:
168
173
  _context.prev = 10;
169
- _iterator2.f();
170
- return _context.finish(10);
174
+ _t = _context["catch"](3);
175
+ _iterator2.e(_t);
171
176
  case 11:
172
- _context.next = 12;
177
+ _context.prev = 11;
178
+ _iterator2.f();
179
+ return _context.finish(11);
180
+ case 12:
181
+ _context.next = 13;
173
182
  return zip.generateAsync({
174
183
  type: 'blob',
175
184
  compression: 'DEFLATE',
@@ -177,23 +186,24 @@ export var zipDir = function zipDir(dirPath, options) {
177
186
  level: COMPRESSION_LEVEL
178
187
  }
179
188
  });
180
- case 12:
189
+ case 13:
181
190
  blob = _context.sent;
182
- _context.next = 13;
191
+ _context.next = 14;
183
192
  return blob.arrayBuffer();
184
- case 13:
193
+ case 14:
185
194
  buffer = _context.sent;
186
195
  now = new Date();
187
196
  datefmt = now.toISOString().replace(/\.\d{3}Z$/, 'Z') + 'Z';
188
- file = new File([buffer], "logs-".concat(datefmt, ".zip"), {
197
+ fileName = (options === null || options === void 0 ? void 0 : options.zipFileName) || "logs-".concat(datefmt);
198
+ file = new File([buffer], "".concat(fileName, ".zip"), {
189
199
  type: 'application/zip'
190
200
  });
191
201
  resolve(file);
192
- case 14:
202
+ case 15:
193
203
  case "end":
194
204
  return _context.stop();
195
205
  }
196
- }, _callee, null, [[3, 9, 10, 11]]);
206
+ }, _callee, null, [[3, 10, 11, 12]]);
197
207
  }));
198
208
  return function (_x2, _x3) {
199
209
  return _ref2.apply(this, arguments);
@@ -278,6 +288,7 @@ var isDirectory = /*#__PURE__*/function () {
278
288
  * @param dirPath 目录路径
279
289
  * @param options.filePattern 文件名匹配规则(不符合的删除)
280
290
  * @param options.dirPattern 目录名匹配规则(不符合的删除)
291
+ * @param options.fileExtensionPattern 文件扩展名匹配规则(如 /\.(log|txt)$/),不传则不过滤
281
292
  */
282
293
  export var cleanDir = function cleanDir(dirPath, options) {
283
294
  return new Promise(function (resolve) {
@@ -287,7 +298,7 @@ export var cleanDir = function cleanDir(dirPath, options) {
287
298
  withFileTypes: true
288
299
  }, /*#__PURE__*/function () {
289
300
  var _ref5 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee5(err, items) {
290
- var _iterator3, _step3, item, itemPath, dirMatches, _t3;
301
+ var _iterator3, _step3, item, itemPath, matchesExtension, matchesPattern, keep, dirMatches, _t3;
291
302
  return _regeneratorRuntime.wrap(function (_context5) {
292
303
  while (1) switch (_context5.prev = _context5.next) {
293
304
  case 0:
@@ -320,8 +331,11 @@ export var cleanDir = function cleanDir(dirPath, options) {
320
331
  _context5.next = 5;
321
332
  break;
322
333
  }
323
- // 文件:检查 filePattern
324
- if (options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(item.name)) {
334
+ // 文件:检查扩展名和 filePattern
335
+ matchesExtension = options !== null && options !== void 0 && options.fileExtensionPattern ? options.fileExtensionPattern.test(item.name) : true;
336
+ matchesPattern = options !== null && options !== void 0 && options.filePattern ? options.filePattern.test(item.name) : true;
337
+ keep = matchesExtension && matchesPattern;
338
+ if (!keep) {
325
339
  fs.unlinkSync(itemPath);
326
340
  console.log("[cleanDir] deleted file: ".concat(itemPath));
327
341
  }
@@ -461,7 +475,12 @@ var _cleanDirRecursively = /*#__PURE__*/function () {
461
475
 
462
476
  /**
463
477
  * 递归遍历目录并收集所有文件
464
- * filePattern 只对顶层目录的文件生效,匹配 dirPattern 的子目录会收集所有文件
478
+ * @param dirPath 目录路径
479
+ * @param fileExtensionPattern 文件扩展名匹配规则(对所有目录生效)
480
+ * @param options.filePattern 文件名匹配规则(只对顶层目录生效)
481
+ * @param options.dirPattern 目录名匹配规则
482
+ * @param baseDir 基础目录路径
483
+ * @param skipFileFilter 是否跳过 filePattern 验证(子目录中使用)
465
484
  */
466
485
  var _collectFiles = /*#__PURE__*/function () {
467
486
  var _ref7 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee7(dirPath, options) {
@@ -473,6 +492,8 @@ var _collectFiles = /*#__PURE__*/function () {
473
492
  _iterator5,
474
493
  _step5,
475
494
  entry,
495
+ patternFail,
496
+ extFail,
476
497
  relativePath,
477
498
  subFiles,
478
499
  _args7 = arguments,
@@ -519,7 +540,13 @@ var _collectFiles = /*#__PURE__*/function () {
519
540
  _context7.next = 6;
520
541
  break;
521
542
  }
522
- if (!(!skipFileFilter && options !== null && options !== void 0 && options.filePattern && !options.filePattern.test(entry.name))) {
543
+ if (skipFileFilter) {
544
+ _context7.next = 5;
545
+ break;
546
+ }
547
+ patternFail = (options === null || options === void 0 ? void 0 : options.filePattern) && !options.filePattern.test(entry.name);
548
+ extFail = (options === null || options === void 0 ? void 0 : options.fileExtensionPattern) && !options.fileExtensionPattern.test(entry.name);
549
+ if (!(patternFail || extFail)) {
523
550
  _context7.next = 5;
524
551
  break;
525
552
  }
@@ -547,7 +574,9 @@ var _collectFiles = /*#__PURE__*/function () {
547
574
  return _context7.abrupt("continue", 10);
548
575
  case 8:
549
576
  _context7.next = 9;
550
- return _collectFiles(entry.path, options, baseDir, true);
577
+ return _collectFiles(entry.path, {
578
+ fileExtensionPattern: options === null || options === void 0 ? void 0 : options.fileExtensionPattern
579
+ }, baseDir, false);
551
580
  case 9:
552
581
  subFiles = _context7.sent;
553
582
  result.push.apply(result, _toConsumableArray(subFiles));
@@ -18,6 +18,7 @@ import "core-js/modules/web.dom-collections.iterator.js";
18
18
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
19
19
  import _regeneratorRuntime from "@babel/runtime/regenerator";
20
20
  import "core-js/modules/es.array.concat.js";
21
+ import "core-js/modules/es.string.ends-with.js";
21
22
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
22
23
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
23
24
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
@@ -35,7 +36,7 @@ export var collectLogsOnFs = /*#__PURE__*/function () {
35
36
  fs = window.require('fs');
36
37
  fs.readdir(logDirPath, /*#__PURE__*/function () {
37
38
  var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(err, files) {
38
- var zip, _iterator, _step, file, filePath, logFileData, blob, buffer;
39
+ var zip, _iterator, _step, file, filePath, logFileData, blob, buffer, _t;
39
40
  return _regeneratorRuntime.wrap(function (_context) {
40
41
  while (1) switch (_context.prev = _context.next) {
41
42
  case 0:
@@ -47,7 +48,7 @@ export var collectLogsOnFs = /*#__PURE__*/function () {
47
48
  reply(self, callId, WorkerDirectives.COLLECT_LOGS_ON_FS, {
48
49
  isSuccess: false
49
50
  });
50
- _context.next = 5;
51
+ _context.next = 13;
51
52
  break;
52
53
  case 1:
53
54
  if (!(files.length === 0)) {
@@ -58,42 +59,62 @@ export var collectLogsOnFs = /*#__PURE__*/function () {
58
59
  isSuccess: true,
59
60
  buffer: null
60
61
  });
61
- _context.next = 5;
62
+ _context.next = 13;
62
63
  break;
63
64
  case 2:
64
65
  zip = new JSZip();
65
66
  _iterator = _createForOfIteratorHelper(files);
66
- try {
67
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
68
- file = _step.value;
69
- filePath = "".concat(logDirPath, "/").concat(file);
70
- logFileData = fs.readFileSync(filePath);
71
- zip.file(file, logFileData);
72
- }
73
- } catch (err) {
74
- _iterator.e(err);
75
- } finally {
76
- _iterator.f();
67
+ _context.prev = 3;
68
+ _iterator.s();
69
+ case 4:
70
+ if ((_step = _iterator.n()).done) {
71
+ _context.next = 7;
72
+ break;
73
+ }
74
+ file = _step.value;
75
+ if (file.endsWith('.log')) {
76
+ _context.next = 5;
77
+ break;
77
78
  }
78
- _context.next = 3;
79
+ return _context.abrupt("continue", 6);
80
+ case 5:
81
+ filePath = "".concat(logDirPath, "/").concat(file);
82
+ logFileData = fs.readFileSync(filePath);
83
+ zip.file(file, logFileData);
84
+ case 6:
85
+ _context.next = 4;
86
+ break;
87
+ case 7:
88
+ _context.next = 9;
89
+ break;
90
+ case 8:
91
+ _context.prev = 8;
92
+ _t = _context["catch"](3);
93
+ _iterator.e(_t);
94
+ case 9:
95
+ _context.prev = 9;
96
+ _iterator.f();
97
+ return _context.finish(9);
98
+ case 10:
99
+ _context.next = 11;
79
100
  return zip.generateAsync({
80
101
  type: 'blob'
81
102
  });
82
- case 3:
103
+ case 11:
83
104
  blob = _context.sent;
84
- _context.next = 4;
105
+ _context.next = 12;
85
106
  return blob.arrayBuffer();
86
- case 4:
107
+ case 12:
87
108
  buffer = _context.sent;
88
109
  reply(self, callId, WorkerDirectives.COLLECT_LOGS_ON_FS, {
89
110
  isSuccess: true,
90
111
  buffer: buffer
91
112
  });
92
- case 5:
113
+ case 13:
93
114
  case "end":
94
115
  return _context.stop();
95
116
  }
96
- }, _callee);
117
+ }, _callee, null, [[3, 8, 9, 10]]);
97
118
  }));
98
119
  return function (_x4, _x5) {
99
120
  return _ref2.apply(this, arguments);
@@ -101,7 +122,7 @@ export var collectLogsOnFs = /*#__PURE__*/function () {
101
122
  }());
102
123
  } catch (e) {
103
124
  console.error("[logger] failed to get buffer.", e);
104
- reply(self, callId, WorkerDirectives.GET_BUFFER, {
125
+ reply(self, callId, WorkerDirectives.COLLECT_LOGS_ON_FS, {
105
126
  isSuccess: false
106
127
  });
107
128
  }
@@ -117,7 +138,7 @@ export var collectLogsOnFs = /*#__PURE__*/function () {
117
138
  }();
118
139
  export var clearTempLogs = /*#__PURE__*/function () {
119
140
  var _ref3 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3(self, callId) {
120
- var _t;
141
+ var _t2;
121
142
  return _regeneratorRuntime.wrap(function (_context3) {
122
143
  while (1) switch (_context3.prev = _context3.next) {
123
144
  case 0:
@@ -132,8 +153,8 @@ export var clearTempLogs = /*#__PURE__*/function () {
132
153
  break;
133
154
  case 2:
134
155
  _context3.prev = 2;
135
- _t = _context3["catch"](0);
136
- console.error("[logger] failed to clear temp logs.", _t);
156
+ _t2 = _context3["catch"](0);
157
+ console.error("[logger] failed to clear temp logs.", _t2);
137
158
  reply(self, callId, WorkerDirectives.CLEAR_TEMP_LOGS, {
138
159
  isSuccess: false
139
160
  });
@@ -14,6 +14,7 @@ import "core-js/modules/es.array.map.js";
14
14
  import "core-js/modules/es.array.push.js";
15
15
  import "core-js/modules/es.array.slice.js";
16
16
  import "core-js/modules/es.array.sort.js";
17
+ import "core-js/modules/es.array.splice.js";
17
18
  import "core-js/modules/es.date.to-string.js";
18
19
  import "core-js/modules/es.object.to-string.js";
19
20
  import "core-js/modules/es.string.ends-with.js";
@@ -62,10 +63,6 @@ export var createRotateTransport = function createRotateTransport(filenamePrefix
62
63
  _this.path = require('path');
63
64
  return _this;
64
65
  }
65
-
66
- /**
67
- * 生成唯一的日志文件名,避免同一秒多次轮转导致的覆盖
68
- */
69
66
  _inherits(CustomRotateTransport, _WinstonTransport);
70
67
  return _createClass(CustomRotateTransport, [{
71
68
  key: "generateLogFilename",
@@ -79,10 +76,6 @@ export var createRotateTransport = function createRotateTransport(filenamePrefix
79
76
  }
80
77
  return this.rotateIndex === 0 ? "".concat(this.filenamePrefix, "-").concat(timestamp, ".log") : "".concat(this.filenamePrefix, "-").concat(timestamp, "-").concat(this.rotateIndex, ".log");
81
78
  }
82
-
83
- /**
84
- * 创建新日志文件,并清理旧文件
85
- */
86
79
  }, {
87
80
  key: "createNewLogFile",
88
81
  value: function createNewLogFile() {
@@ -101,11 +94,15 @@ export var createRotateTransport = function createRotateTransport(filenamePrefix
101
94
  this.currentStream = this.fs.createWriteStream(this.currentFile, {
102
95
  flags: 'a'
103
96
  });
104
- this.currentSize = this.fs.existsSync(this.currentFile) ? this.fs.statSync(this.currentFile).size : 0;
97
+ // 新文件必然从 0 开始,用内存计数器替代 statSync
98
+ this.currentSize = 0;
99
+ this.cleanupOldFiles(this.maxFiles - 1);
105
100
  }
106
101
 
107
102
  /**
108
- * 只允许每个 buffer 触发一次轮转,避免多余文件
103
+ * 与老版本结构完全一致,只做一处改动:
104
+ * 原来在写入回调里用 statSync 读磁盘更新 currentSize,
105
+ * 改为用内存计数器 currentSize += buffer.length,消除每条日志的磁盘读操作。
109
106
  */
110
107
  }, {
111
108
  key: "processQueue",
@@ -117,86 +114,91 @@ export var createRotateTransport = function createRotateTransport(filenamePrefix
117
114
  buffer = _ref.buffer,
118
115
  callback = _ref.callback;
119
116
 
120
- // 1. 单条日志大于 maxSize,直接新建新文件并写入,不要理会 currentStream
117
+ // 1. 单条日志大于 maxSize,直接新建新文件并写入
121
118
  if (buffer.length > this.maxSize) {
122
119
  this.createNewLogFile();
123
120
  console.log("[CustomRotateTransport] buffer.length (".concat(buffer.length, ") > maxSize (").concat(this.maxSize, "), force rotate."));
124
121
  this.currentStream.write(buffer, function () {
125
- try {
126
- _this2.currentSize = _this2.fs.statSync(_this2.currentFile).size;
127
- } catch (_unused) {
128
- _this2.currentSize += buffer.length;
129
- }
122
+ _this2.currentSize += buffer.length; // ← 替代 statSync
130
123
  callback();
131
124
  _this2.writing = false;
132
125
  _this2.processQueue();
133
- _this2.cleanupOldFiles();
134
126
  });
135
127
  return;
136
128
  }
137
129
 
138
- // 2. buffer.length <= maxSize,才需要判断 currentStream 是否为空
130
+ // 2. buffer.length <= maxSize,判断 currentStream 是否为空
139
131
  if (!this.currentStream) {
140
132
  this.createNewLogFile();
141
133
  }
142
- var actualSize = 0;
143
- try {
144
- actualSize = this.fs.existsSync(this.currentFile) ? this.fs.statSync(this.currentFile).size : 0;
145
- } catch (_unused2) {
146
- actualSize = this.currentSize;
147
- }
148
- if (actualSize + buffer.length > this.maxSize) {
149
- console.log("[CustomRotateTransport] actualSize (".concat(actualSize, ") + buffer.length (").concat(buffer.length, ") > maxSize (").concat(this.maxSize, "), rotate."));
134
+
135
+ // 用内存计数器替代 existsSync + statSync 判断是否需要轮转
136
+ if (this.currentSize + buffer.length > this.maxSize) {
150
137
  this.createNewLogFile();
151
138
  }
152
139
  this.currentStream.write(buffer, function () {
153
- try {
154
- _this2.currentSize = _this2.fs.statSync(_this2.currentFile).size;
155
- } catch (_unused3) {
156
- _this2.currentSize += buffer.length;
157
- }
140
+ _this2.currentSize += buffer.length;
158
141
  callback();
159
142
  _this2.writing = false;
160
143
  _this2.processQueue();
161
- _this2.cleanupOldFiles();
162
144
  });
163
145
  }
164
146
 
165
147
  /**
166
- * 清理旧日志文件,只保留 maxFiles
148
+ * 只把同步 I/O(readdirSync / statSync / unlinkSync)改为异步回调,
149
+ * 避免阻塞 Worker 线程导致写入队列积压。
167
150
  */
168
151
  }, {
169
152
  key: "cleanupOldFiles",
170
153
  value: function cleanupOldFiles() {
171
154
  var _this3 = this;
172
- try {
173
- var files = this.fs.readdirSync(this.logFolderPath).filter(function (file) {
174
- return file.startsWith(_this3.filenamePrefix) && file.endsWith('.log');
175
- }).map(function (file) {
176
- return {
177
- name: file,
178
- path: _this3.path.join(_this3.logFolderPath, file),
179
- stat: _this3.fs.statSync(_this3.path.join(_this3.logFolderPath, file))
180
- };
181
- }).sort(function (a, b) {
182
- return b.stat.mtime.getTime() - a.stat.mtime.getTime();
155
+ var keepFiles = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.maxFiles;
156
+ var prefix = this.filenamePrefix;
157
+ var logDir = this.logFolderPath;
158
+ console.log("[CustomRotateTransport] cleanupOldFiles: ".concat(logDir));
159
+ this.fs.readdir(logDir, function (err, entries) {
160
+ if (err) {
161
+ console.warn('[CustomRotateTransport] Error reading log folder:', err);
162
+ return;
163
+ }
164
+ var files = entries.filter(function (file) {
165
+ return file.startsWith(prefix) && file.endsWith('.log');
183
166
  });
184
- console.log("[CustomRotateTransport] Found ".concat(files.length, " log files, maxFiles: ").concat(this.maxFiles));
185
- if (files.length > this.maxFiles) {
186
- var filesToDelete = files.slice(this.maxFiles);
187
- console.log("[CustomRotateTransport] Will delete ".concat(filesToDelete.length, " old files"));
188
- filesToDelete.forEach(function (file) {
189
- try {
190
- _this3.fs.unlinkSync(file.path);
191
- console.log("[CustomRotateTransport] Deleted old log file: ".concat(file.path));
192
- } catch (err) {
193
- console.warn("[CustomRotateTransport] Failed to delete log file: ".concat(file.path), err);
167
+ console.log("[CustomRotateTransport] Found ".concat(files.length, " log files, maxKeepFiles: ").concat(keepFiles));
168
+ if (files.length <= keepFiles) return;
169
+
170
+ // 并行 stat mtime,全部完成后按时间排序删除最旧的
171
+ var pending = files.length;
172
+ var fileInfos = [];
173
+ files.forEach(function (file) {
174
+ var filePath = _this3.path.join(logDir, file);
175
+ _this3.fs.stat(filePath, function (statErr, stat) {
176
+ if (!statErr) {
177
+ fileInfos.push({
178
+ name: file,
179
+ path: filePath,
180
+ stat: stat
181
+ });
182
+ }
183
+ pending--;
184
+ if (pending === 0) {
185
+ fileInfos.sort(function (a, b) {
186
+ return b.stat.mtime.getTime() - a.stat.mtime.getTime();
187
+ });
188
+ var filesToDelete = fileInfos.slice(keepFiles);
189
+ filesToDelete.forEach(function (f) {
190
+ _this3.fs.unlink(f.path, function (unlinkErr) {
191
+ if (unlinkErr) {
192
+ console.warn("[CustomRotateTransport] Failed to delete log file: ".concat(f.path), unlinkErr);
193
+ } else {
194
+ console.log("[CustomRotateTransport] Deleted old log file: ".concat(f.path));
195
+ }
196
+ });
197
+ });
194
198
  }
195
199
  });
196
- }
197
- } catch (err) {
198
- console.warn('[CustomRotateTransport] Error cleaning up old log files:', err);
199
- }
200
+ });
201
+ });
200
202
  }
201
203
  }, {
202
204
  key: "log",
@@ -216,6 +218,21 @@ export var createRotateTransport = function createRotateTransport(filenamePrefix
216
218
  }, {
217
219
  key: "close",
218
220
  value: function close() {
221
+ // 同步刷入 writeQueue 中还未写入的日志,防止进程退出时丢失
222
+ var remaining = this.writeQueue.splice(0);
223
+ if (remaining.length > 0 && this.currentFile) {
224
+ try {
225
+ var data = Buffer.concat(remaining.map(function (item) {
226
+ return item.buffer;
227
+ }));
228
+ this.fs.appendFileSync(this.currentFile, data);
229
+ } catch (_unused) {
230
+ // 关闭阶段忽略写入错误
231
+ }
232
+ remaining.forEach(function (item) {
233
+ return item.callback();
234
+ });
235
+ }
219
236
  if (this.currentStream) {
220
237
  this.currentStream.end();
221
238
  this.currentStream = null;
@@ -144,7 +144,7 @@ export var collectLogs = /*#__PURE__*/function () {
144
144
  return _regeneratorRuntime.wrap(function (_context3) {
145
145
  while (1) switch (_context3.prev = _context3.next) {
146
146
  case 0:
147
- buffer = new ArrayBuffer();
147
+ buffer = new ArrayBuffer(0);
148
148
  lastId = 0;
149
149
  _context3.prev = 1;
150
150
  _createMultiEntryZip = createMultiEntryZip(), addFile = _createMultiEntryZip.addFile, finish = _createMultiEntryZip.finish;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-foundation",
3
- "version": "3.9.1",
3
+ "version": "3.10.0-beta",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-es/index.js",
@@ -24,7 +24,7 @@
24
24
  "@types/jasmine": "^5.1.4",
25
25
  "@types/lodash": "^4.14.168",
26
26
  "@types/node": "^20.11.30",
27
- "agora-toolchain": "3.9.1",
27
+ "agora-toolchain": "3.10.0-beta",
28
28
  "core-js": "^3.33.3",
29
29
  "tslib": "^2.6.2",
30
30
  "winston": "^3.16.0",