()
| 483 | } |
| 484 | |
| 485 | async stop() { |
| 486 | if (!this._started) return; |
| 487 | // Tear down in deliberate reverse-of-start order, but don't let one |
| 488 | // failing step abort the rest: a thrown sync.stop() must not leave the |
| 489 | // HTTP server and store leaked. Each step is isolated; failures are |
| 490 | // warned and collected so shutdown always completes. |
| 491 | const steps = [ |
| 492 | ['sync', () => this.sync?.stop()], |
| 493 | ['heartbeat', () => this.lifecycle?.stopHeartbeatLoop()], |
| 494 | ['server', () => this.server?.stop()], |
| 495 | ['store', () => this.store?.close()], |
| 496 | ]; |
| 497 | const errors = []; |
| 498 | for (const [name, fn] of steps) { |
| 499 | try { |
| 500 | await fn(); |
| 501 | } catch (err) { |
| 502 | errors.push(err); |
| 503 | this.logger.warn('[proxy] error stopping ' + name + ': ' + (err && err.message ? err.message : err)); |
| 504 | } |
| 505 | } |
| 506 | this._started = false; |
| 507 | if (errors.length) { |
| 508 | this.logger.log('[proxy] stopped with ' + errors.length + ' teardown error(s)'); |
| 509 | } else { |
| 510 | this.logger.log('[proxy] stopped'); |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | get mailbox() { |
| 515 | return this.store; |
no test coverage detected