()
| 596 | |
| 597 | let closed = false; |
| 598 | const doClose = async (): Promise<void> => { |
| 599 | if (closed) return; |
| 600 | closed = true; |
| 601 | |
| 602 | try { |
| 603 | ix.invokeFunction((a) => a.get(IWSGateway)); |
| 604 | |
| 605 | ix.invokeFunction((a) => a.get(IConnectionRegistry).closeAll('server shutting down')); |
| 606 | } catch { |
| 607 | |
| 608 | } |
| 609 | |
| 610 | try { |
| 611 | await app.close(); |
| 612 | } catch { |
| 613 | |
| 614 | } |
| 615 | |
| 616 | try { |
| 617 | ix.dispose(); |
| 618 | } catch { |
| 619 | |
| 620 | } |
| 621 | |
| 622 | // The persistent token is intentionally left on disk so it survives the |
| 623 | // next start (ROADMAP M5.1). dispose() is a no-op for the persistent store; |
| 624 | // the call is kept so the interface is honored uniformly and a test |
| 625 | // override can still observe shutdown. |
| 626 | try { |
| 627 | await tokenStore.dispose(); |
| 628 | } catch { |
| 629 | // ignore — token file may already be gone |
| 630 | } |
| 631 | |
| 632 | // Stop the auth-failure limiter's cleanup timer (ROADMAP M6.4). Only set |
| 633 | // on non-loopback binds; the `?.` is a no-op on loopback. |
| 634 | authFailureLimiter?.dispose(); |
| 635 | |
| 636 | lockHandle.release(); |
| 637 | }; |
| 638 | |
| 639 | // Expose process-terminating shutdown to routes via DI. Respect a |
| 640 | // `serviceOverrides` entry so tests can observe the request without exiting. |
no test coverage detected