openrtc 0.1.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 (122) hide show
  1. package/README.md +106 -0
  2. package/dist/OpenRTC.d.ts +71 -0
  3. package/dist/OpenRTC.js +130 -0
  4. package/dist/api/MediaTransport.d.ts +7 -0
  5. package/dist/api/MediaTransport.js +262 -0
  6. package/dist/api/PlutoPeerConnection.d.ts +50 -0
  7. package/dist/api/PlutoPeerConnection.js +434 -0
  8. package/dist/api/PlutoWebSocket.d.ts +24 -0
  9. package/dist/api/PlutoWebSocket.js +113 -0
  10. package/dist/api/PlutoWebTransport.d.ts +29 -0
  11. package/dist/api/PlutoWebTransport.js +105 -0
  12. package/dist/auth/host.d.ts +73 -0
  13. package/dist/auth/host.js +513 -0
  14. package/dist/auth/index.d.ts +1 -0
  15. package/dist/auth/index.js +1 -0
  16. package/dist/auth/internal.d.ts +1 -0
  17. package/dist/auth/internal.js +1 -0
  18. package/dist/compat/index.d.ts +20 -0
  19. package/dist/compat/index.js +20 -0
  20. package/dist/core/Client.d.ts +352 -0
  21. package/dist/core/Client.js +3689 -0
  22. package/dist/core/Connection.d.ts +67 -0
  23. package/dist/core/Connection.js +404 -0
  24. package/dist/core/IEngineBridge.d.ts +18 -0
  25. package/dist/core/IEngineBridge.js +1 -0
  26. package/dist/core/ISignalingTransport.d.ts +13 -0
  27. package/dist/core/ISignalingTransport.js +1 -0
  28. package/dist/core/PeerLifecycleManager.d.ts +43 -0
  29. package/dist/core/PeerLifecycleManager.js +330 -0
  30. package/dist/core/PeerProjectionStore.d.ts +43 -0
  31. package/dist/core/PeerProjectionStore.js +327 -0
  32. package/dist/core/PlutoRTC.d.ts +13 -0
  33. package/dist/core/PlutoRTC.js +26 -0
  34. package/dist/core/Room.d.ts +47 -0
  35. package/dist/core/Room.js +376 -0
  36. package/dist/core/Signaling.d.ts +39 -0
  37. package/dist/core/Signaling.js +147 -0
  38. package/dist/core/bridges/BridgeCodecs.d.ts +19 -0
  39. package/dist/core/bridges/BridgeCodecs.js +317 -0
  40. package/dist/core/bridges/BrowserFirestoreRooms.d.ts +36 -0
  41. package/dist/core/bridges/BrowserFirestoreRooms.js +169 -0
  42. package/dist/core/bridges/BrowserFirestoreSignaling.d.ts +111 -0
  43. package/dist/core/bridges/BrowserFirestoreSignaling.js +1010 -0
  44. package/dist/core/bridges/NativeIpcBridge.d.ts +156 -0
  45. package/dist/core/bridges/NativeIpcBridge.js +1472 -0
  46. package/dist/core/bridges/RtdbPresence.d.ts +122 -0
  47. package/dist/core/bridges/RtdbPresence.js +201 -0
  48. package/dist/core/bridges/TauriBridge.d.ts +8 -0
  49. package/dist/core/bridges/TauriBridge.js +7 -0
  50. package/dist/core/bridges/WasmBridge.d.ts +233 -0
  51. package/dist/core/bridges/WasmBridge.js +1811 -0
  52. package/dist/core/config.d.ts +31 -0
  53. package/dist/core/config.js +138 -0
  54. package/dist/core/transports/ITransport.d.ts +12 -0
  55. package/dist/core/transports/ITransport.js +1 -0
  56. package/dist/core/transports/LocalDaemonBridge.d.ts +41 -0
  57. package/dist/core/transports/LocalDaemonBridge.js +227 -0
  58. package/dist/core/transports/TauriSignaling.d.ts +28 -0
  59. package/dist/core/transports/TauriSignaling.js +373 -0
  60. package/dist/core/transports/TransportFactory.d.ts +9 -0
  61. package/dist/core/transports/TransportFactory.js +59 -0
  62. package/dist/core/transports/WasmSignaling.d.ts +78 -0
  63. package/dist/core/transports/WasmSignaling.js +522 -0
  64. package/dist/core/transports/WebRTCTransport.d.ts +23 -0
  65. package/dist/core/transports/WebRTCTransport.js +132 -0
  66. package/dist/core/trust.d.ts +23 -0
  67. package/dist/core/trust.js +99 -0
  68. package/dist/core/types.d.ts +559 -0
  69. package/dist/core/types.js +19 -0
  70. package/dist/index.d.ts +3 -0
  71. package/dist/index.js +3 -0
  72. package/dist/moq/MoQMediaTransport.d.ts +6 -0
  73. package/dist/moq/MoQMediaTransport.js +106 -0
  74. package/dist/moq/MoQPublisher.d.ts +22 -0
  75. package/dist/moq/MoQPublisher.js +179 -0
  76. package/dist/moq/MoQSession.d.ts +35 -0
  77. package/dist/moq/MoQSession.js +166 -0
  78. package/dist/moq/MoQSubscriber.d.ts +19 -0
  79. package/dist/moq/MoQSubscriber.js +235 -0
  80. package/dist/moq/common.d.ts +47 -0
  81. package/dist/moq/common.js +304 -0
  82. package/dist/moq/messages.d.ts +105 -0
  83. package/dist/moq/messages.js +367 -0
  84. package/dist/presets/plutoHosted.d.ts +13 -0
  85. package/dist/presets/plutoHosted.js +18 -0
  86. package/dist/runtime/DelegatingRuntimeAdapter.d.ts +68 -0
  87. package/dist/runtime/DelegatingRuntimeAdapter.js +250 -0
  88. package/dist/runtime/IpcRuntimeAdapter.d.ts +9 -0
  89. package/dist/runtime/IpcRuntimeAdapter.js +13 -0
  90. package/dist/runtime/RuntimeClient.d.ts +257 -0
  91. package/dist/runtime/RuntimeClient.js +1484 -0
  92. package/dist/runtime/TauriRuntimeAdapter.d.ts +1 -0
  93. package/dist/runtime/TauriRuntimeAdapter.js +1 -0
  94. package/dist/runtime/WasmRuntimeAdapter.d.ts +6 -0
  95. package/dist/runtime/WasmRuntimeAdapter.js +11 -0
  96. package/dist/runtime/factories.d.ts +24 -0
  97. package/dist/runtime/factories.js +66 -0
  98. package/dist/runtime/index.d.ts +6 -0
  99. package/dist/runtime/index.js +5 -0
  100. package/dist/runtime/ipc.d.ts +8 -0
  101. package/dist/runtime/ipc.js +65 -0
  102. package/dist/runtime/ipcContract.d.ts +46 -0
  103. package/dist/runtime/ipcContract.js +1 -0
  104. package/dist/runtime/tauri.d.ts +16 -0
  105. package/dist/runtime/tauri.js +22 -0
  106. package/dist/runtime/types.d.ts +110 -0
  107. package/dist/runtime/types.js +1 -0
  108. package/dist/transports/TauriTransport.d.ts +17 -0
  109. package/dist/transports/TauriTransport.js +49 -0
  110. package/dist/transports/WebTransport.d.ts +23 -0
  111. package/dist/transports/WebTransport.js +71 -0
  112. package/dist/utils/debug.d.ts +4 -0
  113. package/dist/utils/debug.js +33 -0
  114. package/dist/utils/runtime.d.ts +1 -0
  115. package/dist/utils/runtime.js +10 -0
  116. package/package.json +97 -0
  117. package/wasm/README.md +64 -0
  118. package/wasm/openrtc.d.ts +284 -0
  119. package/wasm/openrtc.js +2132 -0
  120. package/wasm/openrtc_bg.wasm +0 -0
  121. package/wasm/openrtc_bg.wasm.d.ts +110 -0
  122. package/wasm/package.json +16 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * RtdbPresence — Phase 1 RTDB-backed device presence.
3
+ *
4
+ * Replaces the 60-second Firestore heartbeat loop with Firebase Realtime
5
+ * Database `onDisconnect` hooks. When a client disconnects (tab close,
6
+ * crash, network loss), the RTDB server automatically writes the offline
7
+ * state — no cleanup polling needed.
8
+ *
9
+ * RTDB paths mirror the Firestore device collection paths:
10
+ *
11
+ * personal/spaceKey → presence/spaces/{namespaceId}/devices/{deviceId}
12
+ * user-scoped → presence/{appTag}/users/{userId}/devices/{deviceId}
13
+ *
14
+ * Ticket version invalidation:
15
+ * Each presence node stores a `ticketVersion` integer. WasmBridge bumps it
16
+ * whenever it writes a new Firestore ticket (network change, reconnect).
17
+ * Subscribers compare the received ticketVersion against their cached value;
18
+ * if it differs they re-fetch the full device doc from Firestore. This means
19
+ * other clients never hold a stale connection ticket for more than one RTDB
20
+ * heartbeat interval (~1 s), eliminating the P2P-failure→Firestore fallback.
21
+ */
22
+ import type { FirebaseApp } from 'firebase/app';
23
+ export interface RtdbPresenceState {
24
+ online: boolean;
25
+ lastSeenAt: object | null;
26
+ ticketVersion: number;
27
+ }
28
+ export interface RtdbDevicePresence {
29
+ online: boolean;
30
+ ticketVersion: number;
31
+ }
32
+ export type RtdbPresenceMap = Record<string, RtdbDevicePresence>;
33
+ export type DiscoveryPresenceScope = {
34
+ kind: 'user-scoped';
35
+ appTag: string;
36
+ userId: string;
37
+ } | {
38
+ kind: 'personal';
39
+ namespaceId: string;
40
+ };
41
+ export interface RtdbMemberPresence {
42
+ online: boolean;
43
+ }
44
+ export type RtdbRoomPresenceMap = Record<string, RtdbMemberPresence>;
45
+ /**
46
+ * Returns the RTDB presence path for a specific device given a discovery scope.
47
+ */
48
+ export declare function rtdbDevicePath(scope: DiscoveryPresenceScope, deviceId: string): string;
49
+ /**
50
+ * Returns the RTDB presence path for all devices in a discovery scope
51
+ * (the parent node that `onValue` subscribes to).
52
+ */
53
+ export declare function rtdbScopePath(scope: DiscoveryPresenceScope): string;
54
+ /**
55
+ * Returns the RTDB path for a single room member's liveness node.
56
+ *
57
+ * Room presence lives under a separate namespace:
58
+ * presence/{appTag}/rooms/{roomId}/members/{memberId}
59
+ *
60
+ * For personal spaces the appTag is "space::{namespaceId}".
61
+ */
62
+ export declare function rtdbRoomMemberPath(appTag: string, roomId: string, memberId: string): string;
63
+ /** Returns the RTDB path for all members in a room (parent of member nodes). */
64
+ export declare function rtdbRoomMembersPath(appTag: string, roomId: string): string;
65
+ export declare class RtdbPresence {
66
+ private readonly db;
67
+ constructor(app: FirebaseApp);
68
+ /**
69
+ * Register this device as online in RTDB and set an onDisconnect handler
70
+ * so the server marks it offline automatically on connection loss.
71
+ *
72
+ * Should be called once per session, after a Firestore ticket has been
73
+ * written. Does not replace the Firestore device doc — that remains the
74
+ * source of truth for the connection ticket and metadata.
75
+ */
76
+ registerDevice(scope: DiscoveryPresenceScope, deviceId: string, ticketVersion?: number): Promise<void>;
77
+ /**
78
+ * Bump the ticketVersion for a device. Call this whenever a new Firestore
79
+ * ticket is written (e.g. on network change / node restart). Subscribers
80
+ * will see the version change and re-fetch the Firestore doc.
81
+ */
82
+ bumpTicketVersion(scope: DiscoveryPresenceScope, deviceId: string, newVersion: number): Promise<void>;
83
+ /**
84
+ * Explicitly mark a device offline (graceful disconnect / sign-out).
85
+ * The onDisconnect handler would eventually fire anyway, but calling this
86
+ * immediately on graceful shutdown gives instant feedback to other clients.
87
+ */
88
+ markOffline(scope: DiscoveryPresenceScope, deviceId: string, ticketVersion?: number): Promise<void>;
89
+ /**
90
+ * Register a room member as online and set an onDisconnect handler.
91
+ * Called immediately after the Firestore member doc is written (join).
92
+ */
93
+ registerRoomMember(appTag: string, roomId: string, memberId: string): Promise<void>;
94
+ /**
95
+ * Explicitly mark a room member offline (graceful leave).
96
+ * The onDisconnect would fire eventually, but calling this immediately
97
+ * gives instant feedback to other members in the room.
98
+ */
99
+ markRoomMemberOffline(appTag: string, roomId: string, memberId: string): Promise<void>;
100
+ /**
101
+ * Subscribe to all member presence states in a room.
102
+ * Returns an unsubscribe function. The callback receives a map of
103
+ * memberId → { online } whenever any member's liveness changes.
104
+ *
105
+ * Consumers (BrowserFirestoreRooms) overlay this onto the Firestore
106
+ * member roster to show which members are currently alive vs. ghost entries.
107
+ */
108
+ onRoomPresenceChange(appTag: string, roomId: string, callback: (presenceMap: RtdbRoomPresenceMap) => void): () => void;
109
+ /**
110
+ * Subscribe to all device presence states in a discovery scope.
111
+ *
112
+ * Returns an unsubscribe function. The callback receives the full map of
113
+ * deviceId → { online, ticketVersion } whenever any device's presence
114
+ * changes. This replaces the `onSnapshot` on the Firestore device
115
+ * collection for the online/offline boolean overlay.
116
+ *
117
+ * The Firestore device collection remains as the source of truth for
118
+ * connection tickets and metadata; RTDB is only used for the live
119
+ * online boolean and ticket invalidation signal.
120
+ */
121
+ onPresenceChange(scope: DiscoveryPresenceScope, callback: (presenceMap: RtdbPresenceMap) => void): () => void;
122
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * RtdbPresence — Phase 1 RTDB-backed device presence.
3
+ *
4
+ * Replaces the 60-second Firestore heartbeat loop with Firebase Realtime
5
+ * Database `onDisconnect` hooks. When a client disconnects (tab close,
6
+ * crash, network loss), the RTDB server automatically writes the offline
7
+ * state — no cleanup polling needed.
8
+ *
9
+ * RTDB paths mirror the Firestore device collection paths:
10
+ *
11
+ * personal/spaceKey → presence/spaces/{namespaceId}/devices/{deviceId}
12
+ * user-scoped → presence/{appTag}/users/{userId}/devices/{deviceId}
13
+ *
14
+ * Ticket version invalidation:
15
+ * Each presence node stores a `ticketVersion` integer. WasmBridge bumps it
16
+ * whenever it writes a new Firestore ticket (network change, reconnect).
17
+ * Subscribers compare the received ticketVersion against their cached value;
18
+ * if it differs they re-fetch the full device doc from Firestore. This means
19
+ * other clients never hold a stale connection ticket for more than one RTDB
20
+ * heartbeat interval (~1 s), eliminating the P2P-failure→Firestore fallback.
21
+ */
22
+ import { getDatabase, ref, set, onValue, onDisconnect, serverTimestamp, } from 'firebase/database';
23
+ /**
24
+ * Returns the RTDB presence path for a specific device given a discovery scope.
25
+ */
26
+ export function rtdbDevicePath(scope, deviceId) {
27
+ switch (scope.kind) {
28
+ case 'user-scoped':
29
+ return `presence/${scope.appTag}/users/${scope.userId}/devices/${deviceId}`;
30
+ case 'personal':
31
+ return `presence/spaces/${scope.namespaceId}/devices/${deviceId}`;
32
+ }
33
+ }
34
+ /**
35
+ * Returns the RTDB presence path for all devices in a discovery scope
36
+ * (the parent node that `onValue` subscribes to).
37
+ */
38
+ export function rtdbScopePath(scope) {
39
+ switch (scope.kind) {
40
+ case 'user-scoped':
41
+ return `presence/${scope.appTag}/users/${scope.userId}/devices`;
42
+ case 'personal':
43
+ return `presence/spaces/${scope.namespaceId}/devices`;
44
+ }
45
+ }
46
+ /**
47
+ * Returns the RTDB path for a single room member's liveness node.
48
+ *
49
+ * Room presence lives under a separate namespace:
50
+ * presence/{appTag}/rooms/{roomId}/members/{memberId}
51
+ *
52
+ * For personal spaces the appTag is "space::{namespaceId}".
53
+ */
54
+ export function rtdbRoomMemberPath(appTag, roomId, memberId) {
55
+ return `presence/${appTag}/rooms/${roomId}/members/${memberId}`;
56
+ }
57
+ /** Returns the RTDB path for all members in a room (parent of member nodes). */
58
+ export function rtdbRoomMembersPath(appTag, roomId) {
59
+ return `presence/${appTag}/rooms/${roomId}/members`;
60
+ }
61
+ export class RtdbPresence {
62
+ constructor(app) {
63
+ this.db = getDatabase(app);
64
+ }
65
+ /**
66
+ * Register this device as online in RTDB and set an onDisconnect handler
67
+ * so the server marks it offline automatically on connection loss.
68
+ *
69
+ * Should be called once per session, after a Firestore ticket has been
70
+ * written. Does not replace the Firestore device doc — that remains the
71
+ * source of truth for the connection ticket and metadata.
72
+ */
73
+ async registerDevice(scope, deviceId, ticketVersion = 0) {
74
+ const deviceRef = ref(this.db, rtdbDevicePath(scope, deviceId));
75
+ // Register server-side cleanup: if the WebSocket drops, RTDB writes
76
+ // this offline state automatically.
77
+ await onDisconnect(deviceRef).set({
78
+ online: false,
79
+ lastSeenAt: serverTimestamp(),
80
+ ticketVersion,
81
+ });
82
+ // Now write online state.
83
+ await set(deviceRef, {
84
+ online: true,
85
+ lastSeenAt: serverTimestamp(),
86
+ ticketVersion,
87
+ });
88
+ }
89
+ /**
90
+ * Bump the ticketVersion for a device. Call this whenever a new Firestore
91
+ * ticket is written (e.g. on network change / node restart). Subscribers
92
+ * will see the version change and re-fetch the Firestore doc.
93
+ */
94
+ async bumpTicketVersion(scope, deviceId, newVersion) {
95
+ const deviceRef = ref(this.db, rtdbDevicePath(scope, deviceId));
96
+ // Update the onDisconnect payload to include the new version too.
97
+ await onDisconnect(deviceRef).set({
98
+ online: false,
99
+ lastSeenAt: serverTimestamp(),
100
+ ticketVersion: newVersion,
101
+ });
102
+ // Partial update — only change the version, keep online: true.
103
+ const versionRef = ref(this.db, `${rtdbDevicePath(scope, deviceId)}/ticketVersion`);
104
+ await set(versionRef, newVersion);
105
+ }
106
+ /**
107
+ * Explicitly mark a device offline (graceful disconnect / sign-out).
108
+ * The onDisconnect handler would eventually fire anyway, but calling this
109
+ * immediately on graceful shutdown gives instant feedback to other clients.
110
+ */
111
+ async markOffline(scope, deviceId, ticketVersion = 0) {
112
+ // Cancel the onDisconnect so we don't write twice.
113
+ const deviceRef = ref(this.db, rtdbDevicePath(scope, deviceId));
114
+ await onDisconnect(deviceRef).cancel();
115
+ await set(deviceRef, {
116
+ online: false,
117
+ lastSeenAt: serverTimestamp(),
118
+ ticketVersion,
119
+ });
120
+ }
121
+ // -------------------------------------------------------------------------
122
+ // Room member presence (Phase 2)
123
+ // -------------------------------------------------------------------------
124
+ /**
125
+ * Register a room member as online and set an onDisconnect handler.
126
+ * Called immediately after the Firestore member doc is written (join).
127
+ */
128
+ async registerRoomMember(appTag, roomId, memberId) {
129
+ const memberRef = ref(this.db, rtdbRoomMemberPath(appTag, roomId, memberId));
130
+ await onDisconnect(memberRef).set({ online: false, lastSeenAt: serverTimestamp() });
131
+ await set(memberRef, { online: true, lastSeenAt: serverTimestamp() });
132
+ }
133
+ /**
134
+ * Explicitly mark a room member offline (graceful leave).
135
+ * The onDisconnect would fire eventually, but calling this immediately
136
+ * gives instant feedback to other members in the room.
137
+ */
138
+ async markRoomMemberOffline(appTag, roomId, memberId) {
139
+ const memberRef = ref(this.db, rtdbRoomMemberPath(appTag, roomId, memberId));
140
+ await onDisconnect(memberRef).cancel();
141
+ await set(memberRef, { online: false, lastSeenAt: serverTimestamp() });
142
+ }
143
+ /**
144
+ * Subscribe to all member presence states in a room.
145
+ * Returns an unsubscribe function. The callback receives a map of
146
+ * memberId → { online } whenever any member's liveness changes.
147
+ *
148
+ * Consumers (BrowserFirestoreRooms) overlay this onto the Firestore
149
+ * member roster to show which members are currently alive vs. ghost entries.
150
+ */
151
+ onRoomPresenceChange(appTag, roomId, callback) {
152
+ const roomRef = ref(this.db, rtdbRoomMembersPath(appTag, roomId));
153
+ const unsubscribe = onValue(roomRef, (snapshot) => {
154
+ const raw = snapshot.val() ?? {};
155
+ const presenceMap = {};
156
+ for (const [memberId, val] of Object.entries(raw)) {
157
+ if (val && typeof val === 'object') {
158
+ presenceMap[memberId] = {
159
+ online: val.online === true,
160
+ };
161
+ }
162
+ }
163
+ callback(presenceMap);
164
+ }, (error) => {
165
+ console.warn('[RtdbPresence] room presence subscription error:', error);
166
+ });
167
+ return unsubscribe;
168
+ }
169
+ /**
170
+ * Subscribe to all device presence states in a discovery scope.
171
+ *
172
+ * Returns an unsubscribe function. The callback receives the full map of
173
+ * deviceId → { online, ticketVersion } whenever any device's presence
174
+ * changes. This replaces the `onSnapshot` on the Firestore device
175
+ * collection for the online/offline boolean overlay.
176
+ *
177
+ * The Firestore device collection remains as the source of truth for
178
+ * connection tickets and metadata; RTDB is only used for the live
179
+ * online boolean and ticket invalidation signal.
180
+ */
181
+ onPresenceChange(scope, callback) {
182
+ const scopeRef = ref(this.db, rtdbScopePath(scope));
183
+ const unsubscribe = onValue(scopeRef, (snapshot) => {
184
+ const raw = snapshot.val() ?? {};
185
+ const presenceMap = {};
186
+ for (const [deviceId, val] of Object.entries(raw)) {
187
+ if (val && typeof val === 'object') {
188
+ const v = val;
189
+ presenceMap[deviceId] = {
190
+ online: v.online === true,
191
+ ticketVersion: typeof v.ticketVersion === 'number' ? v.ticketVersion : 0,
192
+ };
193
+ }
194
+ }
195
+ callback(presenceMap);
196
+ }, (error) => {
197
+ console.warn('[RtdbPresence] presence subscription error:', error);
198
+ });
199
+ return unsubscribe;
200
+ }
201
+ }
@@ -0,0 +1,8 @@
1
+ import { NativeIpcBridge, type NativeIpcBridgeOptions } from './NativeIpcBridge';
2
+ export type TauriBridgeOptions = NativeIpcBridgeOptions;
3
+ /**
4
+ * @deprecated Prefer `NativeIpcBridge` or the first-party `openrtc-tauri` adapter package.
5
+ * This wrapper remains for one compatibility cycle.
6
+ */
7
+ export declare class TauriBridge extends NativeIpcBridge {
8
+ }
@@ -0,0 +1,7 @@
1
+ import { NativeIpcBridge, } from './NativeIpcBridge';
2
+ /**
3
+ * @deprecated Prefer `NativeIpcBridge` or the first-party `openrtc-tauri` adapter package.
4
+ * This wrapper remains for one compatibility cycle.
5
+ */
6
+ export class TauriBridge extends NativeIpcBridge {
7
+ }
@@ -0,0 +1,233 @@
1
+ import { FirebaseApp } from 'firebase/app';
2
+ import { Auth } from 'firebase/auth';
3
+ import { RtdbPresence, type DiscoveryPresenceScope } from './RtdbPresence';
4
+ import { AuthMode, BackendConnectionState, Device, DiscoveryMode, DeviceEvent, ExplicitFileDataSendParams, ISignalingBackend, RuntimeIdentity, SignalingMode, SignalingEnvelope, SignalingSession, SessionEvent } from '../types';
5
+ export interface WasmBridgeOptions {
6
+ apiKey?: string;
7
+ discoveryMode?: DiscoveryMode;
8
+ spaceKey?: string;
9
+ authMode?: AuthMode;
10
+ signalingMode?: SignalingMode;
11
+ projectId?: string;
12
+ storagePrefix?: string;
13
+ nodeIdPersistence?: 'persistent' | 'ephemeral';
14
+ deviceName?: string;
15
+ }
16
+ /**
17
+ * WasmBridge is the browser host wrapper over the Rust/WASM runtime.
18
+ *
19
+ * Allowed:
20
+ * - normalize JS/WASM binding shapes
21
+ * - forward auth, discovery, signaling, and lifecycle calls into Rust
22
+ * - expose typed browser-facing results
23
+ *
24
+ * Not allowed:
25
+ * - become a second lifecycle engine
26
+ * - invent peer identity or connect policy that is not backed by Rust
27
+ * - normalize app-specific payload protocols into runtime behavior
28
+ *
29
+ * Source of truth:
30
+ * - Rust/WASM owns lifecycle, discovery, signaling, and connection health
31
+ * - RuntimeClient owns app-facing orchestration
32
+ */
33
+ export declare class WasmBridge implements ISignalingBackend {
34
+ private static readonly EXPLICIT_FILE_PROTOCOL_TYPE;
35
+ private static readonly EXPLICIT_FILE_CHUNK_SIZE_BYTES;
36
+ private static readonly BROWSER_AUTO_CONNECT_SCAN_DEBOUNCE_MS;
37
+ private static readonly BROWSER_AUTO_CONNECT_RETRY_COOLDOWN_MS;
38
+ private static readonly BROWSER_AUTO_CONNECT_SETTLE_GUARD_MS;
39
+ app: FirebaseApp | null;
40
+ auth: Auth | null;
41
+ readonly appTag: string;
42
+ protected options: {
43
+ appTag: string;
44
+ apiKey?: string;
45
+ discoveryMode?: DiscoveryMode;
46
+ spaceKey?: string;
47
+ authMode: AuthMode;
48
+ signalingMode?: SignalingMode;
49
+ projectId: string;
50
+ storagePrefix?: string;
51
+ nodeIdPersistence?: 'persistent' | 'ephemeral';
52
+ deviceName?: string;
53
+ };
54
+ private authListeners;
55
+ private persistenceReady;
56
+ wasmClient: any;
57
+ private readonly createdAtMs;
58
+ private missingWasmWarnings;
59
+ private readonly wasmReadyWarningDelayMs;
60
+ private browserFirestoreSignaling;
61
+ private lastObservedFirebaseUserId;
62
+ private sessionReaders;
63
+ protected authContext: {
64
+ userId: string;
65
+ token?: string | null;
66
+ refreshToken?: string | null;
67
+ customToken?: string | null;
68
+ tokenProvider?: (forceRefresh?: boolean) => Promise<string | null>;
69
+ expiresAtMs?: number | null;
70
+ } | null;
71
+ private injectedBrowserAuthSessionActive;
72
+ private startedAutoConnectKey;
73
+ private autoConnectStartsInFlight;
74
+ private browserAutoConnectUnsubscribe;
75
+ private readonly browserAutoConnectLastAttemptAt;
76
+ private readonly browserAutoConnectInFlight;
77
+ private readonly browserAutoConnectWaitStartedAt;
78
+ private readonly browserAutoConnectEscalationCount;
79
+ private readonly browserAutoConnectObservedSignatures;
80
+ private readonly browserAutoConnectRecoveryTimers;
81
+ private browserAutoConnectPendingDevices;
82
+ private browserAutoConnectScanTimer;
83
+ private browserPresenceLoopInterval;
84
+ private browserPresenceLoopState;
85
+ private _rtdbPresence;
86
+ private rtdbPresenceScope;
87
+ private rtdbTicketVersion;
88
+ private rtdbPresenceDisabledForSession;
89
+ /** Phase 2: expose the RTDB presence instance so RuntimeClient can wire it into RoomManager. */
90
+ get rtdbPresence(): RtdbPresence | null;
91
+ private hasSyncedAuthToWasmClient;
92
+ private lastWasmAuthUserId;
93
+ private lastWasmAuthToken;
94
+ private lastDeliveredConnectionStates;
95
+ constructor(options: WasmBridgeOptions);
96
+ get tag(): string;
97
+ private static readonly WEB_DEVICE_ID_STORAGE_PREFIX;
98
+ private static readonly EPHEMERAL_DEVICE_ID_TTL_MS;
99
+ private getDeviceIdentityStorageKey;
100
+ private getNodeIdPersistenceMode;
101
+ private isTicketOnlyMode;
102
+ private createRandomDeviceId;
103
+ protected resolveWebLogicalDeviceId(): string;
104
+ private getDefaultBrowserDeviceName;
105
+ private getBrowserFirestoreSignaling;
106
+ private withWebPresenceMetadata;
107
+ setAuthContext(context: {
108
+ userId: string;
109
+ token?: string | null;
110
+ refreshToken?: string | null;
111
+ customToken?: string | null;
112
+ tokenProvider?: (forceRefresh?: boolean) => Promise<string | null>;
113
+ expiresAtMs?: number | null;
114
+ } | null): Promise<void>;
115
+ private ensureBrowserFirestoreAuth;
116
+ private syncWasmAuthToken;
117
+ private toRuntimeIdentity;
118
+ protected getCurrentUserId(): string | null;
119
+ getCurrentUserIdForScope(): string | null;
120
+ getAuthContext(): Readonly<typeof this.authContext> | null;
121
+ protected isPersonalSpaceMode(): boolean;
122
+ getResolvedOptions(): Readonly<typeof this.options>;
123
+ protected usesLegacyBrowserSignalingFallback(): boolean;
124
+ protected getDiscoveryScopeId(): string | null;
125
+ protected matchesTag(device: Pick<Device, 'tag' | 'appTag'>): boolean;
126
+ matchesDiscoveryTag(device: Pick<Device, 'tag' | 'appTag'>): boolean;
127
+ private stopSessionSubscriptions;
128
+ private stopRuntimeAuthScopedLoops;
129
+ private stopBrowserAutoConnect;
130
+ private clearBrowserAutoConnectDeviceState;
131
+ private queueBrowserAutoConnectRecoveryForDevice;
132
+ private triggerBrowserAutoConnectRecovery;
133
+ private browserAutoConnectLifecycleFresh;
134
+ private getRustOwnedAutoConnectGate;
135
+ private browserAutoConnectNonInitiatorGraceMs;
136
+ private queueBrowserAutoConnectGraceExpiryRecheck;
137
+ private shouldAttemptBrowserAutoConnectDial;
138
+ private hasHealthyBrowserAutoConnectSession;
139
+ private browserAutoConnectSignature;
140
+ private refreshBrowserAutoConnectDeviceForDial;
141
+ private scheduleBrowserAutoConnectScan;
142
+ private processBrowserAutoConnectSnapshot;
143
+ private stopBrowserPresenceLoop;
144
+ private disableRtdbPresenceForSession;
145
+ private startLegacyBrowserPresenceLoop;
146
+ /**
147
+ * Phase 1: Register RTDB presence for the current device.
148
+ *
149
+ * Initialises an RtdbPresence instance (lazy, one per WasmBridge), wires
150
+ * it into the BrowserFirestoreSignaling device watcher so RTDB state is
151
+ * used for the online/offline overlay, and registers this device as online
152
+ * with an onDisconnect cleanup hook.
153
+ *
154
+ * Called by startPresenceLoop (and directly from updatePresence when the
155
+ * ticket changes after initial registration).
156
+ */
157
+ registerRtdbPresence(scope: DiscoveryPresenceScope, deviceId: string, ticketVersion?: number): Promise<void>;
158
+ stopAuthScopedActivity(options?: {
159
+ userId?: string | null;
160
+ }): Promise<void>;
161
+ setWasmClient(client: any): void;
162
+ private warnMissingWasmOnce;
163
+ private waitForWasmClient;
164
+ protected normalizeDevice(raw: Record<string, any>, fallbackId?: string): Device;
165
+ normalizeDeviceRecord(raw: Record<string, any>, fallbackId?: string): Device;
166
+ getResolvedWebLogicalDeviceId(): string;
167
+ private normalizeDeviceStatusSnapshot;
168
+ private normalizePeerSessionSnapshot;
169
+ private toBackendConnectionStateFromPeerSession;
170
+ private normalizeConnectionStateSnapshot;
171
+ private normalizeWindowConnectionStateDetail;
172
+ private stabilizeConnectionState;
173
+ private sameConnectionState;
174
+ private deliverConnectionState;
175
+ private enrichBackendConnectionState;
176
+ get currentUser(): RuntimeIdentity | null;
177
+ onAuthChange(callback: (user: RuntimeIdentity | null) => void): () => void;
178
+ checkForSSOToken(): Promise<void>;
179
+ waitForAuth(): Promise<void>;
180
+ signInAnonymously(): Promise<void>;
181
+ signInWithPluto(): Promise<void>;
182
+ signOut(): Promise<void>;
183
+ getTurnCredentials(): Promise<any>;
184
+ updatePresence(localNodeId: string, ticketStr: string, isOnline?: boolean, ttlMs?: number, metadata?: string, deviceNameOverride?: string): Promise<void>;
185
+ setOffline(localNodeId: string): Promise<void>;
186
+ updateDevice(deviceId: string, updates: {
187
+ deviceName?: string;
188
+ capabilities?: Device['capabilities'];
189
+ metadata?: string;
190
+ }): Promise<void>;
191
+ deleteDevice(deviceId: string): Promise<void>;
192
+ cleanupStaleDevices(): Promise<void>;
193
+ sendMessage(targetId: string, payload: string, state?: string, replyPayload?: string): Promise<string>;
194
+ pollMessages(targetId: string): Promise<SignalingEnvelope[]>;
195
+ searchDevices(excludeNodeId?: string): Promise<Device[]>;
196
+ listDevicesWithStatus(): Promise<import('../types').DeviceStatusSnapshot[]>;
197
+ private searchDevicesViaWasm;
198
+ private onDevicesChangeViaWasmPolling;
199
+ getPeerSession(id: string): Promise<import('../types').PeerSessionSnapshot | null>;
200
+ listPeerSessions(): Promise<import('../types').PeerSessionSnapshot[]>;
201
+ waitForSettledPeer(id: string, timeoutMs?: number): Promise<import('../types').PeerSessionSnapshot | null>;
202
+ openPeerBi(id: string, timeoutMs?: number): Promise<import('../types').BackendPeerBiStream>;
203
+ openPeerUni(id: string, timeoutMs?: number): Promise<import('../types').BackendPeerUniStream>;
204
+ resolvePeerConnectionRecords(id: string): Promise<import('../types').ManagedConnectionRecord[]>;
205
+ resolvePeerIdentity(id: string): Promise<import('../types').ResolvedPeerIdentity>;
206
+ onDevicesChange(callback: (devices: Device[]) => void, excludeNodeId?: string): () => void;
207
+ subscribeDevices(userId: string): Promise<ReadableStream<DeviceEvent[]>>;
208
+ startAutoConnect(userId: string, localDeviceId: string): void;
209
+ notifyDisconnectRequested(remoteNodeId: string): void;
210
+ setAutoConnectExcluded(deviceId: string, excluded: boolean): Promise<void>;
211
+ connectToDevice(params: {
212
+ deviceId?: string | null;
213
+ endpointTicket: string;
214
+ }): Promise<import('../types').NativeConnectResult>;
215
+ forceReconnectSnapshot(): void;
216
+ startPresenceLoop(userId: string, localNodeId: string, deviceName: string, ticket: string, metadata?: string): void;
217
+ /**
218
+ * Build the RTDB discovery scope from the current auth context and options.
219
+ * Returns null if scope cannot be determined (e.g. not yet authenticated).
220
+ */
221
+ private buildRtdbScope;
222
+ createSession(session: SignalingSession): Promise<void>;
223
+ updateSession(sessionId: string, updateData: any): Promise<void>;
224
+ subscribeSessions(localDeviceId: string): Promise<ReadableStream<SessionEvent[]>>;
225
+ getLocalDeviceId(): Promise<string | null>;
226
+ isConnected(node_id: string): Promise<boolean>;
227
+ getConnectionStates(): Promise<BackendConnectionState[]>;
228
+ onConnectionStateChange(callback: (state: BackendConnectionState) => void): Promise<() => void> | (() => void);
229
+ sendExplicitFilePath(connectionId: string, filePath: string, transferId?: string): Promise<string>;
230
+ sendExplicitFileData(params: ExplicitFileDataSendParams): Promise<void>;
231
+ getTransferHistory(limit?: number): Promise<Array<[string, unknown]>>;
232
+ deleteTransferJob(jobId: string): Promise<void>;
233
+ }