websocket-ts 2.2.1 → 3.0.0

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.
Files changed (196) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +201 -63
  3. package/dist/cjs/{src/backoff → backoff}/backoff.d.ts +6 -3
  4. package/dist/{esm/src → cjs}/backoff/backoff.d.ts.map +1 -1
  5. package/dist/{esm/src → cjs}/backoff/backoff.js.map +1 -1
  6. package/dist/cjs/{src/backoff → backoff}/constantbackoff.d.ts +2 -2
  7. package/dist/cjs/backoff/constantbackoff.d.ts.map +1 -0
  8. package/dist/cjs/backoff/constantbackoff.js +34 -0
  9. package/dist/cjs/backoff/constantbackoff.js.map +1 -0
  10. package/dist/{esm/src → cjs}/backoff/exponentialbackoff.d.ts +2 -2
  11. package/dist/cjs/backoff/exponentialbackoff.d.ts.map +1 -0
  12. package/dist/cjs/{src/backoff → backoff}/exponentialbackoff.js +19 -27
  13. package/dist/cjs/backoff/exponentialbackoff.js.map +1 -0
  14. package/dist/{esm/src → cjs}/backoff/linearbackoff.d.ts +1 -1
  15. package/dist/cjs/backoff/linearbackoff.d.ts.map +1 -0
  16. package/dist/cjs/{src/backoff → backoff}/linearbackoff.js +23 -31
  17. package/dist/cjs/backoff/linearbackoff.js.map +1 -0
  18. package/dist/cjs/index.d.ts +14 -0
  19. package/dist/cjs/index.d.ts.map +1 -0
  20. package/dist/cjs/index.js +20 -0
  21. package/dist/cjs/index.js.map +1 -0
  22. package/dist/cjs/package.json +1 -0
  23. package/dist/{esm/src → cjs}/queue/array_queue.d.ts +1 -1
  24. package/dist/cjs/queue/array_queue.d.ts.map +1 -0
  25. package/dist/cjs/{src/queue → queue}/array_queue.js +17 -18
  26. package/dist/cjs/queue/array_queue.js.map +1 -0
  27. package/dist/cjs/queue/queue.d.ts.map +1 -0
  28. package/dist/cjs/{src/queue → queue}/queue.js.map +1 -1
  29. package/dist/cjs/{src/queue → queue}/ring_queue.d.ts +1 -1
  30. package/dist/cjs/queue/ring_queue.d.ts.map +1 -0
  31. package/dist/cjs/{src/queue → queue}/ring_queue.js +25 -22
  32. package/dist/cjs/queue/ring_queue.js.map +1 -0
  33. package/dist/cjs/{src/websocket.d.ts → websocket.d.ts} +84 -20
  34. package/dist/cjs/websocket.d.ts.map +1 -0
  35. package/dist/cjs/websocket.js +588 -0
  36. package/dist/cjs/websocket.js.map +1 -0
  37. package/dist/cjs/{src/websocket_buffer.d.ts → websocket_buffer.d.ts} +1 -1
  38. package/dist/cjs/websocket_buffer.d.ts.map +1 -0
  39. package/dist/cjs/websocket_buffer.js.map +1 -0
  40. package/dist/{esm/src → cjs}/websocket_builder.d.ts +22 -13
  41. package/dist/cjs/websocket_builder.d.ts.map +1 -0
  42. package/dist/cjs/websocket_builder.js +236 -0
  43. package/dist/cjs/websocket_builder.js.map +1 -0
  44. package/dist/cjs/websocket_event.d.ts +93 -0
  45. package/dist/cjs/websocket_event.d.ts.map +1 -0
  46. package/dist/cjs/websocket_event.js +27 -0
  47. package/dist/cjs/websocket_event.js.map +1 -0
  48. package/dist/{esm/src → cjs}/websocket_options.d.ts +6 -5
  49. package/dist/cjs/websocket_options.d.ts.map +1 -0
  50. package/dist/cjs/websocket_options.js.map +1 -0
  51. package/dist/{esm/src → cjs}/websocket_retry_options.d.ts +4 -3
  52. package/dist/cjs/websocket_retry_options.d.ts.map +1 -0
  53. package/dist/cjs/websocket_retry_options.js.map +1 -0
  54. package/dist/esm/{src/backoff → backoff}/backoff.d.ts +6 -3
  55. package/dist/{cjs/src → esm}/backoff/backoff.d.ts.map +1 -1
  56. package/dist/{cjs/src → esm}/backoff/backoff.js.map +1 -1
  57. package/dist/esm/{src/backoff → backoff}/constantbackoff.d.ts +2 -2
  58. package/dist/esm/backoff/constantbackoff.d.ts.map +1 -0
  59. package/dist/esm/{src/backoff → backoff}/constantbackoff.js +3 -3
  60. package/dist/esm/backoff/constantbackoff.js.map +1 -0
  61. package/dist/{cjs/src → esm}/backoff/exponentialbackoff.d.ts +2 -2
  62. package/dist/esm/backoff/exponentialbackoff.d.ts.map +1 -0
  63. package/dist/esm/{src/backoff → backoff}/exponentialbackoff.js +6 -5
  64. package/dist/esm/backoff/exponentialbackoff.js.map +1 -0
  65. package/dist/{cjs/src → esm}/backoff/linearbackoff.d.ts +1 -1
  66. package/dist/esm/backoff/linearbackoff.d.ts.map +1 -0
  67. package/dist/esm/{src/backoff → backoff}/linearbackoff.js +8 -7
  68. package/dist/esm/backoff/linearbackoff.js.map +1 -0
  69. package/dist/esm/index.d.ts +14 -0
  70. package/dist/esm/index.d.ts.map +1 -0
  71. package/dist/esm/index.js +9 -0
  72. package/dist/esm/index.js.map +1 -0
  73. package/dist/esm/package.json +1 -0
  74. package/dist/{cjs/src → esm}/queue/array_queue.d.ts +1 -1
  75. package/dist/esm/queue/array_queue.d.ts.map +1 -0
  76. package/dist/esm/queue/array_queue.js.map +1 -0
  77. package/dist/esm/queue/queue.d.ts.map +1 -0
  78. package/dist/esm/{src/queue → queue}/queue.js.map +1 -1
  79. package/dist/esm/{src/queue → queue}/ring_queue.d.ts +1 -1
  80. package/dist/esm/queue/ring_queue.d.ts.map +1 -0
  81. package/dist/esm/{src/queue → queue}/ring_queue.js +7 -3
  82. package/dist/esm/queue/ring_queue.js.map +1 -0
  83. package/dist/esm/{src/websocket.d.ts → websocket.d.ts} +84 -20
  84. package/dist/esm/websocket.d.ts.map +1 -0
  85. package/dist/esm/websocket.js +584 -0
  86. package/dist/esm/websocket.js.map +1 -0
  87. package/dist/esm/{src/websocket_buffer.d.ts → websocket_buffer.d.ts} +1 -1
  88. package/dist/esm/websocket_buffer.d.ts.map +1 -0
  89. package/dist/esm/websocket_buffer.js.map +1 -0
  90. package/dist/{cjs/src → esm}/websocket_builder.d.ts +22 -13
  91. package/dist/esm/websocket_builder.d.ts.map +1 -0
  92. package/dist/esm/{src/websocket_builder.js → websocket_builder.js} +42 -23
  93. package/dist/esm/websocket_builder.js.map +1 -0
  94. package/dist/esm/websocket_event.d.ts +93 -0
  95. package/dist/esm/websocket_event.d.ts.map +1 -0
  96. package/dist/esm/websocket_event.js +24 -0
  97. package/dist/esm/websocket_event.js.map +1 -0
  98. package/dist/{cjs/src → esm}/websocket_options.d.ts +6 -5
  99. package/dist/esm/websocket_options.d.ts.map +1 -0
  100. package/dist/esm/websocket_options.js.map +1 -0
  101. package/dist/{cjs/src → esm}/websocket_retry_options.d.ts +4 -3
  102. package/dist/esm/websocket_retry_options.d.ts.map +1 -0
  103. package/dist/esm/websocket_retry_options.js.map +1 -0
  104. package/package.json +44 -25
  105. package/src/backoff/backoff.ts +6 -3
  106. package/src/backoff/constantbackoff.ts +4 -4
  107. package/src/backoff/exponentialbackoff.ts +7 -6
  108. package/src/backoff/linearbackoff.ts +9 -8
  109. package/src/index.ts +14 -13
  110. package/src/queue/array_queue.ts +1 -1
  111. package/src/queue/ring_queue.ts +12 -8
  112. package/src/websocket.ts +343 -78
  113. package/src/websocket_buffer.ts +1 -3
  114. package/src/websocket_builder.ts +33 -22
  115. package/src/websocket_event.ts +47 -13
  116. package/src/websocket_options.ts +6 -5
  117. package/src/websocket_retry_options.ts +4 -3
  118. package/dist/cjs/src/backoff/constantbackoff.d.ts.map +0 -1
  119. package/dist/cjs/src/backoff/constantbackoff.js +0 -43
  120. package/dist/cjs/src/backoff/constantbackoff.js.map +0 -1
  121. package/dist/cjs/src/backoff/exponentialbackoff.d.ts.map +0 -1
  122. package/dist/cjs/src/backoff/exponentialbackoff.js.map +0 -1
  123. package/dist/cjs/src/backoff/linearbackoff.d.ts.map +0 -1
  124. package/dist/cjs/src/backoff/linearbackoff.js.map +0 -1
  125. package/dist/cjs/src/index.d.ts +0 -14
  126. package/dist/cjs/src/index.d.ts.map +0 -1
  127. package/dist/cjs/src/index.js +0 -20
  128. package/dist/cjs/src/index.js.map +0 -1
  129. package/dist/cjs/src/queue/array_queue.d.ts.map +0 -1
  130. package/dist/cjs/src/queue/array_queue.js.map +0 -1
  131. package/dist/cjs/src/queue/queue.d.ts.map +0 -1
  132. package/dist/cjs/src/queue/ring_queue.d.ts.map +0 -1
  133. package/dist/cjs/src/queue/ring_queue.js.map +0 -1
  134. package/dist/cjs/src/websocket.d.ts.map +0 -1
  135. package/dist/cjs/src/websocket.js +0 -435
  136. package/dist/cjs/src/websocket.js.map +0 -1
  137. package/dist/cjs/src/websocket_buffer.d.ts.map +0 -1
  138. package/dist/cjs/src/websocket_buffer.js.map +0 -1
  139. package/dist/cjs/src/websocket_builder.d.ts.map +0 -1
  140. package/dist/cjs/src/websocket_builder.js +0 -263
  141. package/dist/cjs/src/websocket_builder.js.map +0 -1
  142. package/dist/cjs/src/websocket_event.d.ts +0 -67
  143. package/dist/cjs/src/websocket_event.d.ts.map +0 -1
  144. package/dist/cjs/src/websocket_event.js +0 -22
  145. package/dist/cjs/src/websocket_event.js.map +0 -1
  146. package/dist/cjs/src/websocket_options.d.ts.map +0 -1
  147. package/dist/cjs/src/websocket_options.js.map +0 -1
  148. package/dist/cjs/src/websocket_retry_options.d.ts.map +0 -1
  149. package/dist/cjs/src/websocket_retry_options.js.map +0 -1
  150. package/dist/esm/src/backoff/constantbackoff.d.ts.map +0 -1
  151. package/dist/esm/src/backoff/constantbackoff.js.map +0 -1
  152. package/dist/esm/src/backoff/exponentialbackoff.d.ts.map +0 -1
  153. package/dist/esm/src/backoff/exponentialbackoff.js.map +0 -1
  154. package/dist/esm/src/backoff/linearbackoff.d.ts.map +0 -1
  155. package/dist/esm/src/backoff/linearbackoff.js.map +0 -1
  156. package/dist/esm/src/index.d.ts +0 -14
  157. package/dist/esm/src/index.d.ts.map +0 -1
  158. package/dist/esm/src/index.js +0 -9
  159. package/dist/esm/src/index.js.map +0 -1
  160. package/dist/esm/src/queue/array_queue.d.ts.map +0 -1
  161. package/dist/esm/src/queue/array_queue.js.map +0 -1
  162. package/dist/esm/src/queue/queue.d.ts.map +0 -1
  163. package/dist/esm/src/queue/ring_queue.d.ts.map +0 -1
  164. package/dist/esm/src/queue/ring_queue.js.map +0 -1
  165. package/dist/esm/src/websocket.d.ts.map +0 -1
  166. package/dist/esm/src/websocket.js +0 -357
  167. package/dist/esm/src/websocket.js.map +0 -1
  168. package/dist/esm/src/websocket_buffer.d.ts.map +0 -1
  169. package/dist/esm/src/websocket_buffer.js.map +0 -1
  170. package/dist/esm/src/websocket_builder.d.ts.map +0 -1
  171. package/dist/esm/src/websocket_builder.js.map +0 -1
  172. package/dist/esm/src/websocket_event.d.ts +0 -67
  173. package/dist/esm/src/websocket_event.d.ts.map +0 -1
  174. package/dist/esm/src/websocket_event.js +0 -19
  175. package/dist/esm/src/websocket_event.js.map +0 -1
  176. package/dist/esm/src/websocket_options.d.ts.map +0 -1
  177. package/dist/esm/src/websocket_options.js.map +0 -1
  178. package/dist/esm/src/websocket_retry_options.d.ts.map +0 -1
  179. package/dist/esm/src/websocket_retry_options.js.map +0 -1
  180. package/eslint.config.mjs +0 -43
  181. package/tsconfig.cjs.json +0 -74
  182. package/tsconfig.esm.json +0 -74
  183. package/websocket-ts-2.2.1.tgz +0 -0
  184. /package/dist/cjs/{src/backoff → backoff}/backoff.js +0 -0
  185. /package/dist/cjs/{src/queue → queue}/queue.d.ts +0 -0
  186. /package/dist/cjs/{src/queue → queue}/queue.js +0 -0
  187. /package/dist/cjs/{src/websocket_buffer.js → websocket_buffer.js} +0 -0
  188. /package/dist/cjs/{src/websocket_options.js → websocket_options.js} +0 -0
  189. /package/dist/cjs/{src/websocket_retry_options.js → websocket_retry_options.js} +0 -0
  190. /package/dist/esm/{src/backoff → backoff}/backoff.js +0 -0
  191. /package/dist/esm/{src/queue → queue}/array_queue.js +0 -0
  192. /package/dist/esm/{src/queue → queue}/queue.d.ts +0 -0
  193. /package/dist/esm/{src/queue → queue}/queue.js +0 -0
  194. /package/dist/esm/{src/websocket_buffer.js → websocket_buffer.js} +0 -0
  195. /package/dist/esm/{src/websocket_options.js → websocket_options.js} +0 -0
  196. /package/dist/esm/{src/websocket_retry_options.js → websocket_retry_options.js} +0 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Joscha Behrmann
3
+ Copyright (c) 2020 Joscha Behrmann
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,30 +1,33 @@
1
- <div>
2
- <div align="center">
3
- <img src="https://raw.githubusercontent.com/jjxxs/websocket-ts/gh-pages/websocket-ts-logo.svg" alt="websocket-ts" width="300" height="65" />
4
- </div>
5
- <p align="center">
6
- <img src="https://github.com/jjxxs/websocket-ts/actions/workflows/build.yml/badge.svg" alt="Build Status" />
7
- <img src="https://github.com/jjxxs/websocket-ts/actions/workflows/test.yml/badge.svg" alt="Build Status" />
8
- <a href="https://coveralls.io/github/jjxxs/websocket-ts?branch=master">
9
- <img src="https://coveralls.io/repos/github/jjxxs/websocket-ts/badge.svg?branch=master&service=github" alt="Coverage Status" />
10
- </a>
11
- <a href="https://github.com/jjxxs/websocket-ts/releases/latest">
12
- <img src="https://img.shields.io/github/v/release/jjxxs/websocket-ts" alt="Release" />
13
- </a>
14
- <a href="/LICENSE">
15
- <img src="https://img.shields.io/github/license/jjxxs/websocket-ts" alt="License" />
16
- </a>
17
- </p>
1
+ <div align="center">
2
+ <img src="https://raw.githubusercontent.com/jjxxs/websocket-ts/gh-pages/websocket-ts-logo.svg" alt="websocket-ts" width="300" height="65" />
18
3
  </div>
19
-
4
+ &nbsp;
20
5
  <div align="center">
21
6
  A <b>WebSocket</b> for browsers with <b>auto-reconnect</b> and <b>message buffering</b> written in <b>TypeScript</b>.
7
+ </div>
8
+ &nbsp;
9
+ <div align="center">
10
+
11
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/jjxxs/websocket-ts/build.yml)](https://github.com/jjxxs/websocket-ts/actions/workflows/build.yml)
12
+ [![Test Status](https://img.shields.io/github/actions/workflow/status/jjxxs/websocket-ts/test.yml?label=tests)](https://github.com/jjxxs/websocket-ts/actions/workflows/test.yml)
13
+ [![Coverage Status](https://img.shields.io/coveralls/github/jjxxs/websocket-ts/main)](https://coveralls.io/github/jjxxs/websocket-ts?branch=main)
14
+ [![npm downloads](https://img.shields.io/npm/dm/websocket-ts.svg)](https://npm-stat.com/charts.html?package=websocket-ts)
15
+
16
+ </div>
17
+
18
+ <div align="center">
19
+
20
+ [![npm version](https://img.shields.io/npm/v/websocket-ts.svg)](https://www.npmjs.org/package/websocket-ts)
21
+ [![Bundle Size](https://deno.bundlejs.com/badge?q=websocket-ts)](https://bundlejs.com/?q=websocket-ts)
22
+ [![Snyk security](https://snyk.io/test/npm/websocket-ts/badge.svg)](https://snyk.io/test/npm/websocket-ts)
23
+ [![License](https://img.shields.io/npm/l/websocket-ts)](https://github.com/jjxxs/websocket-ts/blob/main/LICENSE)
24
+
22
25
  </div>
23
26
 
24
27
  ## Features
25
28
 
26
- - **Lightweight & Standalone**: No dependencies, 2.1 kB minified & gzipped.
27
- - **Browser-native**: Utilizes WebSocket API, offers direct access.
29
+ - **Lightweight & Standalone**: No dependencies, only a few kB minified & gzipped (see badge).
30
+ - **Browser-native**: Utilizes native WebSocket API, offers direct access to the underlying connection.
28
31
  - **Smart Reconnect**: Optional auto-reconnect and message buffering.
29
32
  - **Easy Setup**: Optional builder class for quick initialization.
30
33
  - **Well-Tested**: High test coverage, well-documented for extensibility.
@@ -35,11 +38,11 @@ A <b>WebSocket</b> for browsers with <b>auto-reconnect</b> and <b>message buffer
35
38
  Install `websocket-ts` with npm:
36
39
 
37
40
  ```bash
38
- $ npm install websocket-ts
41
+ npm install websocket-ts
39
42
  ```
40
43
 
41
44
  ## Quickstart
42
- This example shows how to use the package, complete with message buffering and automatic reconnection.
45
+ This example creates a WebSocket with message buffering and automatic reconnection.
43
46
  The created websocket will echo back any received messages. It will buffer messages when disconnected
44
47
  and attempt to reconnect every 1 second.
45
48
 
@@ -71,7 +74,7 @@ ws.addEventListener(WebsocketEvent.message, echoOnMessage);
71
74
  ```
72
75
 
73
76
  ## Usage
74
- This will demonstrate how to use `websocket-ts` in your project using the provided `WebsocketBuild`-class.
77
+ This section demonstrates how to use `websocket-ts` in your project using the provided `WebsocketBuilder` class.
75
78
 
76
79
  For a more detailed description of the API, please refer to the [API Documentation](https://jjxxs.github.io/websocket-ts/).
77
80
 
@@ -85,36 +88,71 @@ const ws = new WebsocketBuilder("ws://localhost:42421").build();
85
88
 
86
89
  #### Events
87
90
 
88
- There are six events which can be subscribed to through with event listeners:
91
+ There are seven events you can listen for:
92
+
93
+ | Event | Description |
94
+ |---|---|
95
+ | `open` | Connection opened |
96
+ | `close` | Connection closed |
97
+ | `error` | An error occurred |
98
+ | `message` | Message received |
99
+ | `retry` | Reconnect attempt |
100
+ | `reconnect` | Automatic retry succeeded (fires before `open`) |
101
+ | `exhausted` | Gave up reconnecting after `maxRetries` consecutive failed attempts |
102
+
103
+ You can use either `WebsocketEvent.open` or the string `"open"` when registering listeners.
104
+
105
+ The `retry`, `reconnect` and `exhausted` events are `CustomEvent`s whose `detail`
106
+ carries the number of retries and the time of the last successful connection.
107
+
108
+ The browser's own `error` events carry no details. When an automatic retry
109
+ fails because the URL provider or the `WebSocket` constructor threw, the
110
+ `error` event is an `ErrorEvent` whose `error` is the thrown value:
89
111
 
90
112
  ```typescript
91
- export enum WebsocketEvent {
92
- open = "open", // Connection opened
93
- close = "close", // Connection closed
94
- error = "error", // Error-induced closure
95
- message = "message", // Message received
96
- retry = "retry", // Reconnect attempt
97
- reconnect = "reconnect" // Successful reconnect
98
- }
113
+ ws.addEventListener(WebsocketEvent.error, (i, ev) => {
114
+ if (ev instanceof ErrorEvent) {
115
+ console.warn("connection attempt failed", ev.error);
116
+ }
117
+ });
99
118
  ```
100
119
 
120
+ Errors thrown by the `WebSocket` constructor may include the URL in their
121
+ message, so take care when forwarding them to logs if the URL contains a token.
122
+
101
123
  #### Add Event Listeners
102
124
  Event listeners receive the websocket instance (`i`) and the triggering event (`ev`) as arguments.
103
125
 
104
- ```typescript
105
- const ws = new WebsocketBuilder("ws://localhost:42421")
106
- .onOpen((i, ev) => console.log("opened"))
107
- .onClose((i, ev) => console.log("closed"))
108
- .onError((i, ev) => console.log("error"))
109
- .onMessage((i, ev) => console.log("message"))
110
- .onRetry((i, ev) => console.log("retry"))
111
- .onReconnect((i, ev) => console.log("reconnect"))
112
- .build();
113
- ```
126
+ ```typescript
127
+ const ws = new WebsocketBuilder("ws://localhost:42421")
128
+ .onOpen((i, ev) => console.log("opened"))
129
+ .onClose((i, ev) => console.log("closed"))
130
+ .onError((i, ev) => console.log("error"))
131
+ .onMessage((i, ev) => console.log("message"))
132
+ .onRetry((i, ev) => console.log("retry"))
133
+ .onReconnect((i, ev) => console.log("reconnect"))
134
+ .onExhausted((i, ev) => console.log("gave up"))
135
+ .build();
136
+ ```
137
+
138
+ Listeners accept the options `once` (remove after the first call) and `signal`
139
+ (an `AbortSignal`). Aborting the signal removes the listener again, which is
140
+ handy for tearing down a group of listeners in one call, e.g. when a component
141
+ unmounts:
142
+
143
+ ```typescript
144
+ const controller = new AbortController();
145
+ ws.addEventListener(WebsocketEvent.message, onMessage, { signal: controller.signal });
146
+ ws.addEventListener(WebsocketEvent.close, onClose, { signal: controller.signal });
147
+ /* ... on teardown: */
148
+ controller.abort(); // removes both listeners
149
+ ```
114
150
 
115
151
  #### Remove Event Listeners
116
152
 
117
- To unregister a specific event listener, use `removeEventListener`:
153
+ To unregister a specific event listener, use `removeEventListener`. Listeners
154
+ are matched by function identity; the options they were registered with are
155
+ ignored when matching:
118
156
 
119
157
  ```typescript
120
158
  let ws: Websocket
@@ -132,15 +170,20 @@ let ws: Websocket;
132
170
  ws.send("Hello World!");
133
171
  ```
134
172
 
173
+ If the websocket is not connected, the message is stored in the buffer and
174
+ sent once the connection is (re-)established — but only if a buffer was
175
+ configured (see below). Without a buffer, or after `close()` was called, the
176
+ message is silently dropped.
177
+
135
178
  #### Reconnect & Backoff (Optional)
136
179
 
137
- If you'd like the websocket to automatically reconnect upon disconnection, you can optionally provide a `Backoff` strategy.
138
- This sets the delay between reconnection attempts. There are three built-in `Backoff` implementations, or you can create
139
- your own by implementing the `Backoff` interface. If no Backoff is provided, the websocket will not attempt to reconnect.
180
+ To automatically reconnect after a disconnection, provide a `Backoff` strategy.
181
+ This controls the delay between reconnection attempts. There are three built-in `Backoff` implementations, or you can create
182
+ your own by implementing the `Backoff` interface. If no backoff is provided, the websocket will not attempt to reconnect.
140
183
 
141
184
  ##### ConstantBackoff
142
185
 
143
- The `ConstantBackoff` strategy enforces a fixed delay between each reconnection attempt.
186
+ `ConstantBackoff` uses a fixed delay between each reconnection attempt.
144
187
  To set a constant 1-second wait time, use:
145
188
 
146
189
  ```typescript
@@ -149,11 +192,11 @@ const ws = new WebsocketBuilder("ws://localhost:42421")
149
192
  .build();
150
193
  ```
151
194
 
152
- ##### LinearBackoff
195
+ #### LinearBackoff
153
196
 
154
- The `LinearBackoff` strategy increases the delay between reconnection attempts linearly,
197
+ `LinearBackoff` increases the delay between reconnection attempts linearly,
155
198
  up to an optional maximum. For example, to start with a 0-second delay and increase by
156
- 10 second for each retry, capping at 60 seconds, use:
199
+ 10 seconds for each retry, capping at 60 seconds, use:
157
200
 
158
201
  ```typescript
159
202
  const ws = new WebsocketBuilder("ws://localhost:42421")
@@ -163,9 +206,9 @@ const ws = new WebsocketBuilder("ws://localhost:42421")
163
206
 
164
207
  ##### ExponentialBackoff
165
208
 
166
- The `ExponentialBackoff` strategy doubles the delay between each reconnection attempt, up
209
+ `ExponentialBackoff` doubles the delay between each reconnection attempt, up
167
210
  to a specified maximum. This approach is inspired by the binary exponential backoff algorithm
168
- commonly used in networking. For example, to generate a backoff series like `[1s, 2s, 4s, 8s]`, use:
211
+ commonly used in networking. For example, to use a base of 1000ms with a maximum exponent of 6:
169
212
 
170
213
  ```typescript
171
214
  const ws = new WebsocketBuilder("ws://localhost:42421")
@@ -173,20 +216,66 @@ const ws = new WebsocketBuilder("ws://localhost:42421")
173
216
  .build();
174
217
  ```
175
218
 
219
+ ##### Retry Limit & Instant Reconnect
220
+
221
+ Two options refine the retry behavior. Both require a backoff to be
222
+ configured — setting them without one throws at construction:
223
+
224
+ - `withMaxRetries(n)` stops reconnecting after `n` consecutive failed
225
+ attempts. The counter resets on every successful reconnect, so the limit
226
+ applies per outage, not per websocket lifetime. When the limit is reached,
227
+ the `exhausted` event fires.
228
+ - `withInstantReconnect(true)` skips the backoff delay for the *first* retry
229
+ of an outage — useful when a dropped connection is usually recoverable
230
+ immediately (e.g. a server restart). Subsequent retries follow the backoff.
231
+
232
+ ```typescript
233
+ const ws = new WebsocketBuilder("ws://localhost:42421")
234
+ .withBackoff(new ExponentialBackoff(1000, 6))
235
+ .withMaxRetries(10) // give up after 10 consecutive failures
236
+ .withInstantReconnect(true) // first retry of an outage is instant
237
+ .build();
238
+ ```
239
+
240
+ ##### Giving Up & Resuming
241
+
242
+ When `maxRetries` is exhausted, the websocket stops retrying and fires the
243
+ `exhausted` event. Use `reconnect()` to resume — it resets the retry budget,
244
+ resolves the URL (provider) again and connects immediately. It also works on
245
+ an open or user-closed websocket, e.g. to force a new connection after an
246
+ auth-token rotation or when the browser comes back online:
247
+
248
+ ```typescript
249
+ const ws = new WebsocketBuilder("ws://localhost:42421")
250
+ .withBackoff(new ConstantBackoff(1000))
251
+ .withMaxRetries(5)
252
+ .onExhausted(() => showReconnectBanner())
253
+ .build();
254
+
255
+ reconnectButton.onclick = () => ws.reconnect();
256
+ window.addEventListener("online", () => ws.reconnect());
257
+ ```
258
+
176
259
  #### Buffer (Optional)
177
260
 
178
- To buffer outgoing messages when the websocket is disconnected, you can optionally specify
179
- a `Queue`. This queue will temporarily store your messages and send them in sequence when
261
+ To buffer outgoing messages when the websocket is disconnected, provide a `Queue`.
262
+ The queue temporarily stores messages and sends them in order when
180
263
  the websocket (re)connects. Two built-in `Queue` implementations are available, or you can
181
- create your own by implementing the `Queue` interface. If no queue is provided, messages
264
+ create your own by implementing the `Queue` interface. If no queue is provided, messages
182
265
  won't be buffered.
183
266
 
267
+ The buffer only covers the time the websocket is not open: a message handed to an open
268
+ connection is not buffered again, so it is lost if the connection drops before it arrives.
269
+ If you need guaranteed delivery, add acknowledgements at the application level.
270
+
271
+ Buffered messages are kept when you call `close()` and are sent once `reconnect()` opens a
272
+ new connection, even if the URL provider now returns a URL for a different user. At such a
273
+ boundary, clear the queue you passed in (e.g. `queue.clear()`) or create a new websocket.
274
+
184
275
  ##### RingQueue
185
276
 
186
- The `RingQueue` is a fixed-capacity, first-in-first-out (FIFO) queue. When it reaches capacity,
187
- the oldest element is removed to accommodate new ones. Reading from the queue returns and
188
- removes the oldest element. For instance, to set up a `RingQueue` with a 100-element capacity,
189
- use:
277
+ `RingQueue` is a fixed-capacity, first-in-first-out (FIFO) queue. When it reaches capacity,
278
+ the oldest element is removed to make room for new ones. For instance, to set up a `RingQueue` with a 100-element capacity:
190
279
 
191
280
  ```typescript
192
281
  const ws = new WebsocketBuilder("ws://localhost:42421")
@@ -196,8 +285,8 @@ const ws = new WebsocketBuilder("ws://localhost:42421")
196
285
 
197
286
  ##### ArrayQueue
198
287
 
199
- The ArrayQueue offers an unbounded capacity, functioning as a first-in-first-out (FIFO) queue.
200
- Reading from this queue returns and removes the oldest element. To use an `ArrayQueue`, use:
288
+ `ArrayQueue` is an unbounded first-in-first-out (FIFO) queue.
289
+ To use an `ArrayQueue`:
201
290
 
202
291
  ```typescript
203
292
  const ws = new WebsocketBuilder("ws://localhost:42421")
@@ -205,8 +294,57 @@ const ws = new WebsocketBuilder("ws://localhost:42421")
205
294
  .build();
206
295
  ```
207
296
 
297
+ #### URL Provider (Optional)
298
+
299
+ By default, the URL is a static string. To use a different URL between connection attempts, provide
300
+ a function instead. The function is called on each connection attempt, including the initial one and
301
+ any retries, and should only resolve a URL. This enables use cases like load balancing, auth token
302
+ rotation, and failover.
303
+
304
+ ```typescript
305
+ const ws = new WebsocketBuilder(() => `ws://localhost:42421?token=${getToken()}`)
306
+ .withBackoff(new ConstantBackoff(1000))
307
+ .build();
308
+ ```
309
+
310
+ Calling `reconnect()` also re-runs the provider, so a fresh URL (e.g. a new
311
+ token) can be forced without waiting for the connection to drop.
312
+
313
+ If the provider throws during an automatic retry, the websocket fires an
314
+ `error` event carrying the thrown value (see [Events](#events)) and keeps
315
+ retrying under the usual backoff and `maxRetries` rules. When called from the
316
+ constructor or `reconnect()`, the exception propagates to the caller.
317
+
318
+ ## Upgrading from 2.x
319
+
320
+ - **Imports**: import from `websocket-ts` only. Deep imports such as
321
+ `websocket-ts/dist/cjs/src/...` no longer resolve.
322
+ - **JavaScript target**: the published code is ES2018 (2.x shipped ES5/ES2015).
323
+ Transpile it yourself if you support older browsers.
324
+ - **Retry options**: setting `maxRetries` or `instantReconnect` (even to `false`)
325
+ without a backoff now throws, as does a `maxRetries` that is neither a whole
326
+ number ≥ 0 nor `Infinity`.
327
+ - **`instantReconnect`**: only the first retry of an outage is instant; later
328
+ retries follow the backoff and count towards `maxRetries`. In 2.x every retry
329
+ was instant and `maxRetries` was ignored.
330
+ - **Backoff**: the first retry now waits the first value of the series, as
331
+ documented, e.g. `ExponentialBackoff(1000)` waits 1s, 2s, 4s, … (2.x: 2s, 4s, …).
332
+ Custom `Backoff`s must return `current` from `next()` and then advance.
333
+ - **`reconnect` event**: fires (before `open`) whenever an automatic retry
334
+ succeeds, now also if the initial connection failed. A manual `reconnect()`
335
+ only fires `open`, so put (re-)subscription logic into `open`.
336
+ - **URL provider errors**: an exception from the provider (or the `WebSocket`
337
+ constructor) during an automatic retry no longer escapes as an uncaught
338
+ error that stops reconnecting. It is delivered as an `error` event (an
339
+ `ErrorEvent` carrying the thrown value), and retrying continues.
340
+ - **Listeners**: options are limited to `once` and `signal`.
341
+ `removeEventListener` removes every registration of the given function,
342
+ regardless of its options.
343
+ - **Types**: `send()` and buffers accept `string | Blob | BufferSource`;
344
+ `SharedArrayBuffer`-backed data is no longer accepted.
345
+
208
346
  ## Build & Tests
209
347
 
210
- To compile the project, execute `npm run build`.
348
+ To compile the project, run `npm run build`.
211
349
 
212
- To run tests, use `npm run test`.
350
+ To run tests, use `npm run test`.
@@ -1,6 +1,9 @@
1
1
  /**
2
2
  * A Backoff produces a series of numbers that are used to determine
3
3
  * the delay between connection-retries. Values are expected to be in milliseconds.
4
+ *
5
+ * Implementations must not call back into the websocket (e.g. close() or reconnect()):
6
+ * the websocket calls them in the middle of its lifecycle transitions.
4
7
  */
5
8
  export interface Backoff {
6
9
  /**
@@ -8,12 +11,12 @@ export interface Backoff {
8
11
  */
9
12
  readonly retries: number;
10
13
  /**
11
- * Current number in the series.
14
+ * Current number in the series, i.e. the value that the next call to next() will return.
12
15
  */
13
16
  readonly current: number;
14
17
  /**
15
- * Advances the series to the next number and returns it.
16
- * @return the next number in the series
18
+ * Returns the current number in the series and advances the series by one step.
19
+ * @return the current number in the series
17
20
  */
18
21
  next(): number;
19
22
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../../../src/backoff/backoff.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,IAAI,IAAI,MAAM,CAAC;IAEf;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;CACf"}
1
+ {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../../src/backoff/backoff.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,IAAI,IAAI,MAAM,CAAC;IAEf;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;CACf"}
@@ -1 +1 @@
1
- {"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../../../src/backoff/backoff.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../../src/backoff/backoff.ts"],"names":[],"mappings":""}
@@ -1,4 +1,4 @@
1
- import { Backoff } from "./backoff";
1
+ import { Backoff } from "./backoff.js";
2
2
  /**
3
3
  * ConstantBackoff always returns the same backoff-time.
4
4
  */
@@ -7,7 +7,7 @@ export declare class ConstantBackoff implements Backoff {
7
7
  private _retries;
8
8
  /**
9
9
  * Creates a new ConstantBackoff.
10
- * @param backoff the backoff-time to return
10
+ * @param backoff the backoff-time (in milliseconds) to return
11
11
  */
12
12
  constructor(backoff: number);
13
13
  get retries(): number;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constantbackoff.d.ts","sourceRoot":"","sources":["../../../src/backoff/constantbackoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC;;GAEG;AACH,qBAAa,eAAgB,YAAW,OAAO;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAa;IAE7B;;;OAGG;gBACS,OAAO,EAAE,MAAM;IAQ3B,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,IAAI,MAAM;IAKd,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConstantBackoff = void 0;
4
+ /**
5
+ * ConstantBackoff always returns the same backoff-time.
6
+ */
7
+ class ConstantBackoff {
8
+ /**
9
+ * Creates a new ConstantBackoff.
10
+ * @param backoff the backoff-time (in milliseconds) to return
11
+ */
12
+ constructor(backoff) {
13
+ this._retries = 0;
14
+ if (!Number.isFinite(backoff) || backoff < 0) {
15
+ throw new Error("Backoff must be a finite, non-negative number");
16
+ }
17
+ this.backoff = backoff;
18
+ }
19
+ get retries() {
20
+ return this._retries;
21
+ }
22
+ get current() {
23
+ return this.backoff;
24
+ }
25
+ next() {
26
+ this._retries++;
27
+ return this.backoff;
28
+ }
29
+ reset() {
30
+ this._retries = 0;
31
+ }
32
+ }
33
+ exports.ConstantBackoff = ConstantBackoff;
34
+ //# sourceMappingURL=constantbackoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constantbackoff.js","sourceRoot":"","sources":["../../../src/backoff/constantbackoff.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACH,MAAa,eAAe;IAI1B;;;OAGG;IACH,YAAY,OAAe;QANnB,aAAQ,GAAW,CAAC,CAAC;QAO3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IACpB,CAAC;CACF;AAhCD,0CAgCC"}
@@ -1,4 +1,4 @@
1
- import { Backoff } from "./backoff";
1
+ import { Backoff } from "./backoff.js";
2
2
  /**
3
3
  * ExponentialBackoff increases the backoff-time exponentially.
4
4
  * An optional maximum can be provided as an upper bound to the
@@ -36,7 +36,7 @@ export declare class ExponentialBackoff implements Backoff {
36
36
  private _retries;
37
37
  /**
38
38
  * Creates a new ExponentialBackoff.
39
- * @param base the base of the exponentiation
39
+ * @param base the base of the exponentiation (in milliseconds)
40
40
  * @param expMax the maximum exponent, no bound if undefined
41
41
  */
42
42
  constructor(base: number, expMax?: number);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exponentialbackoff.d.ts","sourceRoot":"","sources":["../../../src/backoff/exponentialbackoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,kBAAmB,YAAW,OAAO;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,CAAC,CAAS;IAClB,OAAO,CAAC,QAAQ,CAAa;IAE7B;;;;OAIG;gBACS,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAazC,IAAI,OAAO,WAEV;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,IAAI,MAAM;IAUd,KAAK,IAAI,IAAI;CAId"}
@@ -31,51 +31,43 @@ exports.ExponentialBackoff = void 0;
31
31
  * backoff = 1000 * 2^3 = 8000
32
32
  * ... // and so on
33
33
  */
34
- var ExponentialBackoff = /** @class */ (function () {
34
+ class ExponentialBackoff {
35
35
  /**
36
36
  * Creates a new ExponentialBackoff.
37
- * @param base the base of the exponentiation
37
+ * @param base the base of the exponentiation (in milliseconds)
38
38
  * @param expMax the maximum exponent, no bound if undefined
39
39
  */
40
- function ExponentialBackoff(base, expMax) {
40
+ constructor(base, expMax) {
41
41
  this._retries = 0;
42
- if (!Number.isInteger(base) || base < 0) {
43
- throw new Error("Base must be a positive integer or zero");
42
+ if (!Number.isFinite(base) || base < 0) {
43
+ throw new Error("Base must be a finite, non-negative number");
44
44
  }
45
45
  if (expMax !== undefined && (!Number.isInteger(expMax) || expMax < 0)) {
46
- throw new Error("ExpMax must be a undefined, a positive integer or zero");
46
+ throw new Error("ExpMax must be undefined or a non-negative integer");
47
47
  }
48
48
  this.base = base;
49
49
  this.expMax = expMax;
50
50
  this.i = 0;
51
51
  }
52
- Object.defineProperty(ExponentialBackoff.prototype, "retries", {
53
- get: function () {
54
- return this._retries;
55
- },
56
- enumerable: false,
57
- configurable: true
58
- });
59
- Object.defineProperty(ExponentialBackoff.prototype, "current", {
60
- get: function () {
61
- return this.base * Math.pow(2, this.i);
62
- },
63
- enumerable: false,
64
- configurable: true
65
- });
66
- ExponentialBackoff.prototype.next = function () {
52
+ get retries() {
53
+ return this._retries;
54
+ }
55
+ get current() {
56
+ return this.base * Math.pow(2, this.i);
57
+ }
58
+ next() {
59
+ const backoff = this.current;
67
60
  this._retries++;
68
61
  this.i =
69
62
  this.expMax === undefined
70
63
  ? this.i + 1
71
64
  : Math.min(this.i + 1, this.expMax);
72
- return this.current;
73
- };
74
- ExponentialBackoff.prototype.reset = function () {
65
+ return backoff;
66
+ }
67
+ reset() {
75
68
  this._retries = 0;
76
69
  this.i = 0;
77
- };
78
- return ExponentialBackoff;
79
- }());
70
+ }
71
+ }
80
72
  exports.ExponentialBackoff = ExponentialBackoff;
81
73
  //# sourceMappingURL=exponentialbackoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exponentialbackoff.js","sourceRoot":"","sources":["../../../src/backoff/exponentialbackoff.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAa,kBAAkB;IAM7B;;;;OAIG;IACH,YAAY,IAAY,EAAE,MAAe;QAPjC,aAAQ,GAAW,CAAC,CAAC;QAQ3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACb,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,IAAI;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,CAAC;YACJ,IAAI,CAAC,MAAM,KAAK,SAAS;gBACvB,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;gBACZ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACb,CAAC;CACF;AA9CD,gDA8CC"}
@@ -1,4 +1,4 @@
1
- import { Backoff } from "./backoff";
1
+ import { Backoff } from "./backoff.js";
2
2
  /**
3
3
  * LinearBackoff returns a backoff-time that is incremented by a fixed amount
4
4
  * with every step/retry. An optional maximum can be provided as an upper bound
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linearbackoff.d.ts","sourceRoot":"","sources":["../../../src/backoff/linearbackoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,aAAc,YAAW,OAAO;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAS;IAC9B,OAAO,CAAC,CAAC,CAAa;IACtB,OAAO,CAAC,QAAQ,CAAa;IAE7B;;;;;OAKG;gBACS,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM;IAqB5D,IAAI,OAAO,WAEV;IAED,IAAI,OAAO,IAAI,MAAM,CAIpB;IAED,IAAI,IAAI,MAAM;IAOd,KAAK,IAAI,IAAI;CAId"}