mx-cloud 0.0.22 → 0.0.23

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.
@@ -160,5 +160,10 @@ export default class Interpreter extends EventEmitter {
160
160
  */
161
161
  run(page: Page, params?: ParamType): Promise<void>;
162
162
  stop(): Promise<void>;
163
+ /**
164
+ * Cleanup method to release resources and prevent memory leaks
165
+ * Call this when the interpreter is no longer needed
166
+ */
167
+ cleanup(): Promise<void>;
163
168
  }
164
169
  export {};
@@ -394,8 +394,9 @@ class Interpreter extends events_1.EventEmitter {
394
394
  for (const link of links) {
395
395
  // eslint-disable-next-line
396
396
  this.concurrency.addJob(() => __awaiter(this, void 0, void 0, function* () {
397
+ let newPage = null;
397
398
  try {
398
- const newPage = yield context.newPage();
399
+ newPage = yield context.newPage();
399
400
  yield newPage.goto(link);
400
401
  yield newPage.waitForLoadState('networkidle');
401
402
  yield this.runLoop(newPage, this.initializedWorkflow);
@@ -406,6 +407,16 @@ class Interpreter extends events_1.EventEmitter {
406
407
  // the interpreter by throwing).
407
408
  this.log(e, logger_1.Level.ERROR);
408
409
  }
410
+ finally {
411
+ if (newPage && !newPage.isClosed()) {
412
+ try {
413
+ yield newPage.close();
414
+ }
415
+ catch (closeError) {
416
+ this.log('Failed to close enqueued page', logger_1.Level.WARN);
417
+ }
418
+ }
419
+ }
409
420
  }));
410
421
  }
411
422
  yield page.close();
@@ -2224,9 +2235,10 @@ class Interpreter extends events_1.EventEmitter {
2224
2235
  * User-requested concurrency should be entirely managed by the concurrency manager,
2225
2236
  * e.g. via `enqueueLinks`.
2226
2237
  */
2227
- p.on('popup', (popup) => {
2238
+ const popupHandler = (popup) => {
2228
2239
  this.concurrency.addJob(() => this.runLoop(popup, workflowCopy));
2229
- });
2240
+ };
2241
+ p.on('popup', popupHandler);
2230
2242
  /* eslint no-constant-condition: ["warn", { "checkLoops": false }] */
2231
2243
  let loopIterations = 0;
2232
2244
  const MAX_LOOP_ITERATIONS = 1000; // Circuit breaker
@@ -2234,41 +2246,58 @@ class Interpreter extends events_1.EventEmitter {
2234
2246
  const MAX_CONSECUTIVE_FAILURES = 10;
2235
2247
  const startTime = Date.now();
2236
2248
  const MAX_EXECUTION_TIME = 30 * 60 * 1000; // 30 minutes max
2249
+ // Cleanup function to remove popup listener
2250
+ const cleanup = () => {
2251
+ try {
2252
+ if (!p.isClosed()) {
2253
+ p.removeListener('popup', popupHandler);
2254
+ }
2255
+ }
2256
+ catch (cleanupError) {
2257
+ }
2258
+ };
2237
2259
  while (true) {
2238
2260
  // Multiple circuit breakers to prevent infinite loops
2239
2261
  if (++loopIterations > MAX_LOOP_ITERATIONS) {
2240
2262
  this.log('Maximum loop iterations reached, terminating to prevent infinite loop', logger_1.Level.ERROR);
2263
+ cleanup();
2241
2264
  return;
2242
2265
  }
2243
2266
  // Time-based circuit breaker
2244
2267
  if (Date.now() - startTime > MAX_EXECUTION_TIME) {
2245
2268
  this.log('Maximum execution time reached (30 minutes), terminating workflow', logger_1.Level.ERROR);
2269
+ cleanup();
2246
2270
  return;
2247
2271
  }
2248
2272
  // Failure-based circuit breaker
2249
2273
  if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
2250
2274
  this.log('Too many consecutive failures, terminating to prevent hang', logger_1.Level.ERROR);
2275
+ cleanup();
2251
2276
  return;
2252
2277
  }
2253
2278
  // Check abort flag immediately
2254
2279
  if (this.isAborted) {
2255
2280
  this.log('Workflow aborted in runLoop', logger_1.Level.WARN);
2281
+ cleanup();
2256
2282
  return;
2257
2283
  }
2258
2284
  // Checks whether the page was closed from outside,
2259
2285
  // or the workflow execution has been stopped via `interpreter.stop()`
2260
2286
  if (p.isClosed() || !this.stopper) {
2287
+ cleanup();
2261
2288
  return;
2262
2289
  }
2263
2290
  try {
2264
2291
  yield p.waitForLoadState();
2265
2292
  }
2266
2293
  catch (e) {
2294
+ cleanup();
2267
2295
  yield p.close();
2268
2296
  return;
2269
2297
  }
2270
2298
  if (workflowCopy.length === 0) {
2271
2299
  this.log('All actions completed. Workflow finished.', logger_1.Level.LOG);
2300
+ cleanup();
2272
2301
  return;
2273
2302
  }
2274
2303
  // const newSelectors = this.getSelectors(workflowCopy);
@@ -2359,6 +2388,7 @@ class Interpreter extends events_1.EventEmitter {
2359
2388
  }
2360
2389
  else {
2361
2390
  //await this.disableAdBlocker(p);
2391
+ cleanup();
2362
2392
  return;
2363
2393
  }
2364
2394
  }
@@ -2444,5 +2474,47 @@ class Interpreter extends events_1.EventEmitter {
2444
2474
  }
2445
2475
  });
2446
2476
  }
2477
+ /**
2478
+ * Cleanup method to release resources and prevent memory leaks
2479
+ * Call this when the interpreter is no longer needed
2480
+ */
2481
+ cleanup() {
2482
+ return __awaiter(this, void 0, void 0, function* () {
2483
+ try {
2484
+ // Stop any running workflows first
2485
+ if (this.stopper) {
2486
+ try {
2487
+ yield this.stop();
2488
+ }
2489
+ catch (error) {
2490
+ this.log(`Error stopping workflow during cleanup: ${error.message}`, logger_1.Level.WARN);
2491
+ }
2492
+ }
2493
+ // Clear ad-blocker resources
2494
+ if (this.blocker) {
2495
+ try {
2496
+ this.blocker = null;
2497
+ this.log('Ad-blocker resources cleared', logger_1.Level.DEBUG);
2498
+ }
2499
+ catch (error) {
2500
+ this.log(`Error cleaning up ad-blocker: ${error.message}`, logger_1.Level.WARN);
2501
+ }
2502
+ }
2503
+ // Clear accumulated data to free memory
2504
+ this.cumulativeResults = [];
2505
+ this.autohealFailures = [];
2506
+ this.namedResults = {};
2507
+ this.serializableDataByType = { scrapeList: {}, scrapeSchema: {} };
2508
+ // Reset state
2509
+ this.isAborted = false;
2510
+ this.initializedWorkflow = null;
2511
+ this.log('Interpreter cleanup completed', logger_1.Level.DEBUG);
2512
+ }
2513
+ catch (error) {
2514
+ this.log(`Error during interpreter cleanup: ${error.message}`, logger_1.Level.ERROR);
2515
+ throw error;
2516
+ }
2517
+ });
2518
+ }
2447
2519
  }
2448
2520
  exports.default = Interpreter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mx-cloud",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "mx cloud",
5
5
  "main": "build/index.js",
6
6
  "typings": "build/index.d.ts",