(signal: AbortSignal)
| 111 | while (!signal.aborted) { |
| 112 | const data = this.resetOnReconnect ? this.createInitial() : this.snapshot.data; |
| 113 | if (this.snapshot.phase !== "error") { |
| 114 | this.setSnapshot({ phase: "connecting", data }); |
| 115 | } else if (this.resetOnReconnect) { |
| 116 | this.setSnapshot({ ...this.snapshot, data }); |
| 117 | } |
| 118 | try { |
| 119 | await this.runStream({ |
| 120 | signal, |
| 121 | update: (updater) => { |
| 122 | attempt = 0; |
| 123 | this.setSnapshot({ phase: "active", data: updater(this.snapshot.data) }); |
| 124 | }, |
| 125 | }); |
| 126 | } catch (error) { |
| 127 | if (signal.aborted) { |
| 128 | return; |
| 129 | } |
| 130 | const described = describeError(error); |
| 131 | this.setSnapshot({ |
| 132 | ...this.snapshot, |
| 133 | phase: "error", |
| 134 | error: described.message, |
| 135 | errorCode: described.code, |
| 136 | }); |
| 137 | if (isTerminalCode(described.code)) { |
| 138 | return; |
| 139 | } |
| 140 | } |
| 141 | attempt += 1; |
| 142 | await this.backoff(Math.min(1000 * attempt, 5000), signal); |
| 143 | if (this.skipBackoff) { |
| 144 | this.skipBackoff = false; |
| 145 | attempt = 0; |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | private backoff(durationMs: number, signal: AbortSignal): Promise<void> { |
| 151 | return new Promise((resolve) => { |
no test coverage detected