zero-query 1.1.1 → 1.2.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 (154) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +2 -0
  3. package/cli/args.js +33 -33
  4. package/cli/commands/build-api.js +443 -442
  5. package/cli/commands/build.js +254 -247
  6. package/cli/commands/bundle.js +1228 -1224
  7. package/cli/commands/create.js +137 -121
  8. package/cli/commands/dev/devtools/index.js +56 -56
  9. package/cli/commands/dev/devtools/js/components.js +49 -49
  10. package/cli/commands/dev/devtools/js/core.js +423 -423
  11. package/cli/commands/dev/devtools/js/elements.js +421 -421
  12. package/cli/commands/dev/devtools/js/network.js +166 -166
  13. package/cli/commands/dev/devtools/js/performance.js +73 -73
  14. package/cli/commands/dev/devtools/js/router.js +105 -105
  15. package/cli/commands/dev/devtools/js/source.js +132 -132
  16. package/cli/commands/dev/devtools/js/stats.js +35 -35
  17. package/cli/commands/dev/devtools/js/tabs.js +79 -79
  18. package/cli/commands/dev/devtools/panel.html +95 -95
  19. package/cli/commands/dev/devtools/styles.css +244 -244
  20. package/cli/commands/dev/index.js +107 -107
  21. package/cli/commands/dev/logger.js +75 -75
  22. package/cli/commands/dev/overlay.js +858 -858
  23. package/cli/commands/dev/server.js +220 -220
  24. package/cli/commands/dev/validator.js +94 -94
  25. package/cli/commands/dev/watcher.js +172 -172
  26. package/cli/help.js +114 -112
  27. package/cli/index.js +52 -52
  28. package/cli/scaffold/default/LICENSE +21 -21
  29. package/cli/scaffold/default/app/app.js +207 -207
  30. package/cli/scaffold/default/app/components/about.js +201 -201
  31. package/cli/scaffold/default/app/components/api-demo.js +143 -143
  32. package/cli/scaffold/default/app/components/contact-card.js +231 -231
  33. package/cli/scaffold/default/app/components/contacts/contacts.css +706 -706
  34. package/cli/scaffold/default/app/components/contacts/contacts.html +200 -200
  35. package/cli/scaffold/default/app/components/contacts/contacts.js +196 -196
  36. package/cli/scaffold/default/app/components/counter.js +127 -127
  37. package/cli/scaffold/default/app/components/home.js +249 -249
  38. package/cli/scaffold/default/app/components/not-found.js +16 -16
  39. package/cli/scaffold/default/app/components/playground/playground.css +115 -115
  40. package/cli/scaffold/default/app/components/playground/playground.html +161 -161
  41. package/cli/scaffold/default/app/components/playground/playground.js +116 -116
  42. package/cli/scaffold/default/app/components/todos.js +225 -225
  43. package/cli/scaffold/default/app/components/toolkit/toolkit.css +97 -97
  44. package/cli/scaffold/default/app/components/toolkit/toolkit.html +146 -146
  45. package/cli/scaffold/default/app/components/toolkit/toolkit.js +280 -280
  46. package/cli/scaffold/default/app/routes.js +15 -15
  47. package/cli/scaffold/default/app/store.js +101 -101
  48. package/cli/scaffold/default/global.css +552 -552
  49. package/cli/scaffold/default/index.html +99 -99
  50. package/cli/scaffold/minimal/app/app.js +85 -85
  51. package/cli/scaffold/minimal/app/components/about.js +68 -68
  52. package/cli/scaffold/minimal/app/components/counter.js +122 -122
  53. package/cli/scaffold/minimal/app/components/home.js +68 -68
  54. package/cli/scaffold/minimal/app/components/not-found.js +16 -16
  55. package/cli/scaffold/minimal/app/routes.js +9 -9
  56. package/cli/scaffold/minimal/app/store.js +36 -36
  57. package/cli/scaffold/minimal/global.css +300 -300
  58. package/cli/scaffold/minimal/index.html +44 -44
  59. package/cli/scaffold/ssr/app/app.js +41 -41
  60. package/cli/scaffold/ssr/app/components/about.js +55 -55
  61. package/cli/scaffold/ssr/app/components/blog/index.js +65 -65
  62. package/cli/scaffold/ssr/app/components/blog/post.js +86 -86
  63. package/cli/scaffold/ssr/app/components/home.js +37 -37
  64. package/cli/scaffold/ssr/app/components/not-found.js +15 -15
  65. package/cli/scaffold/ssr/app/routes.js +8 -8
  66. package/cli/scaffold/ssr/global.css +228 -228
  67. package/cli/scaffold/ssr/index.html +37 -37
  68. package/cli/scaffold/ssr/package.json +8 -8
  69. package/cli/scaffold/ssr/server/data/posts.js +144 -144
  70. package/cli/scaffold/ssr/server/index.js +213 -213
  71. package/cli/scaffold/webrtc/app/app.js +11 -0
  72. package/cli/scaffold/webrtc/app/components/video-room.js +295 -0
  73. package/cli/scaffold/webrtc/app/lib/room.js +252 -0
  74. package/cli/scaffold/webrtc/assets/.gitkeep +0 -0
  75. package/cli/scaffold/webrtc/global.css +250 -0
  76. package/cli/scaffold/webrtc/index.html +21 -0
  77. package/cli/utils.js +305 -287
  78. package/dist/API.md +661 -0
  79. package/dist/zquery.dist.zip +0 -0
  80. package/dist/zquery.js +10313 -6614
  81. package/dist/zquery.min.js +8 -631
  82. package/index.d.ts +570 -371
  83. package/index.js +311 -240
  84. package/package.json +76 -70
  85. package/src/component.js +1709 -1691
  86. package/src/core.js +921 -921
  87. package/src/diff.js +497 -497
  88. package/src/errors.js +209 -209
  89. package/src/expression.js +922 -922
  90. package/src/http.js +242 -242
  91. package/src/package.json +1 -1
  92. package/src/reactive.js +255 -255
  93. package/src/router.js +843 -843
  94. package/src/ssr.js +418 -418
  95. package/src/store.js +318 -318
  96. package/src/utils.js +515 -515
  97. package/src/webrtc/e2ee.js +351 -0
  98. package/src/webrtc/errors.js +116 -0
  99. package/src/webrtc/ice.js +301 -0
  100. package/src/webrtc/index.js +131 -0
  101. package/src/webrtc/joinToken.js +119 -0
  102. package/src/webrtc/observe.js +172 -0
  103. package/src/webrtc/peer.js +351 -0
  104. package/src/webrtc/reactive.js +268 -0
  105. package/src/webrtc/room.js +625 -0
  106. package/src/webrtc/sdp.js +302 -0
  107. package/src/webrtc/sfu/index.js +43 -0
  108. package/src/webrtc/sfu/livekit.js +131 -0
  109. package/src/webrtc/sfu/mediasoup.js +150 -0
  110. package/src/webrtc/signaling.js +373 -0
  111. package/src/webrtc/turn.js +237 -0
  112. package/tests/_helpers/webrtcFakes.js +289 -0
  113. package/tests/audit.test.js +4158 -4158
  114. package/tests/cli.test.js +1136 -1103
  115. package/tests/compare.test.js +497 -486
  116. package/tests/component.test.js +3969 -3938
  117. package/tests/core.test.js +1910 -1910
  118. package/tests/dev-server.test.js +489 -489
  119. package/tests/diff.test.js +1416 -1416
  120. package/tests/docs.test.js +1664 -1650
  121. package/tests/electron-features.test.js +864 -864
  122. package/tests/errors.test.js +619 -619
  123. package/tests/expression.test.js +1056 -1056
  124. package/tests/http.test.js +648 -648
  125. package/tests/reactive.test.js +819 -819
  126. package/tests/router.test.js +2327 -2327
  127. package/tests/ssr.test.js +870 -870
  128. package/tests/store.test.js +830 -830
  129. package/tests/test-minifier.js +153 -153
  130. package/tests/test-ssr.js +27 -27
  131. package/tests/utils.test.js +1377 -1377
  132. package/tests/webrtc/e2ee.test.js +283 -0
  133. package/tests/webrtc/ice.test.js +202 -0
  134. package/tests/webrtc/joinToken.test.js +89 -0
  135. package/tests/webrtc/observe.test.js +111 -0
  136. package/tests/webrtc/peer.test.js +373 -0
  137. package/tests/webrtc/reactive.test.js +235 -0
  138. package/tests/webrtc/room.test.js +406 -0
  139. package/tests/webrtc/sdp.test.js +151 -0
  140. package/tests/webrtc/sfu-livekit.test.js +119 -0
  141. package/tests/webrtc/sfu.test.js +160 -0
  142. package/tests/webrtc/signaling.test.js +251 -0
  143. package/tests/webrtc/turn.test.js +256 -0
  144. package/types/collection.d.ts +383 -383
  145. package/types/component.d.ts +186 -186
  146. package/types/errors.d.ts +135 -135
  147. package/types/http.d.ts +92 -92
  148. package/types/misc.d.ts +201 -201
  149. package/types/reactive.d.ts +98 -98
  150. package/types/router.d.ts +190 -190
  151. package/types/ssr.d.ts +102 -102
  152. package/types/store.d.ts +146 -146
  153. package/types/utils.d.ts +245 -245
  154. package/types/webrtc.d.ts +653 -0
@@ -0,0 +1,653 @@
1
+ /**
2
+ * types/webrtc.d.ts - WebRTC client type surface
3
+ *
4
+ * Mirrors the full target shape from `.myshit/webrtc-client-roadmap.md` §4
5
+ * so subsequent passes only ever ADD members. The implementation only
6
+ * wires `SignalingClient` and the error family in this release - higher
7
+ * level helpers are declared here but throw at runtime until they land.
8
+ */
9
+
10
+ import type { Signal } from './reactive';
11
+ import { ZQueryError } from './errors';
12
+
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Error family
16
+ // ---------------------------------------------------------------------------
17
+
18
+ /** Options accepted by every WebRTC error constructor. */
19
+ export interface WebRtcErrorOptions {
20
+ /** Stable, programmatic error code. */
21
+ code?: string;
22
+ /** Extra structured context (peer id, room id, etc.). */
23
+ context?: Record<string, any>;
24
+ /** Original error, if any. */
25
+ cause?: Error;
26
+ }
27
+
28
+ /** Base WebRTC error - all other classes derive from this. */
29
+ export class WebRtcError extends ZQueryError {
30
+ constructor(message: string, options?: WebRtcErrorOptions);
31
+ }
32
+
33
+ /** Signaling-channel (WebSocket / protocol) error. */
34
+ export class SignalingError extends WebRtcError {
35
+ constructor(message: string, options?: WebRtcErrorOptions);
36
+ }
37
+
38
+ /** ICE candidate / connectivity error. */
39
+ export class IceError extends WebRtcError {
40
+ constructor(message: string, options?: WebRtcErrorOptions);
41
+ }
42
+
43
+ /** SDP parse / validate error. */
44
+ export class SdpError extends WebRtcError {
45
+ constructor(message: string, options?: WebRtcErrorOptions);
46
+ }
47
+
48
+ /** TURN credential fetch / refresh error. */
49
+ export class TurnError extends WebRtcError {
50
+ constructor(message: string, options?: WebRtcErrorOptions);
51
+ }
52
+
53
+ /** End-to-end encryption error. */
54
+ export class E2eeError extends WebRtcError {
55
+ constructor(message: string, options?: WebRtcErrorOptions);
56
+ }
57
+
58
+ /** SFU adapter (mediasoup / LiveKit) error. */
59
+ export class SfuError extends WebRtcError {
60
+ constructor(message: string, options?: WebRtcErrorOptions);
61
+ }
62
+
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // SignalingClient
66
+ // ---------------------------------------------------------------------------
67
+
68
+ /** Reconnect tuning passed to `new SignalingClient(url, { reconnect })`. */
69
+ export interface SignalingReconnectOptions {
70
+ /** Base backoff between attempts, in milliseconds. Default `250`. */
71
+ baseMs?: number;
72
+ /** Hard cap on per-attempt backoff. Default `8000`. */
73
+ capMs?: number;
74
+ /** Max reconnect attempts before giving up. Default `10`. */
75
+ maxRetries?: number;
76
+ }
77
+
78
+ /** All options accepted by the `SignalingClient` constructor. */
79
+ export interface SignalingClientOptions {
80
+ /** Reconnect tuning. Pass `false` to disable auto-reconnect entirely. */
81
+ reconnect?: false | SignalingReconnectOptions;
82
+ /** ICE coalesce window length, in milliseconds. Default `200`. */
83
+ iceFlushMs?: number;
84
+ /** Max ICE frames flushed per coalesce window. Default `10`. */
85
+ iceBatch?: number;
86
+ /** WebSocket constructor (defaults to the global, useful for tests / SSR). */
87
+ WebSocket?: typeof WebSocket;
88
+ }
89
+
90
+ /** Low-level WebSocket signaling client (speaks `@zero-server/webrtc` wire). */
91
+ export class SignalingClient {
92
+ /** Server URL passed to the constructor. */
93
+ readonly url: string;
94
+ /** Server-assigned peer id - populated when the first `hello` frame arrives. */
95
+ readonly peerId: string | null;
96
+ /** `true` while the underlying WebSocket is open. */
97
+ readonly connected: boolean;
98
+ /** `true` once `.close()` has been called - no further reconnects. */
99
+ readonly closed: boolean;
100
+
101
+ constructor(url: string, options?: SignalingClientOptions);
102
+
103
+ /** Open the socket. Resolves on first `open`. */
104
+ connect(): Promise<void>;
105
+
106
+ /** Send a frame `{ type, ...payload }`. `ice` frames are auto-coalesced. */
107
+ send(type: string, payload?: Record<string, any>): void;
108
+
109
+ /** Register a listener for a server frame type or lifecycle event. */
110
+ on(type: string, cb: (payload: any) => void): () => void;
111
+
112
+ /** Remove a previously registered listener. */
113
+ off(type: string, cb: (payload: any) => void): void;
114
+
115
+ /** Gracefully close the socket (sends `bye`, cancels reconnects). */
116
+ close(): void;
117
+ }
118
+
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Peer (RTCPeerConnection wrapper + perfect negotiation)
122
+ // ---------------------------------------------------------------------------
123
+
124
+ /** Options accepted by the `Peer` constructor. */
125
+ export interface PeerOptions {
126
+ /** Perfect-negotiation polite flag (defaults to `false`). */
127
+ polite?: boolean;
128
+ /** STUN/TURN servers forwarded to the underlying `RTCPeerConnection`. */
129
+ iceServers?: RTCIceServer[];
130
+ /** RTCPeerConnection constructor override (tests / non-browser shims). */
131
+ RTCPeerConnection?: typeof RTCPeerConnection;
132
+ /** Hard cap on trickled ICE candidates per peer. Default `30`. */
133
+ maxIceCandidates?: number;
134
+ /** Extra `RTCConfiguration` fields merged into the PC config. */
135
+ rtcConfig?: RTCConfiguration;
136
+ }
137
+
138
+ /** Lifecycle event names emitted by a `Peer`. */
139
+ export type PeerEvent =
140
+ | 'track'
141
+ | 'datachannel'
142
+ | 'connectionstatechange'
143
+ | 'close'
144
+ | 'error';
145
+
146
+ /** Per-remote-peer wrapper around `RTCPeerConnection` with perfect negotiation. */
147
+ export class Peer {
148
+ /** Remote peer id (matches `from`/`to` on the wire). */
149
+ readonly id: string;
150
+ /** Shared signaling client. */
151
+ readonly signaling: SignalingClient;
152
+ /** Perfect-negotiation polite flag (set at construction). */
153
+ readonly polite: boolean;
154
+ /** Underlying `RTCPeerConnection`. */
155
+ readonly pc: RTCPeerConnection;
156
+ /** `true` once `.close()` has been called. */
157
+ readonly closed: boolean;
158
+
159
+ constructor(peerId: string, signaling: SignalingClient, options?: PeerOptions);
160
+
161
+ /** Add a local track. Returns the underlying `RTCRtpSender`. */
162
+ addTrack(track: MediaStreamTrack, ...streams: MediaStream[]): RTCRtpSender;
163
+ /** Remove a previously-added sender. */
164
+ removeTrack(sender: RTCRtpSender): void;
165
+ /** Create a data channel on this peer. */
166
+ createDataChannel(label: string, init?: RTCDataChannelInit): RTCDataChannel;
167
+ /** Force an ICE restart - negotiation kicks off via `negotiationneeded`. */
168
+ restartIce(): void;
169
+
170
+ /** Subscribe to a Peer-level event. */
171
+ on(event: PeerEvent, cb: (payload: any) => void): () => void;
172
+ /** Remove a previously registered listener. */
173
+ off(event: PeerEvent, cb: (payload: any) => void): void;
174
+
175
+ /** Close the underlying connection and detach signaling listeners. */
176
+ close(): void;
177
+ }
178
+
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // High-level surface (declared for forward compatibility - implementation
182
+ // lands in subsequent passes; most members throw at runtime today)
183
+ // ---------------------------------------------------------------------------
184
+
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // SDP + ICE helpers (read-only port of the server-side parsers)
188
+ // ---------------------------------------------------------------------------
189
+
190
+ /** Frozen list of valid SDP direction attributes. */
191
+ export const SDP_DIRECTIONS: ReadonlyArray<'sendrecv' | 'sendonly' | 'recvonly' | 'inactive'>;
192
+
193
+ /** Single `a=` line preserved verbatim. */
194
+ export interface SdpAttribute {
195
+ key: string;
196
+ value: string;
197
+ }
198
+
199
+ /** `a=fingerprint:<alg> <value>`. */
200
+ export interface SdpFingerprint {
201
+ algorithm: string;
202
+ value: string;
203
+ }
204
+
205
+ /** Single `a=rtpmap:<pt> <codec>/<rate>[/<channels>]` entry. */
206
+ export interface SdpRtpMap {
207
+ payload: number;
208
+ codec: string;
209
+ clockRate: number;
210
+ channels?: number;
211
+ }
212
+
213
+ /** Parsed SDP `m=` section with the WebRTC-relevant keys lifted to named fields. */
214
+ export interface SdpMedia {
215
+ kind: string;
216
+ port: number;
217
+ proto: string;
218
+ fmts: string[];
219
+ mid?: string;
220
+ iceUfrag?: string;
221
+ icePwd?: string;
222
+ fingerprint?: SdpFingerprint;
223
+ setup?: string;
224
+ direction?: 'sendrecv' | 'sendonly' | 'recvonly' | 'inactive';
225
+ rtcpMux: boolean;
226
+ candidates: string[];
227
+ rtpmaps: SdpRtpMap[];
228
+ attributes: SdpAttribute[];
229
+ }
230
+
231
+ /** Parsed top-level SDP document. */
232
+ export interface ParsedSdp {
233
+ version: number;
234
+ origin: {
235
+ username: string;
236
+ sessionId: string;
237
+ sessionVersion: number;
238
+ netType: string;
239
+ addrType: string;
240
+ address: string;
241
+ } | null;
242
+ sessionName: string;
243
+ attributes: SdpAttribute[];
244
+ media: SdpMedia[];
245
+ }
246
+
247
+ /** Options accepted by `parseSdp`. */
248
+ export interface ParseSdpOptions {
249
+ /** Reject payloads larger than this many bytes. Default `65536`. */
250
+ maxBytes?: number;
251
+ }
252
+
253
+ /** Parse an SDP document into a structured `ParsedSdp`. Throws `SdpError` on bad input. */
254
+ export function parseSdp(text: string, opts?: ParseSdpOptions): ParsedSdp;
255
+
256
+ /** Parse + enforce the constraints the server-side hub validates. Throws `SdpError`. */
257
+ export function validateSdp(text: string): ParsedSdp;
258
+
259
+
260
+ /** Recognized ICE candidate types (RFC 5245). */
261
+ export const CANDIDATE_TYPES: ReadonlyArray<'host' | 'srflx' | 'prflx' | 'relay'>;
262
+
263
+ /** Recognized TCP candidate types (RFC 6544 §4.5). */
264
+ export const TCP_TYPES: ReadonlyArray<'active' | 'passive' | 'so'>;
265
+
266
+ /** Parsed ICE candidate line. */
267
+ export interface IceCandidate {
268
+ foundation: string;
269
+ component: number;
270
+ transport: string;
271
+ priority: number;
272
+ address: string;
273
+ port: number;
274
+ type: 'host' | 'srflx' | 'prflx' | 'relay';
275
+ relatedAddress?: string;
276
+ relatedPort?: number;
277
+ tcpType?: string;
278
+ extensions: Record<string, string>;
279
+ }
280
+
281
+ /** Parse a single `candidate:` line (with or without `a=` prefix). Throws `IceError`. */
282
+ export function parseCandidate(line: string): IceCandidate;
283
+
284
+ /** Serialize a parsed candidate back to its canonical `candidate:...` line. */
285
+ export function stringifyCandidate(c: IceCandidate): string;
286
+
287
+ /** Address classifiers. */
288
+ export function isPrivateIp(addr: string): boolean;
289
+ export function isLoopbackIp(addr: string): boolean;
290
+ export function isLinkLocalIp(addr: string): boolean;
291
+ export function isMdnsHostname(host: string): boolean;
292
+
293
+ /** Policy accepted by `filterCandidates`. */
294
+ export interface CandidateFilterPolicy {
295
+ blockPrivate?: boolean;
296
+ blockLoopback?: boolean;
297
+ blockLinkLocal?: boolean;
298
+ blockMdns?: boolean;
299
+ blockTcp?: boolean;
300
+ allowedTypes?: ReadonlyArray<'host' | 'srflx' | 'prflx' | 'relay'>;
301
+ maxCandidates?: number;
302
+ predicate?: (c: IceCandidate) => boolean;
303
+ }
304
+
305
+ /** Filter a list of candidate lines / parsed objects against a policy. */
306
+ export function filterCandidates<T extends string | IceCandidate>(
307
+ candidates: T[],
308
+ policy?: CandidateFilterPolicy,
309
+ ): T[];
310
+
311
+ /** Options accepted by `webrtc.join()` once it lands. */
312
+ export interface JoinOptions {
313
+ room: string;
314
+ token?: string;
315
+ iceServers?: RTCIceServer[];
316
+ media?: boolean | MediaStreamConstraints;
317
+ e2ee?: { passphrase: string } | { key: CryptoKey };
318
+ polite?: 'auto' | boolean;
319
+ signalingTimeoutMs?: number;
320
+ reconnect?: false | SignalingReconnectOptions;
321
+ }
322
+
323
+ /** Live view of a remote peer in a `Room`. */
324
+ export interface PeerInfo {
325
+ id: string;
326
+ pc: RTCPeerConnection;
327
+ stream: MediaStream;
328
+ audio: boolean;
329
+ video: boolean;
330
+ connection: 'new' | 'checking' | 'connected' | 'disconnected' | 'failed' | 'closed';
331
+ }
332
+
333
+ /** High-level room handle returned by `webrtc.join()` / `useRoom()`. */
334
+ export class Room {
335
+ readonly id: string;
336
+ readonly self: string;
337
+ readonly closed: boolean;
338
+ readonly peers: Signal<Map<string, PeerInfo>>;
339
+ readonly localTracks: Signal<MediaStreamTrack[]>;
340
+ constructor(opts: { id: string; self: string; signaling: SignalingClient; peerOptions?: PeerOptions });
341
+ publish(stream: MediaStream): Promise<void>;
342
+ unpublish(stream: MediaStream): Promise<void>;
343
+ dataChannel(label: string, opts?: RTCDataChannelInit): RoomDataChannel;
344
+ leave(): Promise<void>;
345
+ on(event: 'peer-joined' | 'peer-left' | 'mute' | 'unmute' | 'error', cb: (...args: any[]) => void): () => void;
346
+ off(event: string, cb: (...args: any[]) => void): void;
347
+ }
348
+
349
+ /** Join a room over the given signaling URL. */
350
+ export function join(url: string, opts: JoinOptions): Promise<Room>;
351
+
352
+ /** Resolve a `Room` from either a URL (calls `join`) or an existing `Room`. */
353
+ export function useRoom(urlOrRoom: string | Room, opts?: JoinOptions): Promise<Room>;
354
+
355
+ /** Reactive handle that tracks a remote peer by id. */
356
+ export function usePeer(room: Room, peerId: string): ReactiveHandle<PeerInfo | null>;
357
+
358
+ /** Reactive handle exposing the live track list for a peer. */
359
+ export function useTracks(peer: PeerInfo): ReactiveHandle<MediaStreamTrack[]> & { refresh(): void };
360
+
361
+ /** Reactive multiplexed data channel keyed by `label`. */
362
+ export function useDataChannel(room: Room, label: string, opts?: { history?: number }): {
363
+ messages: ReactiveHandle<DataChannelMessage[]>;
364
+ send(data: any): void;
365
+ close(): void;
366
+ dispose(): void;
367
+ };
368
+
369
+ /** Reactive connection-quality bucket from periodic `getStats()`. */
370
+ export function useConnectionQuality(peer: PeerInfo, opts?: { intervalMs?: number; getStats?: (pc: RTCPeerConnection) => Promise<any> }): ReactiveHandle<'good' | 'fair' | 'poor'>;
371
+
372
+ /** Multiplexed data channel returned by `Room.dataChannel(label)`. */
373
+ export interface RoomDataChannel {
374
+ readonly label: string;
375
+ readonly closed: boolean;
376
+ send(data: any): void;
377
+ on(event: 'message', cb: (data: any, fromPeerId: string) => void): () => void;
378
+ on(event: 'open', cb: (peerId: string) => void): () => void;
379
+ close(): void;
380
+ }
381
+
382
+ /** Disposable reactive handle returned by composables. */
383
+ export interface ReactiveHandle<T> {
384
+ readonly value: T;
385
+ peek(): T;
386
+ subscribe(cb: (value: T) => void): () => void;
387
+ dispose(): void;
388
+ }
389
+
390
+ /** Buffered message yielded by `useDataChannel`. */
391
+ export interface DataChannelMessage {
392
+ data: any;
393
+ from: string;
394
+ at: number;
395
+ }
396
+
397
+ /** TURN credential bundle returned by `webrtc.fetchTurnCredentials()`. */
398
+ export interface TurnCredentials {
399
+ username: string;
400
+ credential: string;
401
+ urls: string[];
402
+ ttl: number;
403
+ }
404
+
405
+ /** Options accepted by `fetchTurnCredentials()` (extends `RequestInit`). */
406
+ export interface FetchTurnOptions extends RequestInit {
407
+ /** Optional `fetch` implementation override (defaults to global `fetch`). */
408
+ fetch?: typeof fetch;
409
+ }
410
+
411
+ /** Fetch a TURN credential bundle from the app's HTTP endpoint. */
412
+ export function fetchTurnCredentials(url: string, opts?: FetchTurnOptions): Promise<TurnCredentials>;
413
+
414
+ /** Merge TURN credentials with an optional base `iceServers` list, deduping URLs. */
415
+ export function mergeIceServers(base?: RTCIceServer[], turn?: { username: string; credential: string; urls: string[] }): RTCIceServer[];
416
+
417
+ /** Options accepted by `createTurnRefresher()`. */
418
+ export interface TurnRefresherOptions {
419
+ /** TURN credential endpoint. */
420
+ url: string;
421
+ /** Optional `fetch` override. */
422
+ fetch?: typeof fetch;
423
+ /** Refresh `leadMs` milliseconds before the credential TTL expires. Default `30000`. */
424
+ leadMs?: number;
425
+ /** Floor on the scheduled refresh interval in ms. Default `5000`. */
426
+ minIntervalMs?: number;
427
+ /** Called after each successful refresh. */
428
+ onRefresh?: (creds: TurnCredentials) => void;
429
+ /** Called when a refresh fails (next refresh is auto-retried). */
430
+ onError?: (err: Error) => void;
431
+ /** Extra `RequestInit` forwarded to `fetch`. */
432
+ requestInit?: RequestInit;
433
+ }
434
+
435
+ /** Handle returned by `createTurnRefresher()`. */
436
+ export interface TurnRefresher {
437
+ /** Last successfully fetched credentials, or `null` until the first refresh. */
438
+ readonly value: TurnCredentials | null;
439
+ peek(): TurnCredentials | null;
440
+ start(): Promise<TurnCredentials | null>;
441
+ refresh(): Promise<TurnCredentials | null>;
442
+ stop(): void;
443
+ }
444
+
445
+ /** Schedule automatic TURN-credential refresh ahead of expiry. */
446
+ export function createTurnRefresher(opts: TurnRefresherOptions): TurnRefresher;
447
+
448
+
449
+ // ───────────────────────── E2EE (SFrame) ─────────────────────────
450
+
451
+ /** Options for `new SFrameContext()`. */
452
+ export interface SFrameContextOptions {
453
+ /** Maximum number of epochs to retain (default `4`). Oldest is evicted on overflow. */
454
+ maxEpochs?: number;
455
+ }
456
+
457
+ /**
458
+ * Holds the AES-GCM-128 key material for one or more SFrame epochs.
459
+ * Each epoch is identified by an unsigned 8-bit integer (0-255).
460
+ */
461
+ export class SFrameContext {
462
+ constructor(opts?: SFrameContextOptions);
463
+ /** Currently-active epoch (advanced by `setKey()`). */
464
+ readonly currentEpoch: number;
465
+ /** Number of tracked epoch → key mappings. */
466
+ readonly epochCount: number;
467
+ /** Install a key for `epoch` and make it the current epoch. */
468
+ setKey(epoch: number, key: CryptoKey): void;
469
+ /** Drop the key for `epoch` (no-op if absent). */
470
+ removeEpoch(epoch: number): void;
471
+ /** Look up the key for `epoch`, or `null` if not tracked. */
472
+ getKey(epoch: number): CryptoKey | null;
473
+ }
474
+
475
+ /**
476
+ * Derive an AES-GCM-128 SFrame key from a shared passphrase + salt
477
+ * (PBKDF2-SHA256 → HKDF-SHA256).
478
+ */
479
+ export function deriveSFrameKey(passphrase: string, salt: string): Promise<CryptoKey>;
480
+ /** Generate a random AES-GCM-128 key suitable for SFrame use. */
481
+ export function generateSFrameKey(): Promise<CryptoKey>;
482
+
483
+ /**
484
+ * Encrypt a single media/data frame with the current epoch's key.
485
+ * Returns `Uint8Array` laid out as `[1-byte epoch][12-byte IV][ciphertext+tag]`.
486
+ */
487
+ export function encryptFrame(ctx: SFrameContext, payload: BufferSource): Promise<Uint8Array>;
488
+ /** Decrypt a frame produced by `encryptFrame()`. */
489
+ export function decryptFrame(ctx: SFrameContext, frame: BufferSource): Promise<Uint8Array>;
490
+
491
+ /** Handle returned by `attachE2ee()`. */
492
+ export interface E2eeHandle {
493
+ /** Re-walk the peer connection's senders + receivers and wire any new ones. */
494
+ refresh(): void;
495
+ /** Best-effort teardown (current senders/receivers continue with their pipes). */
496
+ detach(): void;
497
+ }
498
+
499
+ /**
500
+ * Install SFrame encrypt/decrypt transforms on every sender and receiver
501
+ * of the given `RTCPeerConnection`. Requires `RTCRtpSender.createEncodedStreams()`.
502
+ */
503
+ export function attachE2ee(pc: RTCPeerConnection, ctx: SFrameContext): E2eeHandle;
504
+
505
+ /** Adapter interface returned by `loadSfuAdapter()`. */
506
+ export interface SfuAdapter {
507
+ name: 'mediasoup' | 'livekit';
508
+ /** Underlying adapter-specific device (mediasoup `Device`, etc.). */
509
+ device?: any;
510
+ /** Underlying livekit-client `Room` instance (LiveKit adapter only). */
511
+ room?: any;
512
+ /** Has `load()` completed at least once (mediasoup) / has `connect()` resolved (livekit)? */
513
+ readonly loaded?: boolean;
514
+ readonly connected?: boolean;
515
+ /** Load the device with the SFU router's RTP capabilities (mediasoup). */
516
+ load?(routerRtpCapabilities: any): Promise<void>;
517
+ canProduce?(kind: 'audio' | 'video'): boolean;
518
+ createSendTransport?(params: any): any;
519
+ createRecvTransport?(params: any): any;
520
+ /** Connect to a LiveKit server (livekit adapter only). */
521
+ connect?(url: string, token: string, connectOpts?: any): Promise<void>;
522
+ /** Disconnect from a LiveKit server (livekit adapter only). */
523
+ disconnect?(): Promise<void>;
524
+ /** Higher-level join (signaling-dependent; not yet implemented). */
525
+ join(opts: any): Promise<Room>;
526
+ }
527
+
528
+ /** Load an SFU adapter by name. Peer dependency must be installed. */
529
+ export function loadSfuAdapter(
530
+ name: 'mediasoup' | 'livekit',
531
+ opts?: { client?: any; deviceOptions?: any; roomOptions?: any },
532
+ ): Promise<SfuAdapter>;
533
+
534
+ /** Decoded payload of a server-issued join token (UX-only; the server re-validates). */
535
+ export interface DecodedJoinToken {
536
+ user: { id: string; [k: string]: any } | null;
537
+ room: string | null;
538
+ exp: number | null;
539
+ raw: any;
540
+ }
541
+
542
+ /** UX-only decode of a `signJoinToken(...)` payload. */
543
+ export function decodeJoinToken(token: string): DecodedJoinToken;
544
+
545
+ /** `true` if `decoded.exp` (seconds since epoch) is in the past. */
546
+ export function isJoinTokenExpired(
547
+ decoded: { exp: number | null } | DecodedJoinToken,
548
+ opts?: { nowMs?: number; skewMs?: number },
549
+ ): boolean;
550
+
551
+ /** Reduced `getStats()` snapshot returned by `samplePeerStats()`. */
552
+ export interface PeerStatsSample {
553
+ report: any;
554
+ inboundRtp: any[];
555
+ outboundRtp: any[];
556
+ candidatePair: any | null;
557
+ summary: {
558
+ rttMs: number | null;
559
+ lossPct: number;
560
+ bytesSent: number;
561
+ bytesReceived: number;
562
+ };
563
+ }
564
+
565
+ /** One-shot reduced `getStats()` snapshot. */
566
+ export function samplePeerStats(pc: RTCPeerConnection): Promise<PeerStatsSample>;
567
+
568
+ /** Periodic stats sampler. */
569
+ export function createStatsSampler(
570
+ pc: RTCPeerConnection,
571
+ opts?: {
572
+ intervalMs?: number;
573
+ onSample?: (s: PeerStatsSample) => void;
574
+ onError?: (err: Error) => void;
575
+ immediate?: boolean;
576
+ },
577
+ ): { stop(): void; getLatest(): PeerStatsSample | null };
578
+
579
+ /** Coarse connection-quality bucket from a reduced sample. */
580
+ export function classifyStats(sample: PeerStatsSample | null): 'good' | 'fair' | 'poor' | 'unknown';
581
+
582
+ /** The `$.webrtc` namespace. */
583
+ export interface WebRtcNamespace {
584
+ SignalingClient: typeof SignalingClient;
585
+ Peer: typeof Peer;
586
+ parseSdp: typeof parseSdp;
587
+ validateSdp: typeof validateSdp;
588
+ parseCandidate: typeof parseCandidate;
589
+ stringifyCandidate: typeof stringifyCandidate;
590
+ filterCandidates: typeof filterCandidates;
591
+ isPrivateIp: typeof isPrivateIp;
592
+ isLoopbackIp: typeof isLoopbackIp;
593
+ isLinkLocalIp: typeof isLinkLocalIp;
594
+ isMdnsHostname: typeof isMdnsHostname;
595
+ WebRtcError: typeof WebRtcError;
596
+ SignalingError: typeof SignalingError;
597
+ IceError: typeof IceError;
598
+ SdpError: typeof SdpError;
599
+ TurnError: typeof TurnError;
600
+ E2eeError: typeof E2eeError;
601
+ SfuError: typeof SfuError;
602
+
603
+ /** Join a room over the given signaling URL. */
604
+ join(url: string, opts: JoinOptions): Promise<Room>;
605
+ /** Fetch TURN credentials from the app's HTTP endpoint. */
606
+ fetchTurnCredentials: typeof fetchTurnCredentials;
607
+ /** Merge TURN credentials with a base `iceServers[]`. */
608
+ mergeIceServers: typeof mergeIceServers;
609
+ /** Schedule automatic TURN-credential refresh ahead of expiry. */
610
+ createTurnRefresher: typeof createTurnRefresher;
611
+ /** Derive an AES-GCM-128 SFrame key from a shared passphrase + salt. */
612
+ deriveSFrameKey: typeof deriveSFrameKey;
613
+ /** Generate a random AES-GCM-128 SFrame key. */
614
+ generateSFrameKey: typeof generateSFrameKey;
615
+ /** SFrame epoch / key holder. */
616
+ SFrameContext: typeof SFrameContext;
617
+ /** Encrypt a single frame with the current SFrame epoch's key. */
618
+ encryptFrame: typeof encryptFrame;
619
+ /** Decrypt a frame previously produced by `encryptFrame()`. */
620
+ decryptFrame: typeof decryptFrame;
621
+ /** Install SFrame encrypt/decrypt transforms on a peer connection. */
622
+ attachE2ee: typeof attachE2ee;
623
+ /** Load an optional SFU adapter (peer-dep). */
624
+ loadSfuAdapter: typeof loadSfuAdapter;
625
+ /** UX-only decode of a `signJoinToken(...)` payload (server validates). */
626
+ decodeJoinToken: typeof decodeJoinToken;
627
+ /** `true` if a decoded token's `exp` is in the past. */
628
+ isJoinTokenExpired: typeof isJoinTokenExpired;
629
+ /** One-shot `getStats()` snapshot. */
630
+ samplePeerStats: typeof samplePeerStats;
631
+ /** Periodic `getStats()` sampler. */
632
+ createStatsSampler: typeof createStatsSampler;
633
+ /** Bucket a reduced sample into a connection-quality label. */
634
+ classifyStats: typeof classifyStats;
635
+ /** Resolve a `Room` from either a URL (calls `join`) or an existing `Room`. */
636
+ useRoom(urlOrRoom: string | Room, opts?: JoinOptions): Promise<Room>;
637
+ /** Reactive handle that tracks a remote peer by id. */
638
+ usePeer(room: Room, peerId: string): ReactiveHandle<PeerInfo | null>;
639
+ /** Reactive handle exposing the live track list for a peer. */
640
+ useTracks(peer: PeerInfo): ReactiveHandle<MediaStreamTrack[]> & { refresh(): void };
641
+ /** Reactive multiplexed data channel keyed by `label`. */
642
+ useDataChannel(room: Room, label: string, opts?: { history?: number }): {
643
+ messages: ReactiveHandle<DataChannelMessage[]>;
644
+ send(data: any): void;
645
+ close(): void;
646
+ dispose(): void;
647
+ };
648
+ /** Reactive connection-quality bucket from periodic `getStats()`. */
649
+ useConnectionQuality(peer: PeerInfo, opts?: { intervalMs?: number; getStats?: (pc: RTCPeerConnection) => Promise<any> }): ReactiveHandle<'good' | 'fair' | 'poor'>;
650
+ }
651
+
652
+ /** Live binding for the `webrtc` named export. */
653
+ export const webrtc: WebRtcNamespace;