@norskvideo/moq-net 0.1.8 → 0.2.1

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 (286) hide show
  1. package/README.md +2 -2
  2. package/announce.d.ts +7 -0
  3. package/announce.d.ts.map +1 -0
  4. package/announce.js +8 -0
  5. package/announce.js.map +1 -0
  6. package/announced.d.ts +49 -91
  7. package/announced.d.ts.map +1 -1
  8. package/announced.js +21 -156
  9. package/announced.js.map +1 -1
  10. package/bandwidth.d.ts +163 -0
  11. package/bandwidth.d.ts.map +1 -0
  12. package/bandwidth.js +304 -0
  13. package/bandwidth.js.map +1 -0
  14. package/bandwidth_api.d.ts +7 -0
  15. package/bandwidth_api.d.ts.map +1 -0
  16. package/bandwidth_api.js +8 -0
  17. package/bandwidth_api.js.map +1 -0
  18. package/broadcast.d.ts +44 -35
  19. package/broadcast.d.ts.map +1 -1
  20. package/broadcast.js +104 -60
  21. package/broadcast.js.map +1 -1
  22. package/connection/accept.d.ts +16 -1
  23. package/connection/accept.d.ts.map +1 -1
  24. package/connection/accept.js +52 -28
  25. package/connection/accept.js.map +1 -1
  26. package/connection/browser.d.ts.map +1 -1
  27. package/connection/browser.js +9 -7
  28. package/connection/browser.js.map +1 -1
  29. package/connection/connect.d.ts +30 -6
  30. package/connection/connect.d.ts.map +1 -1
  31. package/connection/connect.js +110 -54
  32. package/connection/connect.js.map +1 -1
  33. package/connection/established.d.ts +17 -21
  34. package/connection/established.d.ts.map +1 -1
  35. package/connection/established.js.map +1 -1
  36. package/connection/forward.d.ts +2 -0
  37. package/connection/forward.d.ts.map +1 -0
  38. package/connection/forward.js +173 -0
  39. package/connection/forward.js.map +1 -0
  40. package/connection/handshake.d.ts +1 -0
  41. package/connection/handshake.d.ts.map +1 -1
  42. package/connection/handshake.js +5 -2
  43. package/connection/handshake.js.map +1 -1
  44. package/connection/index.d.ts +5 -5
  45. package/connection/index.d.ts.map +1 -1
  46. package/connection/index.js +4 -5
  47. package/connection/index.js.map +1 -1
  48. package/connection/pool.d.ts +186 -0
  49. package/connection/pool.d.ts.map +1 -0
  50. package/connection/pool.js +361 -0
  51. package/connection/pool.js.map +1 -0
  52. package/connection/reload.d.ts +14 -93
  53. package/connection/reload.d.ts.map +1 -1
  54. package/connection/reload.js +217 -81
  55. package/connection/reload.js.map +1 -1
  56. package/connection/stats.d.ts +3 -26
  57. package/connection/stats.d.ts.map +1 -1
  58. package/connection/stats.js.map +1 -1
  59. package/connection/transport.d.ts +0 -7
  60. package/connection/transport.d.ts.map +1 -1
  61. package/consume.d.ts +1 -43
  62. package/consume.d.ts.map +1 -1
  63. package/consume.js +1 -1
  64. package/consume.js.map +1 -1
  65. package/error.d.ts +180 -29
  66. package/error.d.ts.map +1 -1
  67. package/error.js +331 -16
  68. package/error.js.map +1 -1
  69. package/errors.d.ts +7 -0
  70. package/errors.d.ts.map +1 -0
  71. package/errors.js +8 -0
  72. package/errors.js.map +1 -0
  73. package/group.d.ts +9 -43
  74. package/group.d.ts.map +1 -1
  75. package/group.js +284 -69
  76. package/group.js.map +1 -1
  77. package/hop.d.ts +115 -0
  78. package/hop.d.ts.map +1 -0
  79. package/hop.js +119 -0
  80. package/hop.js.map +1 -0
  81. package/ietf/adapter.d.ts +5 -1
  82. package/ietf/adapter.d.ts.map +1 -1
  83. package/ietf/adapter.js +105 -60
  84. package/ietf/adapter.js.map +1 -1
  85. package/ietf/aliases.d.ts +1 -78
  86. package/ietf/aliases.d.ts.map +1 -1
  87. package/ietf/cluster.d.ts +9 -123
  88. package/ietf/cluster.d.ts.map +1 -1
  89. package/ietf/cluster.js +84 -44
  90. package/ietf/cluster.js.map +1 -1
  91. package/ietf/connection.d.ts +6 -72
  92. package/ietf/connection.d.ts.map +1 -1
  93. package/ietf/connection.js +62 -56
  94. package/ietf/connection.js.map +1 -1
  95. package/ietf/error.d.ts +11 -0
  96. package/ietf/error.d.ts.map +1 -0
  97. package/ietf/error.js +193 -0
  98. package/ietf/error.js.map +1 -0
  99. package/ietf/fetch.d.ts +7 -20
  100. package/ietf/fetch.d.ts.map +1 -1
  101. package/ietf/fetch.js +52 -22
  102. package/ietf/fetch.js.map +1 -1
  103. package/ietf/filter.d.ts +2 -0
  104. package/ietf/filter.d.ts.map +1 -1
  105. package/ietf/filter.js +10 -1
  106. package/ietf/filter.js.map +1 -1
  107. package/ietf/goaway.d.ts.map +1 -1
  108. package/ietf/goaway.js +22 -5
  109. package/ietf/goaway.js.map +1 -1
  110. package/ietf/hidden.d.ts +2 -0
  111. package/ietf/hidden.d.ts.map +1 -0
  112. package/ietf/hidden.js +30 -0
  113. package/ietf/hidden.js.map +1 -0
  114. package/ietf/index.d.ts +2 -0
  115. package/ietf/index.d.ts.map +1 -1
  116. package/ietf/index.js +2 -0
  117. package/ietf/index.js.map +1 -1
  118. package/ietf/object.d.ts +15 -8
  119. package/ietf/object.d.ts.map +1 -1
  120. package/ietf/object.js +51 -36
  121. package/ietf/object.js.map +1 -1
  122. package/ietf/parameters.d.ts +14 -2
  123. package/ietf/parameters.d.ts.map +1 -1
  124. package/ietf/parameters.js +97 -29
  125. package/ietf/parameters.js.map +1 -1
  126. package/ietf/properties.d.ts +1 -0
  127. package/ietf/properties.d.ts.map +1 -1
  128. package/ietf/properties.js +14 -0
  129. package/ietf/properties.js.map +1 -1
  130. package/ietf/publish.d.ts +19 -2
  131. package/ietf/publish.d.ts.map +1 -1
  132. package/ietf/publish.js +40 -6
  133. package/ietf/publish.js.map +1 -1
  134. package/ietf/publish_namespace.d.ts +25 -0
  135. package/ietf/publish_namespace.d.ts.map +1 -1
  136. package/ietf/publish_namespace.js +63 -0
  137. package/ietf/publish_namespace.js.map +1 -1
  138. package/ietf/publisher.d.ts +1 -82
  139. package/ietf/publisher.d.ts.map +1 -1
  140. package/ietf/publisher.js +483 -235
  141. package/ietf/publisher.js.map +1 -1
  142. package/ietf/solicit.d.ts +1 -40
  143. package/ietf/solicit.d.ts.map +1 -1
  144. package/ietf/subscribe.d.ts +8 -6
  145. package/ietf/subscribe.d.ts.map +1 -1
  146. package/ietf/subscribe.js +33 -27
  147. package/ietf/subscribe.js.map +1 -1
  148. package/ietf/subscribe_namespace.d.ts +8 -2
  149. package/ietf/subscribe_namespace.d.ts.map +1 -1
  150. package/ietf/subscribe_namespace.js +18 -8
  151. package/ietf/subscribe_namespace.js.map +1 -1
  152. package/ietf/subscriber.d.ts +1 -65
  153. package/ietf/subscriber.d.ts.map +1 -1
  154. package/ietf/subscriber.js +337 -123
  155. package/ietf/subscriber.js.map +1 -1
  156. package/ietf/token.d.ts +2 -0
  157. package/ietf/token.d.ts.map +1 -0
  158. package/ietf/token.js +99 -0
  159. package/ietf/token.js.map +1 -0
  160. package/ietf/track.d.ts +4 -0
  161. package/ietf/track.d.ts.map +1 -1
  162. package/ietf/track.js +6 -20
  163. package/ietf/track.js.map +1 -1
  164. package/ietf/version.d.ts +12 -1
  165. package/ietf/version.d.ts.map +1 -1
  166. package/ietf/version.js +13 -0
  167. package/ietf/version.js.map +1 -1
  168. package/index.d.ts +12 -7
  169. package/index.d.ts.map +1 -1
  170. package/index.js +10 -5
  171. package/index.js.map +1 -1
  172. package/internal.d.ts +115 -1
  173. package/internal.d.ts.map +1 -1
  174. package/internal.js +108 -0
  175. package/internal.js.map +1 -1
  176. package/lite/announce.d.ts +60 -10
  177. package/lite/announce.d.ts.map +1 -1
  178. package/lite/announce.js +176 -31
  179. package/lite/announce.js.map +1 -1
  180. package/lite/connection.d.ts +9 -59
  181. package/lite/connection.d.ts.map +1 -1
  182. package/lite/connection.js +34 -35
  183. package/lite/connection.js.map +1 -1
  184. package/lite/datagram.d.ts +3 -2
  185. package/lite/datagram.d.ts.map +1 -1
  186. package/lite/datagram.js +7 -8
  187. package/lite/datagram.js.map +1 -1
  188. package/lite/fetch.d.ts +15 -1
  189. package/lite/fetch.d.ts.map +1 -1
  190. package/lite/fetch.js +39 -7
  191. package/lite/fetch.js.map +1 -1
  192. package/lite/goaway.d.ts.map +1 -1
  193. package/lite/goaway.js +7 -1
  194. package/lite/goaway.js.map +1 -1
  195. package/lite/group.d.ts +30 -12
  196. package/lite/group.d.ts.map +1 -1
  197. package/lite/group.js +68 -26
  198. package/lite/group.js.map +1 -1
  199. package/lite/message.d.ts +2 -2
  200. package/lite/message.d.ts.map +1 -1
  201. package/lite/message.js +14 -5
  202. package/lite/message.js.map +1 -1
  203. package/lite/priority.d.ts +1 -61
  204. package/lite/priority.d.ts.map +1 -1
  205. package/lite/priority.js +4 -5
  206. package/lite/priority.js.map +1 -1
  207. package/lite/publisher.d.ts +1 -69
  208. package/lite/publisher.d.ts.map +1 -1
  209. package/lite/publisher.js +630 -262
  210. package/lite/publisher.js.map +1 -1
  211. package/lite/setup.d.ts +8 -8
  212. package/lite/setup.d.ts.map +1 -1
  213. package/lite/setup.js +33 -30
  214. package/lite/setup.js.map +1 -1
  215. package/lite/subscribe.d.ts +71 -17
  216. package/lite/subscribe.d.ts.map +1 -1
  217. package/lite/subscribe.js +205 -53
  218. package/lite/subscribe.js.map +1 -1
  219. package/lite/subscriber.d.ts +15 -56
  220. package/lite/subscriber.d.ts.map +1 -1
  221. package/lite/subscriber.js +451 -247
  222. package/lite/subscriber.js.map +1 -1
  223. package/lite/track.d.ts +4 -10
  224. package/lite/track.d.ts.map +1 -1
  225. package/lite/track.js +34 -29
  226. package/lite/track.js.map +1 -1
  227. package/lite/version.d.ts +42 -6
  228. package/lite/version.d.ts.map +1 -1
  229. package/lite/version.js +126 -10
  230. package/lite/version.js.map +1 -1
  231. package/origin.d.ts +256 -29
  232. package/origin.d.ts.map +1 -1
  233. package/origin.js +1427 -37
  234. package/origin.js.map +1 -1
  235. package/package.json +8 -3
  236. package/path.d.ts +25 -7
  237. package/path.d.ts.map +1 -1
  238. package/path.js +5 -3
  239. package/path.js.map +1 -1
  240. package/stream.d.ts +73 -14
  241. package/stream.d.ts.map +1 -1
  242. package/stream.js +372 -141
  243. package/stream.js.map +1 -1
  244. package/tail.d.ts +18 -0
  245. package/tail.d.ts.map +1 -0
  246. package/tail.js +167 -0
  247. package/tail.js.map +1 -0
  248. package/time.d.ts +15 -2
  249. package/time.d.ts.map +1 -1
  250. package/time.js +28 -9
  251. package/time.js.map +1 -1
  252. package/track.d.ts +211 -83
  253. package/track.d.ts.map +1 -1
  254. package/track.js +816 -205
  255. package/track.js.map +1 -1
  256. package/util/abort.d.ts +2 -0
  257. package/util/abort.d.ts.map +1 -0
  258. package/util/abort.js +20 -0
  259. package/util/abort.js.map +1 -0
  260. package/util/log.d.ts +5 -0
  261. package/util/log.d.ts.map +1 -0
  262. package/util/log.js +17 -0
  263. package/util/log.js.map +1 -0
  264. package/util/u64.d.ts +39 -0
  265. package/util/u64.d.ts.map +1 -0
  266. package/util/u64.js +83 -0
  267. package/util/u64.js.map +1 -0
  268. package/util/varint.d.ts +29 -0
  269. package/util/varint.d.ts.map +1 -0
  270. package/util/varint.js +198 -0
  271. package/util/varint.js.map +1 -0
  272. package/varint.d.ts +10 -6
  273. package/varint.d.ts.map +1 -1
  274. package/varint.js +40 -237
  275. package/varint.js.map +1 -1
  276. package/wire.d.ts +80 -0
  277. package/wire.d.ts.map +1 -0
  278. package/wire.js +32 -0
  279. package/wire.js.map +1 -0
  280. package/zod.d.ts +1 -1
  281. package/zod.d.ts.map +1 -1
  282. package/zod.js.map +1 -1
  283. package/mock.d.ts +0 -66
  284. package/mock.d.ts.map +0 -1
  285. package/mock.js +0 -243
  286. package/mock.js.map +0 -1
@@ -0,0 +1,186 @@
1
+ /**
2
+ * A reconnecting, shareable connection: one origin and one reconnect loop per relay URL.
3
+ *
4
+ * @module
5
+ */
6
+ import { type GetPromise, type Getter, Signal } from "@norskvideo/moq-signals";
7
+ import * as Announce from "../announced";
8
+ import type { Handle } from "../bandwidth";
9
+ import * as Origin from "../origin";
10
+ import * as Path from "../path";
11
+ import * as Time from "../time";
12
+ import { type AcceptProps as AcceptPropsType, accept } from "./accept";
13
+ import { isWebTransportSupported } from "./browser";
14
+ import { type CertificateHash as CertificateHashType, type ConnectProps as ConnectPropsType, certificateHash, connect, type WebSocketProps as WebSocketPropsType, type WebTransportProps as WebTransportPropsType } from "./connect";
15
+ import type { Established as EstablishedType } from "./established";
16
+ import { type ReloadDelay, type ReloadStatus } from "./reload";
17
+ import type { Probe as ProbeType, Stats as StatsType } from "./stats";
18
+ import type { Transport as TransportType } from "./transport";
19
+ /** Options for {@link Connection}. */
20
+ export interface ConnectionProps {
21
+ /** The relay to connect to; pass a `Signal` to switch relays live. */
22
+ url?: URL | Signal<URL | undefined>;
23
+ /** Whether to connect at all (default: true); pass a `Signal` to toggle live. */
24
+ enabled?: boolean | Signal<boolean>;
25
+ /**
26
+ * How long the underlying connection outlives its last handle (default: 2000ms).
27
+ *
28
+ * The window is what makes moving an element around the DOM free: the connection and
29
+ * everything it discovered are still warm when the new owner asks for them. The longest
30
+ * linger requested by any handle sharing the connection wins.
31
+ */
32
+ linger?: Time.Milli;
33
+ /**
34
+ * Share a pooled transport keyed on the URL. By default, options specific to this
35
+ * handle use a private reconnect loop; otherwise connections share by URL.
36
+ * Explicit `true` refuses options a pool cannot honor.
37
+ */
38
+ share?: boolean;
39
+ /** WebTransport options applied to each connection attempt (not reactive). */
40
+ webtransport?: WebTransportPropsType;
41
+ /** WebSocket fallback options applied to each connection attempt (not reactive). */
42
+ websocket?: WebSocketPropsType;
43
+ /** Whether the relay supports broadcast discovery. */
44
+ discovery?: boolean;
45
+ /** The origin whose broadcasts are served, spanning reconnects. Implies a private loop. */
46
+ publish?: Origin.Consumer;
47
+ /** The origin fed with the peer's announced broadcasts, spanning reconnects. Implies a private loop. */
48
+ consume?: Origin.Producer;
49
+ /** Backoff settings for the reconnect loop; an unset field uses its default. */
50
+ delay?: ReloadDelay;
51
+ /** A Connection owns the abort signal for each connection attempt. */
52
+ signal?: never;
53
+ /** A one-shot transport cannot be reused by the reconnect loop; call {@link Connection.connect} instead. */
54
+ transport?: never;
55
+ }
56
+ /**
57
+ * A cloneable handle on a MoQ connection: `new Connection({ url })` reconnects and, by
58
+ * default, shares one origin and one reconnect loop with every other handle on that URL.
59
+ *
60
+ * The shared {@link origin} is wired to both directions. Everything the relay announces
61
+ * lands in it, a publish into it is announced to the relay, and a page that publishes and
62
+ * watches the same path resolves it locally with no round trip.
63
+ *
64
+ * {@link close} releases this handle; the connection survives its last handle by a short
65
+ * linger window (see {@link ConnectionProps.linger}), so a component torn down and rebuilt
66
+ * reuses the warm connection instead of redialing.
67
+ *
68
+ * The loop reconnects for as long as a handle holds it, so an outage of any length recovers
69
+ * on its own. An auth rejection stops retrying those credentials and retires the shared
70
+ * connection so the next handle dials fresh; a new URL on this handle starts another
71
+ * sequence. {@link closed} settles only when this handle is released.
72
+ *
73
+ * Options the pool cannot honor (transport options, discovery, delay, a pinned
74
+ * certificate, caller-owned origins) use a private loop. An explicit `share: true`
75
+ * refuses them. A supplied transport cannot reconnect at all; pass it to {@link Connection.connect}
76
+ * instead.
77
+ *
78
+ * @public
79
+ */
80
+ export declare class Connection {
81
+ #private;
82
+ /** Establish a one-shot session; a supplied transport belongs here, not in the reconnect loop. */
83
+ static readonly connect: typeof connect;
84
+ /** Accept a one-shot session on an already-open transport. */
85
+ static readonly accept: typeof accept;
86
+ /** SHA-256 of a certificate, for pinning via `webtransport.serverCertificateHashes`. */
87
+ static readonly certificateHash: typeof certificateHash;
88
+ /** Whether this runtime can connect with WebTransport. */
89
+ static readonly isWebTransportSupported: typeof isWebTransportSupported;
90
+ /** Relay URL to connect to; updating it reconnects to that URL. */
91
+ url: Signal<URL | undefined>;
92
+ /** Whether to hold a connection at all; clearing it releases this handle's share. */
93
+ enabled: Signal<boolean>;
94
+ /** Current status of the connection. */
95
+ readonly status: Getter<ReloadStatus>;
96
+ /**
97
+ * The failure that stopped retrying the current URL, or undefined while the loop is live.
98
+ *
99
+ * Set on an auth rejection or when a private retry window expires. Cleared when a new URL
100
+ * or a disable/re-enable starts another sequence, not on a page hide/show.
101
+ */
102
+ readonly error: Getter<Error | undefined>;
103
+ /**
104
+ * The wire transport the current session runs over, or undefined while disconnected.
105
+ *
106
+ * The session itself is deliberately not exposed: it is shared, so no handle may close
107
+ * or reconfigure it, and everything else it offers is reachable through {@link origin}.
108
+ */
109
+ readonly transport: Getter<TransportType | undefined>;
110
+ /** The current connection's PROBE estimates, or undefined while disconnected. */
111
+ readonly probe: Getter<ProbeType | undefined>;
112
+ /**
113
+ * The send-side bandwidth allocator for the current URL, or undefined
114
+ * while disabled or URL-less.
115
+ *
116
+ * Every handle on this URL shares the same instance, so publishers reserve
117
+ * against one registry. Borrowed, not owned: the type has no close, since
118
+ * closing it would starve every other handle.
119
+ */
120
+ readonly bandwidth: Getter<Handle | undefined>;
121
+ /**
122
+ * The origin for the current URL, or undefined while disabled or URL-less.
123
+ *
124
+ * Publish into it or consume from it; a shared handle's origin is the same one every
125
+ * other handle on this URL uses, and it spans the connection's reconnects. Borrowed,
126
+ * not owned: the type has no close, since closing it would tear the origin down under
127
+ * every other handle.
128
+ */
129
+ readonly origin: Getter<Origin.Table | undefined>;
130
+ /**
131
+ * Settles once this handle is released via {@link close}: `null` on a clean close, or
132
+ * the abort {@link Error}. Peek it synchronously (`undefined` while open), observe it
133
+ * reactively, or `await` it. Attempt failures live on {@link error} instead.
134
+ */
135
+ get closed(): GetPromise<Error | null>;
136
+ /**
137
+ * Take a handle on the connection for {@link ConnectionProps.url}.
138
+ *
139
+ * Dials immediately when a URL is given and `enabled` is not false; otherwise waits for
140
+ * the signals to say go. The handle owns nothing but its own share: {@link close}
141
+ * releases it, and the underlying connection and origin live for as long as any handle
142
+ * (plus the linger window) wants them.
143
+ */
144
+ constructor(props?: ConnectionProps);
145
+ /**
146
+ * Subscribe to broadcast announcements matching `scope`, spanning reconnects
147
+ * and URL switches: a switch retracts everything from
148
+ * the old relay's origin, then the new one's arrivals stream in.
149
+ */
150
+ announced(scope?: Path.Pattern, options?: Announce.Options): Announce.Consumer;
151
+ /** Snapshot the live connection's transport counters, or undefined while disconnected. */
152
+ stats(): Promise<StatsType | undefined>;
153
+ /**
154
+ * Release this handle. The shared connection closes once its last handle is gone and
155
+ * the linger window passes; other handles on the URL are unaffected. Idempotent.
156
+ */
157
+ close(abort?: Error): void;
158
+ }
159
+ /** Types on {@link Connection}: the handle is the class, these are its associated types. */
160
+ export declare namespace Connection {
161
+ /** Options for {@link Connection}. */
162
+ type Props = ConnectionProps;
163
+ /** Options for {@link Connection.connect}. */
164
+ type ConnectProps = ConnectPropsType;
165
+ /** Options for {@link Connection.accept}. */
166
+ type AcceptProps = AcceptPropsType;
167
+ /** Backoff settings for a private reconnect loop. */
168
+ type Backoff = ReloadDelay;
169
+ /** Current state of a {@link Connection}. */
170
+ type Status = ReloadStatus;
171
+ /** The current connection's PROBE estimates. */
172
+ type Probe = ProbeType;
173
+ /** A point-in-time snapshot of the transport's counters. */
174
+ type Stats = StatsType;
175
+ /** The wire transport a session runs over. */
176
+ type Transport = TransportType;
177
+ /** Tuning for the WebSocket fallback. */
178
+ type WebSocketProps = WebSocketPropsType;
179
+ /** WebTransport options, including friendlier certificate pinning. */
180
+ type WebTransportProps = WebTransportPropsType;
181
+ /** A server certificate hash used to pin a self-signed server. */
182
+ type CertificateHash = CertificateHashType;
183
+ /** An established one-shot session. */
184
+ type Established = EstablishedType;
185
+ }
186
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/connection/pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,361 @@
1
+ /* @ts-self-types="./pool.d.ts" */
2
+ /**
3
+ * A reconnecting, shareable connection: one origin and one reconnect loop per relay URL.
4
+ *
5
+ * @module
6
+ */
7
+ import { Effect, Once, Signal } from "@norskvideo/moq-signals";
8
+ import * as Announce from "../announced.js";
9
+ import * as Origin from "../origin.js";
10
+ import * as Path from "../path.js";
11
+ import * as Time from "../time.js";
12
+ import { accept } from "./accept.js";
13
+ import { isWebTransportSupported } from "./browser.js";
14
+ import { certificateHash, connect, } from "./connect.js";
15
+ import { Reload } from "./reload.js";
16
+ /** How long an unreferenced shared connection lingers before it actually closes. */
17
+ const LINGER_MS = Time.Milli(2000);
18
+ /**
19
+ * A cloneable handle on a MoQ connection: `new Connection({ url })` reconnects and, by
20
+ * default, shares one origin and one reconnect loop with every other handle on that URL.
21
+ *
22
+ * The shared {@link origin} is wired to both directions. Everything the relay announces
23
+ * lands in it, a publish into it is announced to the relay, and a page that publishes and
24
+ * watches the same path resolves it locally with no round trip.
25
+ *
26
+ * {@link close} releases this handle; the connection survives its last handle by a short
27
+ * linger window (see {@link ConnectionProps.linger}), so a component torn down and rebuilt
28
+ * reuses the warm connection instead of redialing.
29
+ *
30
+ * The loop reconnects for as long as a handle holds it, so an outage of any length recovers
31
+ * on its own. An auth rejection stops retrying those credentials and retires the shared
32
+ * connection so the next handle dials fresh; a new URL on this handle starts another
33
+ * sequence. {@link closed} settles only when this handle is released.
34
+ *
35
+ * Options the pool cannot honor (transport options, discovery, delay, a pinned
36
+ * certificate, caller-owned origins) use a private loop. An explicit `share: true`
37
+ * refuses them. A supplied transport cannot reconnect at all; pass it to {@link Connection.connect}
38
+ * instead.
39
+ *
40
+ * @public
41
+ */
42
+ export class Connection {
43
+ /** Establish a one-shot session; a supplied transport belongs here, not in the reconnect loop. */
44
+ static connect = connect;
45
+ /** Accept a one-shot session on an already-open transport. */
46
+ static accept = accept;
47
+ /** SHA-256 of a certificate, for pinning via `webtransport.serverCertificateHashes`. */
48
+ static certificateHash = certificateHash;
49
+ /** Whether this runtime can connect with WebTransport. */
50
+ static isWebTransportSupported = isWebTransportSupported;
51
+ /** Relay URL to connect to; updating it reconnects to that URL. */
52
+ url;
53
+ /** Whether to hold a connection at all; clearing it releases this handle's share. */
54
+ enabled;
55
+ /** Current status of the connection. */
56
+ status;
57
+ /**
58
+ * The failure that stopped retrying the current URL, or undefined while the loop is live.
59
+ *
60
+ * Set on an auth rejection or when a private retry window expires. Cleared when a new URL
61
+ * or a disable/re-enable starts another sequence, not on a page hide/show.
62
+ */
63
+ error;
64
+ /**
65
+ * The wire transport the current session runs over, or undefined while disconnected.
66
+ *
67
+ * The session itself is deliberately not exposed: it is shared, so no handle may close
68
+ * or reconfigure it, and everything else it offers is reachable through {@link origin}.
69
+ */
70
+ transport;
71
+ /** The current connection's PROBE estimates, or undefined while disconnected. */
72
+ probe;
73
+ /**
74
+ * The send-side bandwidth allocator for the current URL, or undefined
75
+ * while disabled or URL-less.
76
+ *
77
+ * Every handle on this URL shares the same instance, so publishers reserve
78
+ * against one registry. Borrowed, not owned: the type has no close, since
79
+ * closing it would starve every other handle.
80
+ */
81
+ bandwidth;
82
+ /**
83
+ * The origin for the current URL, or undefined while disabled or URL-less.
84
+ *
85
+ * Publish into it or consume from it; a shared handle's origin is the same one every
86
+ * other handle on this URL uses, and it spans the connection's reconnects. Borrowed,
87
+ * not owned: the type has no close, since closing it would tear the origin down under
88
+ * every other handle.
89
+ */
90
+ origin;
91
+ /**
92
+ * Settles once this handle is released via {@link close}: `null` on a clean close, or
93
+ * the abort {@link Error}. Peek it synchronously (`undefined` while open), observe it
94
+ * reactively, or `await` it. Attempt failures live on {@link error} instead.
95
+ */
96
+ get closed() {
97
+ return this.#closed;
98
+ }
99
+ #closed = new Once();
100
+ #status = new Signal("disconnected");
101
+ #established = new Signal(undefined);
102
+ #probe = new Signal(undefined);
103
+ #origin = new Signal(undefined);
104
+ #bandwidth = new Signal(undefined);
105
+ #error = new Signal(undefined);
106
+ #signals;
107
+ /**
108
+ * Take a handle on the connection for {@link ConnectionProps.url}.
109
+ *
110
+ * Dials immediately when a URL is given and `enabled` is not false; otherwise waits for
111
+ * the signals to say go. The handle owns nothing but its own share: {@link close}
112
+ * releases it, and the underlying connection and origin live for as long as any handle
113
+ * (plus the linger window) wants them.
114
+ */
115
+ constructor(props) {
116
+ refuse(props);
117
+ this.#signals = new Effect();
118
+ this.url = Signal.from(props?.url);
119
+ this.enabled = Signal.from(props?.enabled ?? true);
120
+ this.status = this.#status;
121
+ this.error = this.#error;
122
+ this.probe = this.#probe;
123
+ this.origin = this.#origin;
124
+ this.bandwidth = this.#bandwidth;
125
+ this.transport = this.#signals.computed((effect) => effect.get(this.#established)?.transport);
126
+ const linger = props?.linger;
127
+ // Key on the serialized URL: URL objects use identity equality, and an equivalent
128
+ // instance must not release and redial.
129
+ const href = this.#signals.computed((effect) => effect.get(this.url)?.href);
130
+ if (props && (props.share === false || requiresPrivate(props))) {
131
+ this.#runPrivate(props, href);
132
+ return;
133
+ }
134
+ this.#signals.run((effect) => {
135
+ if (!effect.get(this.enabled))
136
+ return;
137
+ const key = effect.get(href);
138
+ if (!key)
139
+ return;
140
+ const lease = acquire(key, linger);
141
+ effect.cleanup(lease.release);
142
+ effect.set(this.#origin, lease.origin, undefined);
143
+ effect.set(this.#bandwidth, lease.connection.bandwidth, undefined);
144
+ // Proxy the shared loop's outputs, so this handle reads like its own connection.
145
+ effect.run((nested) => nested.set(this.#status, nested.get(lease.connection.status), "disconnected"));
146
+ effect.run((nested) => nested.set(this.#established, nested.get(lease.connection.established), undefined));
147
+ effect.run((nested) => nested.set(this.#probe, nested.get(lease.connection.probe), undefined));
148
+ effect.run((nested) => nested.set(this.#error, nested.get(lease.connection.error), undefined));
149
+ });
150
+ }
151
+ #runPrivate(props, href) {
152
+ const owned = props.consume === undefined;
153
+ const origin = props.consume ?? new Origin.Producer();
154
+ if (owned)
155
+ this.#signals.cleanup(() => origin.close());
156
+ const loop = new Reload({
157
+ url: this.url,
158
+ enabled: this.enabled,
159
+ publish: props.publish ?? (owned ? origin.consume() : undefined),
160
+ consume: origin,
161
+ webtransport: props.webtransport,
162
+ websocket: props.websocket,
163
+ discovery: props.discovery,
164
+ // A handle nobody watches wants unlimited retries; an auth rejection still
165
+ // stops this URL, and a new one starts another sequence.
166
+ delay: { timeout: Time.Milli(0), ...props.delay },
167
+ });
168
+ this.#signals.cleanup(() => loop.close());
169
+ this.#signals.run((effect) => {
170
+ if (!effect.get(this.enabled) || !effect.get(href))
171
+ return;
172
+ effect.set(this.#origin, origin, undefined);
173
+ effect.set(this.#bandwidth, loop.bandwidth, undefined);
174
+ });
175
+ this.#signals.run((effect) => effect.set(this.#status, effect.get(loop.status), "disconnected"));
176
+ this.#signals.run((effect) => effect.set(this.#established, effect.get(loop.established), undefined));
177
+ this.#signals.run((effect) => effect.set(this.#probe, effect.get(loop.probe), undefined));
178
+ this.#signals.run((effect) => effect.set(this.#error, effect.get(loop.error), undefined));
179
+ }
180
+ /**
181
+ * Subscribe to broadcast announcements matching `scope`, spanning reconnects
182
+ * and URL switches: a switch retracts everything from
183
+ * the old relay's origin, then the new one's arrivals stream in.
184
+ */
185
+ announced(scope = Path.Pattern.all(), options) {
186
+ const producer = new Announce.Producer();
187
+ const consumer = producer.consume();
188
+ // Closing the consumer closes the shared state, so stop appending after that.
189
+ let closed = false;
190
+ void consumer.closed.then(() => {
191
+ closed = true;
192
+ });
193
+ // A child of this handle's scope rather than a standalone Effect it merely cleans up
194
+ // after: the disposer run() hands back also drops itself from the parent, so opening
195
+ // and closing announcement streams repeatedly does not pile up dead pumps that live
196
+ // until the whole handle closes.
197
+ const stop = this.#signals.run((effect) => {
198
+ const origin = effect.get(this.#origin);
199
+ if (!origin)
200
+ return;
201
+ const upstream = origin.announced(scope, options);
202
+ effect.cleanup(() => upstream.close());
203
+ // Track what this origin announced so a URL switch retracts it; the last
204
+ // event rides along for the retraction.
205
+ const active = new Map();
206
+ effect.spawn(async () => {
207
+ try {
208
+ for (;;) {
209
+ const entry = await effect.race(upstream.next());
210
+ if (!entry)
211
+ break;
212
+ if (Announce.isActive(entry.kind))
213
+ active.set(entry.prefix, entry);
214
+ else
215
+ active.delete(entry.prefix);
216
+ producer.append(entry);
217
+ }
218
+ }
219
+ finally {
220
+ if (!closed) {
221
+ for (const entry of active.values()) {
222
+ producer.append({ ...entry, kind: "retracted" });
223
+ }
224
+ }
225
+ }
226
+ });
227
+ });
228
+ void consumer.closed.then(stop);
229
+ return consumer;
230
+ }
231
+ /** Snapshot the live connection's transport counters, or undefined while disconnected. */
232
+ async stats() {
233
+ return this.#established.peek()?.stats();
234
+ }
235
+ /**
236
+ * Release this handle. The shared connection closes once its last handle is gone and
237
+ * the linger window passes; other handles on the URL are unaffected. Idempotent.
238
+ */
239
+ close(abort) {
240
+ this.#signals.close();
241
+ if (this.#closed.peek() === undefined)
242
+ this.#closed.set(abort ?? null);
243
+ }
244
+ }
245
+ /** Throw if `props` cannot be honored, rather than silently dropping them. */
246
+ function refuse(props) {
247
+ if (!props)
248
+ return;
249
+ const extra = props;
250
+ if (extra.transport) {
251
+ throw new Error("a supplied transport cannot reconnect; call Connection.connect() instead");
252
+ }
253
+ if (extra.signal) {
254
+ throw new Error("a Connection owns its abort signal; do not pass one");
255
+ }
256
+ if (props.share !== true || !requiresPrivate(props))
257
+ return;
258
+ if (props.publish || props.consume) {
259
+ throw new Error("caller-owned origins cannot be shared; pass share: false");
260
+ }
261
+ const hashes = props.webtransport?.serverCertificateHashes?.length ?? 0;
262
+ if (props.webtransport?.serverCertificate !== undefined || hashes > 0) {
263
+ throw new Error("a pinned certificate cannot be shared; pass share: false");
264
+ }
265
+ if (props.webtransport !== undefined) {
266
+ throw new Error("webtransport options cannot be shared; pass share: false");
267
+ }
268
+ if (props.websocket !== undefined) {
269
+ throw new Error("websocket options cannot be shared; pass share: false");
270
+ }
271
+ if (props.discovery !== undefined) {
272
+ throw new Error("discovery cannot be shared; pass share: false");
273
+ }
274
+ if (props.delay !== undefined) {
275
+ throw new Error("delay cannot be shared; pass share: false");
276
+ }
277
+ }
278
+ /** Options tied to one handle cannot be represented by a URL-keyed shared entry. */
279
+ function requiresPrivate(props) {
280
+ return (props.webtransport !== undefined ||
281
+ props.websocket !== undefined ||
282
+ props.discovery !== undefined ||
283
+ props.delay !== undefined ||
284
+ props.publish !== undefined ||
285
+ props.consume !== undefined);
286
+ }
287
+ /** The process-wide pool backing {@link Connection}. */
288
+ const pool = new Map();
289
+ /** Take a reference on the shared entry for `key`, creating it on first use. */
290
+ function acquire(key, linger) {
291
+ let entry = pool.get(key);
292
+ if (!entry) {
293
+ const origin = new Origin.Producer();
294
+ const connection = new Reload({
295
+ url: new URL(key),
296
+ enabled: true,
297
+ publish: origin.consume(),
298
+ consume: origin,
299
+ // Nobody observes a shared loop's `closed`, so giving up would strand every handle
300
+ // on this URL offline until the page reloads. Retry for as long as the entry lives
301
+ // instead; an auth rejection still stops this URL, and evicts below.
302
+ delay: { timeout: Time.Milli(0) },
303
+ });
304
+ const created = { origin, connection, refs: 0, linger: linger ?? LINGER_MS };
305
+ // The loop only stops on a peer saying these credentials will never work. Drop the
306
+ // entry so a later handle dials fresh rather than joining a loop that has stopped;
307
+ // handles already on it keep it until they release, since a redial would be refused
308
+ // the same way.
309
+ connection.error.subscribe((err) => {
310
+ if (err !== undefined && pool.get(key) === created)
311
+ pool.delete(key);
312
+ });
313
+ entry = created;
314
+ pool.set(key, created);
315
+ }
316
+ const taken = entry;
317
+ taken.linger = Time.Milli(Math.max(taken.linger, linger ?? LINGER_MS));
318
+ taken.refs += 1;
319
+ if (taken.timer !== undefined) {
320
+ clearTimeout(taken.timer);
321
+ taken.timer = undefined;
322
+ }
323
+ let released = false;
324
+ return {
325
+ ...taken,
326
+ release: () => {
327
+ if (released)
328
+ return;
329
+ released = true;
330
+ taken.refs -= 1;
331
+ if (taken.refs > 0)
332
+ return;
333
+ taken.timer = setTimeout(() => {
334
+ if (pool.get(key) === taken)
335
+ pool.delete(key);
336
+ taken.connection.close();
337
+ taken.origin.close();
338
+ }, taken.linger);
339
+ // Don't hold a Node process open for a connection nobody is using.
340
+ taken.timer.unref?.();
341
+ },
342
+ };
343
+ }
344
+ /**
345
+ * Close every shared connection immediately, so the next handle dials fresh.
346
+ *
347
+ * Exists for tests, which otherwise share connections across cases.
348
+ *
349
+ * @internal
350
+ */
351
+ export function resetShared() {
352
+ const entries = [...pool.values()];
353
+ pool.clear();
354
+ for (const entry of entries) {
355
+ if (entry.timer !== undefined)
356
+ clearTimeout(entry.timer);
357
+ entry.connection.close();
358
+ entry.origin.close();
359
+ }
360
+ }
361
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/connection/pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A reconnecting, shareable connection: one origin and one reconnect loop per relay URL.\n *\n * @module\n */\nimport { Effect, type GetPromise, type Getter, Once, Signal } from \"@moq/signals\";\nimport * as Announce from \"../announced.ts\";\nimport type { Handle } from \"../bandwidth.ts\";\nimport * as Origin from \"../origin.ts\";\nimport * as Path from \"../path.ts\";\nimport * as Time from \"../time.ts\";\nimport { type AcceptProps as AcceptPropsType, accept } from \"./accept.ts\";\nimport { isWebTransportSupported } from \"./browser.ts\";\nimport {\n\ttype CertificateHash as CertificateHashType,\n\ttype ConnectProps as ConnectPropsType,\n\tcertificateHash,\n\tconnect,\n\ttype WebSocketProps as WebSocketPropsType,\n\ttype WebTransportProps as WebTransportPropsType,\n} from \"./connect.ts\";\nimport type { Established as EstablishedType } from \"./established.ts\";\nimport { Reload, type ReloadDelay, type ReloadStatus } from \"./reload.ts\";\nimport type { Probe as ProbeType, Stats as StatsType } from \"./stats.ts\";\nimport type { Transport as TransportType } from \"./transport.ts\";\n\n/** How long an unreferenced shared connection lingers before it actually closes. */\nconst LINGER_MS = Time.Milli(2000);\n\n/** Options for {@link Connection}. */\nexport interface ConnectionProps {\n\t/** The relay to connect to; pass a `Signal` to switch relays live. */\n\turl?: URL | Signal<URL | undefined>;\n\n\t/** Whether to connect at all (default: true); pass a `Signal` to toggle live. */\n\tenabled?: boolean | Signal<boolean>;\n\n\t/**\n\t * How long the underlying connection outlives its last handle (default: 2000ms).\n\t *\n\t * The window is what makes moving an element around the DOM free: the connection and\n\t * everything it discovered are still warm when the new owner asks for them. The longest\n\t * linger requested by any handle sharing the connection wins.\n\t */\n\tlinger?: Time.Milli;\n\n\t/**\n\t * Share a pooled transport keyed on the URL. By default, options specific to this\n\t * handle use a private reconnect loop; otherwise connections share by URL.\n\t * Explicit `true` refuses options a pool cannot honor.\n\t */\n\tshare?: boolean;\n\n\t/** WebTransport options applied to each connection attempt (not reactive). */\n\twebtransport?: WebTransportPropsType;\n\n\t/** WebSocket fallback options applied to each connection attempt (not reactive). */\n\twebsocket?: WebSocketPropsType;\n\n\t/** Whether the relay supports broadcast discovery. */\n\tdiscovery?: boolean;\n\n\t/** The origin whose broadcasts are served, spanning reconnects. Implies a private loop. */\n\tpublish?: Origin.Consumer;\n\n\t/** The origin fed with the peer's announced broadcasts, spanning reconnects. Implies a private loop. */\n\tconsume?: Origin.Producer;\n\n\t/** Backoff settings for the reconnect loop; an unset field uses its default. */\n\tdelay?: ReloadDelay;\n\n\t/** A Connection owns the abort signal for each connection attempt. */\n\tsignal?: never;\n\n\t/** A one-shot transport cannot be reused by the reconnect loop; call {@link Connection.connect} instead. */\n\ttransport?: never;\n}\n\n/**\n * A cloneable handle on a MoQ connection: `new Connection({ url })` reconnects and, by\n * default, shares one origin and one reconnect loop with every other handle on that URL.\n *\n * The shared {@link origin} is wired to both directions. Everything the relay announces\n * lands in it, a publish into it is announced to the relay, and a page that publishes and\n * watches the same path resolves it locally with no round trip.\n *\n * {@link close} releases this handle; the connection survives its last handle by a short\n * linger window (see {@link ConnectionProps.linger}), so a component torn down and rebuilt\n * reuses the warm connection instead of redialing.\n *\n * The loop reconnects for as long as a handle holds it, so an outage of any length recovers\n * on its own. An auth rejection stops retrying those credentials and retires the shared\n * connection so the next handle dials fresh; a new URL on this handle starts another\n * sequence. {@link closed} settles only when this handle is released.\n *\n * Options the pool cannot honor (transport options, discovery, delay, a pinned\n * certificate, caller-owned origins) use a private loop. An explicit `share: true`\n * refuses them. A supplied transport cannot reconnect at all; pass it to {@link Connection.connect}\n * instead.\n *\n * @public\n */\nexport class Connection {\n\t/** Establish a one-shot session; a supplied transport belongs here, not in the reconnect loop. */\n\tstatic readonly connect = connect;\n\n\t/** Accept a one-shot session on an already-open transport. */\n\tstatic readonly accept = accept;\n\n\t/** SHA-256 of a certificate, for pinning via `webtransport.serverCertificateHashes`. */\n\tstatic readonly certificateHash = certificateHash;\n\n\t/** Whether this runtime can connect with WebTransport. */\n\tstatic readonly isWebTransportSupported = isWebTransportSupported;\n\n\t/** Relay URL to connect to; updating it reconnects to that URL. */\n\turl: Signal<URL | undefined>;\n\n\t/** Whether to hold a connection at all; clearing it releases this handle's share. */\n\tenabled: Signal<boolean>;\n\n\t/** Current status of the connection. */\n\treadonly status: Getter<ReloadStatus>;\n\n\t/**\n\t * The failure that stopped retrying the current URL, or undefined while the loop is live.\n\t *\n\t * Set on an auth rejection or when a private retry window expires. Cleared when a new URL\n\t * or a disable/re-enable starts another sequence, not on a page hide/show.\n\t */\n\treadonly error: Getter<Error | undefined>;\n\n\t/**\n\t * The wire transport the current session runs over, or undefined while disconnected.\n\t *\n\t * The session itself is deliberately not exposed: it is shared, so no handle may close\n\t * or reconfigure it, and everything else it offers is reachable through {@link origin}.\n\t */\n\treadonly transport: Getter<TransportType | undefined>;\n\n\t/** The current connection's PROBE estimates, or undefined while disconnected. */\n\treadonly probe: Getter<ProbeType | undefined>;\n\n\t/**\n\t * The send-side bandwidth allocator for the current URL, or undefined\n\t * while disabled or URL-less.\n\t *\n\t * Every handle on this URL shares the same instance, so publishers reserve\n\t * against one registry. Borrowed, not owned: the type has no close, since\n\t * closing it would starve every other handle.\n\t */\n\treadonly bandwidth: Getter<Handle | undefined>;\n\n\t/**\n\t * The origin for the current URL, or undefined while disabled or URL-less.\n\t *\n\t * Publish into it or consume from it; a shared handle's origin is the same one every\n\t * other handle on this URL uses, and it spans the connection's reconnects. Borrowed,\n\t * not owned: the type has no close, since closing it would tear the origin down under\n\t * every other handle.\n\t */\n\treadonly origin: Getter<Origin.Table | undefined>;\n\n\t/**\n\t * Settles once this handle is released via {@link close}: `null` on a clean close, or\n\t * the abort {@link Error}. Peek it synchronously (`undefined` while open), observe it\n\t * reactively, or `await` it. Attempt failures live on {@link error} instead.\n\t */\n\tget closed(): GetPromise<Error | null> {\n\t\treturn this.#closed;\n\t}\n\n\t#closed = new Once<Error | null>();\n\treadonly #status = new Signal<ReloadStatus>(\"disconnected\");\n\treadonly #established = new Signal<EstablishedType | undefined>(undefined);\n\treadonly #probe = new Signal<ProbeType | undefined>(undefined);\n\treadonly #origin = new Signal<Origin.Producer | undefined>(undefined);\n\treadonly #bandwidth = new Signal<Handle | undefined>(undefined);\n\treadonly #error = new Signal<Error | undefined>(undefined);\n\t#signals: Effect;\n\n\t/**\n\t * Take a handle on the connection for {@link ConnectionProps.url}.\n\t *\n\t * Dials immediately when a URL is given and `enabled` is not false; otherwise waits for\n\t * the signals to say go. The handle owns nothing but its own share: {@link close}\n\t * releases it, and the underlying connection and origin live for as long as any handle\n\t * (plus the linger window) wants them.\n\t */\n\tconstructor(props?: ConnectionProps) {\n\t\trefuse(props);\n\t\tthis.#signals = new Effect();\n\n\t\tthis.url = Signal.from(props?.url);\n\t\tthis.enabled = Signal.from(props?.enabled ?? true);\n\t\tthis.status = this.#status;\n\t\tthis.error = this.#error;\n\t\tthis.probe = this.#probe;\n\t\tthis.origin = this.#origin;\n\t\tthis.bandwidth = this.#bandwidth;\n\t\tthis.transport = this.#signals.computed((effect) => effect.get(this.#established)?.transport);\n\n\t\tconst linger = props?.linger;\n\n\t\t// Key on the serialized URL: URL objects use identity equality, and an equivalent\n\t\t// instance must not release and redial.\n\t\tconst href = this.#signals.computed((effect) => effect.get(this.url)?.href);\n\n\t\tif (props && (props.share === false || requiresPrivate(props))) {\n\t\t\tthis.#runPrivate(props, href);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#signals.run((effect) => {\n\t\t\tif (!effect.get(this.enabled)) return;\n\t\t\tconst key = effect.get(href);\n\t\t\tif (!key) return;\n\n\t\t\tconst lease = acquire(key, linger);\n\t\t\teffect.cleanup(lease.release);\n\n\t\t\teffect.set(this.#origin, lease.origin, undefined);\n\t\t\teffect.set(this.#bandwidth, lease.connection.bandwidth, undefined);\n\n\t\t\t// Proxy the shared loop's outputs, so this handle reads like its own connection.\n\t\t\teffect.run((nested) => nested.set(this.#status, nested.get(lease.connection.status), \"disconnected\"));\n\t\t\teffect.run((nested) => nested.set(this.#established, nested.get(lease.connection.established), undefined));\n\t\t\teffect.run((nested) => nested.set(this.#probe, nested.get(lease.connection.probe), undefined));\n\t\t\teffect.run((nested) => nested.set(this.#error, nested.get(lease.connection.error), undefined));\n\t\t});\n\t}\n\n\t#runPrivate(props: ConnectionProps, href: Getter<string | undefined>): void {\n\t\tconst owned = props.consume === undefined;\n\t\tconst origin = props.consume ?? new Origin.Producer();\n\t\tif (owned) this.#signals.cleanup(() => origin.close());\n\n\t\tconst loop = new Reload({\n\t\t\turl: this.url,\n\t\t\tenabled: this.enabled,\n\t\t\tpublish: props.publish ?? (owned ? origin.consume() : undefined),\n\t\t\tconsume: origin,\n\t\t\twebtransport: props.webtransport,\n\t\t\twebsocket: props.websocket,\n\t\t\tdiscovery: props.discovery,\n\t\t\t// A handle nobody watches wants unlimited retries; an auth rejection still\n\t\t\t// stops this URL, and a new one starts another sequence.\n\t\t\tdelay: { timeout: Time.Milli(0), ...props.delay },\n\t\t});\n\t\tthis.#signals.cleanup(() => loop.close());\n\n\t\tthis.#signals.run((effect) => {\n\t\t\tif (!effect.get(this.enabled) || !effect.get(href)) return;\n\t\t\teffect.set(this.#origin, origin, undefined);\n\t\t\teffect.set(this.#bandwidth, loop.bandwidth, undefined);\n\t\t});\n\t\tthis.#signals.run((effect) => effect.set(this.#status, effect.get(loop.status), \"disconnected\"));\n\t\tthis.#signals.run((effect) => effect.set(this.#established, effect.get(loop.established), undefined));\n\t\tthis.#signals.run((effect) => effect.set(this.#probe, effect.get(loop.probe), undefined));\n\t\tthis.#signals.run((effect) => effect.set(this.#error, effect.get(loop.error), undefined));\n\t}\n\n\t/**\n\t * Subscribe to broadcast announcements matching `scope`, spanning reconnects\n\t * and URL switches: a switch retracts everything from\n\t * the old relay's origin, then the new one's arrivals stream in.\n\t */\n\tannounced(scope: Path.Pattern = Path.Pattern.all(), options?: Announce.Options): Announce.Consumer {\n\t\tconst producer = new Announce.Producer();\n\t\tconst consumer = producer.consume();\n\n\t\t// Closing the consumer closes the shared state, so stop appending after that.\n\t\tlet closed = false;\n\t\tvoid consumer.closed.then(() => {\n\t\t\tclosed = true;\n\t\t});\n\n\t\t// A child of this handle's scope rather than a standalone Effect it merely cleans up\n\t\t// after: the disposer run() hands back also drops itself from the parent, so opening\n\t\t// and closing announcement streams repeatedly does not pile up dead pumps that live\n\t\t// until the whole handle closes.\n\t\tconst stop = this.#signals.run((effect) => {\n\t\t\tconst origin = effect.get(this.#origin);\n\t\t\tif (!origin) return;\n\n\t\t\tconst upstream = origin.announced(scope, options);\n\t\t\teffect.cleanup(() => upstream.close());\n\n\t\t\t// Track what this origin announced so a URL switch retracts it; the last\n\t\t\t// event rides along for the retraction.\n\t\t\tconst active = new Map<Path.Valid, Announce.Update>();\n\n\t\t\teffect.spawn(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tconst entry = await effect.race(upstream.next());\n\t\t\t\t\t\tif (!entry) break;\n\t\t\t\t\t\tif (Announce.isActive(entry.kind)) active.set(entry.prefix, entry);\n\t\t\t\t\t\telse active.delete(entry.prefix);\n\t\t\t\t\t\tproducer.append(entry);\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tif (!closed) {\n\t\t\t\t\t\tfor (const entry of active.values()) {\n\t\t\t\t\t\t\tproducer.append({ ...entry, kind: \"retracted\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tvoid consumer.closed.then(stop);\n\n\t\treturn consumer;\n\t}\n\n\t/** Snapshot the live connection's transport counters, or undefined while disconnected. */\n\tasync stats(): Promise<StatsType | undefined> {\n\t\treturn this.#established.peek()?.stats();\n\t}\n\n\t/**\n\t * Release this handle. The shared connection closes once its last handle is gone and\n\t * the linger window passes; other handles on the URL are unaffected. Idempotent.\n\t */\n\tclose(abort?: Error): void {\n\t\tthis.#signals.close();\n\t\tif (this.#closed.peek() === undefined) this.#closed.set(abort ?? null);\n\t}\n}\n\n/** Types on {@link Connection}: the handle is the class, these are its associated types. */\nexport namespace Connection {\n\t/** Options for {@link Connection}. */\n\texport type Props = ConnectionProps;\n\t/** Options for {@link Connection.connect}. */\n\texport type ConnectProps = ConnectPropsType;\n\t/** Options for {@link Connection.accept}. */\n\texport type AcceptProps = AcceptPropsType;\n\t/** Backoff settings for a private reconnect loop. */\n\texport type Backoff = ReloadDelay;\n\t/** Current state of a {@link Connection}. */\n\texport type Status = ReloadStatus;\n\t/** The current connection's PROBE estimates. */\n\texport type Probe = ProbeType;\n\t/** A point-in-time snapshot of the transport's counters. */\n\texport type Stats = StatsType;\n\t/** The wire transport a session runs over. */\n\texport type Transport = TransportType;\n\t/** Tuning for the WebSocket fallback. */\n\texport type WebSocketProps = WebSocketPropsType;\n\t/** WebTransport options, including friendlier certificate pinning. */\n\texport type WebTransportProps = WebTransportPropsType;\n\t/** A server certificate hash used to pin a self-signed server. */\n\texport type CertificateHash = CertificateHashType;\n\t/** An established one-shot session. */\n\texport type Established = EstablishedType;\n}\n\n/** Throw if `props` cannot be honored, rather than silently dropping them. */\nfunction refuse(props?: ConnectionProps): void {\n\tif (!props) return;\n\n\tconst extra = props as ConnectionProps & { transport?: unknown; signal?: unknown };\n\tif (extra.transport) {\n\t\tthrow new Error(\"a supplied transport cannot reconnect; call Connection.connect() instead\");\n\t}\n\tif (extra.signal) {\n\t\tthrow new Error(\"a Connection owns its abort signal; do not pass one\");\n\t}\n\tif (props.share !== true || !requiresPrivate(props)) return;\n\n\tif (props.publish || props.consume) {\n\t\tthrow new Error(\"caller-owned origins cannot be shared; pass share: false\");\n\t}\n\tconst hashes = props.webtransport?.serverCertificateHashes?.length ?? 0;\n\tif (props.webtransport?.serverCertificate !== undefined || hashes > 0) {\n\t\tthrow new Error(\"a pinned certificate cannot be shared; pass share: false\");\n\t}\n\tif (props.webtransport !== undefined) {\n\t\tthrow new Error(\"webtransport options cannot be shared; pass share: false\");\n\t}\n\tif (props.websocket !== undefined) {\n\t\tthrow new Error(\"websocket options cannot be shared; pass share: false\");\n\t}\n\tif (props.discovery !== undefined) {\n\t\tthrow new Error(\"discovery cannot be shared; pass share: false\");\n\t}\n\tif (props.delay !== undefined) {\n\t\tthrow new Error(\"delay cannot be shared; pass share: false\");\n\t}\n}\n\n/** Options tied to one handle cannot be represented by a URL-keyed shared entry. */\nfunction requiresPrivate(props: ConnectionProps): boolean {\n\treturn (\n\t\tprops.webtransport !== undefined ||\n\t\tprops.websocket !== undefined ||\n\t\tprops.discovery !== undefined ||\n\t\tprops.delay !== undefined ||\n\t\tprops.publish !== undefined ||\n\t\tprops.consume !== undefined\n\t);\n}\n\n/** One shared connection and the handles keeping it alive. */\ninterface Entry {\n\torigin: Origin.Producer;\n\tconnection: Reload;\n\trefs: number;\n\tlinger: Time.Milli;\n\ttimer?: ReturnType<typeof setTimeout>;\n}\n\n/** The process-wide pool backing {@link Connection}. */\nconst pool = new Map<string, Entry>();\n\n/** Take a reference on the shared entry for `key`, creating it on first use. */\nfunction acquire(key: string, linger?: Time.Milli): Entry & { release: () => void } {\n\tlet entry = pool.get(key);\n\tif (!entry) {\n\t\tconst origin = new Origin.Producer();\n\t\tconst connection = new Reload({\n\t\t\turl: new URL(key),\n\t\t\tenabled: true,\n\t\t\tpublish: origin.consume(),\n\t\t\tconsume: origin,\n\t\t\t// Nobody observes a shared loop's `closed`, so giving up would strand every handle\n\t\t\t// on this URL offline until the page reloads. Retry for as long as the entry lives\n\t\t\t// instead; an auth rejection still stops this URL, and evicts below.\n\t\t\tdelay: { timeout: Time.Milli(0) },\n\t\t});\n\n\t\tconst created: Entry = { origin, connection, refs: 0, linger: linger ?? LINGER_MS };\n\n\t\t// The loop only stops on a peer saying these credentials will never work. Drop the\n\t\t// entry so a later handle dials fresh rather than joining a loop that has stopped;\n\t\t// handles already on it keep it until they release, since a redial would be refused\n\t\t// the same way.\n\t\tconnection.error.subscribe((err) => {\n\t\t\tif (err !== undefined && pool.get(key) === created) pool.delete(key);\n\t\t});\n\n\t\tentry = created;\n\t\tpool.set(key, created);\n\t}\n\n\tconst taken = entry;\n\ttaken.linger = Time.Milli(Math.max(taken.linger, linger ?? LINGER_MS));\n\ttaken.refs += 1;\n\tif (taken.timer !== undefined) {\n\t\tclearTimeout(taken.timer);\n\t\ttaken.timer = undefined;\n\t}\n\n\tlet released = false;\n\treturn {\n\t\t...taken,\n\t\trelease: () => {\n\t\t\tif (released) return;\n\t\t\treleased = true;\n\n\t\t\ttaken.refs -= 1;\n\t\t\tif (taken.refs > 0) return;\n\n\t\t\ttaken.timer = setTimeout(() => {\n\t\t\t\tif (pool.get(key) === taken) pool.delete(key);\n\t\t\t\ttaken.connection.close();\n\t\t\t\ttaken.origin.close();\n\t\t\t}, taken.linger);\n\n\t\t\t// Don't hold a Node process open for a connection nobody is using.\n\t\t\t(taken.timer as { unref?: () => void }).unref?.();\n\t\t},\n\t};\n}\n\n/**\n * Close every shared connection immediately, so the next handle dials fresh.\n *\n * Exists for tests, which otherwise share connections across cases.\n *\n * @internal\n */\nexport function resetShared(): void {\n\tconst entries = [...pool.values()];\n\tpool.clear();\n\tfor (const entry of entries) {\n\t\tif (entry.timer !== undefined) clearTimeout(entry.timer);\n\t\tentry.connection.close();\n\t\tentry.origin.close();\n\t}\n}\n"]}