@cplieger/web-terminal-engine 5.0.5 → 5.0.8

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/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/web-terminal-engine",
3
- "version": "5.0.5",
3
+ "version": "5.0.8",
4
4
  "license": "MPL-2.0",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/web-terminal-engine",
3
- "version": "5.0.5",
3
+ "version": "5.0.8",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "typecheck": "tsc -p tsconfig.json",
@@ -21,7 +21,7 @@
21
21
  "esbuild": "0.28.2",
22
22
  "eslint": "10.8.1",
23
23
  "fast-check": "4.9.0",
24
- "happy-dom": "20.11.2",
24
+ "happy-dom": "20.11.6",
25
25
  "pngjs": "7.0.0",
26
26
  "prettier": "3.9.6",
27
27
  "typescript": "npm:@typescript/typescript6@6.0.2",
package/src/connection.ts CHANGED
@@ -782,7 +782,21 @@ function retransmitOutbox(sock: WebSocket, upgraded: boolean, st: ResumeState):
782
782
  }
783
783
 
784
784
  function scheduleReconnect(): void {
785
- if (connState.status === "reconnecting") {
785
+ // Nothing to schedule when the state already owns a path back to a socket: a
786
+ // backoff timer is pending ("reconnecting"), or a socket exists that will
787
+ // drive its own retry from its close handler or its connect timeout
788
+ // ("connecting"/"connected"). Both callers set "disconnected" before calling
789
+ // cb.onClose(), so a consumer that reconnects from its own close hook lands
790
+ // here at "connecting" — and scheduling over it would OVERWRITE connState,
791
+ // hiding that socket from connect()'s double-call guard, which then never
792
+ // aborts it: the timer's connect() adds a second live socket with the first
793
+ // one's listeners still bound, i.e. the duplicate server connection and
794
+ // double delivery documented inside connect().
795
+ if (
796
+ connState.status === "reconnecting" ||
797
+ connState.status === "connecting" ||
798
+ connState.status === "connected"
799
+ ) {
786
800
  return;
787
801
  }
788
802
  const step = nextBackoffDelay(reconnectDelay);
package/src/store.ts CHANGED
@@ -1826,6 +1826,17 @@ export class LineStore {
1826
1826
  if (this.highest < 0) {
1827
1827
  return;
1828
1828
  }
1829
+ // No window established yet — the descriptor is still the initial base
1830
+ // 0/height 0, whose bottom row is -1, below every index a line can hold.
1831
+ // This whole pass rests on "the window's bottom row is the most recent line
1832
+ // in the terminal", which says nothing at all when there is no window: a
1833
+ // resume replays history BEFORE the screen frame describing the window, so
1834
+ // taking -1 as the tail bound strands that replay and forgets it, silently
1835
+ // (this path is not a trim, so no marker explains it). Same test `inWindow`
1836
+ // and applyScroll's alt gate use to ask whether a window exists at all.
1837
+ if (this.win.height <= 0) {
1838
+ return;
1839
+ }
1829
1840
  const windowBottom = this.win.base + this.win.height - 1;
1830
1841
  if (this.highest <= windowBottom) {
1831
1842
  return;
@@ -2008,6 +2019,18 @@ export class LineStore {
2008
2019
  }
2009
2020
 
2010
2021
  private updateWindow(msg: ScreenMessage): void {
2022
+ // A base that is not a non-negative integer is not a screen position — the
2023
+ // same validity test markUnconfirmedFrom applies to this very value — so it
2024
+ // must not redefine the window's shape, for the same reason a rows-less
2025
+ // frame must not: `base + height - 1` lands below every retained index, and
2026
+ // truncateBelowWindow then hands the WHOLE store over as stranded past the
2027
+ // live window. That is the silent wipe applyScreen's rows-less guard
2028
+ // records, reached with rows present, and just as silent (this path is not a
2029
+ // trim, so no marker explains the missing transcript). The frame's
2030
+ // addressable rows still apply through applyLine's guard 1.
2031
+ if (!Number.isInteger(msg.base) || msg.base < 0) {
2032
+ return;
2033
+ }
2011
2034
  const next: WindowState = {
2012
2035
  base: msg.base,
2013
2036
  height: msg.rows.length,