openrtc 0.1.0 → 0.1.2

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 (130) hide show
  1. package/README.md +1 -2
  2. package/dist/{core/types.d.ts → DelegatingRuntimeAdapter-CW8d78dZ.d.ts} +275 -95
  3. package/dist/IpcRuntimeAdapter-CT-W0Mtp.d.ts +24 -0
  4. package/dist/auth/index.d.ts +75 -1
  5. package/dist/auth/index.js +1 -1
  6. package/dist/auth/internal.d.ts +3 -1
  7. package/dist/auth/internal.js +1 -1
  8. package/dist/chunk-4MH42GUI.js +2 -0
  9. package/dist/chunk-CQAKCS2S.js +4 -0
  10. package/dist/chunk-D54VGRRH.js +1 -0
  11. package/dist/chunk-DIQYFAGL.js +2 -0
  12. package/dist/chunk-PZ25OCVG.js +2 -0
  13. package/dist/chunk-UVASANPG.js +1 -0
  14. package/dist/factories-0TxDPMeu.d.ts +1041 -0
  15. package/dist/index.d.ts +160 -3
  16. package/dist/index.js +1 -3
  17. package/dist/openrtc_bg.wasm +0 -0
  18. package/dist/runtime/WasmRuntimeAdapter.d.ts +5 -3
  19. package/dist/runtime/WasmRuntimeAdapter.js +1 -11
  20. package/dist/runtime/index.d.ts +796 -6
  21. package/dist/runtime/index.js +1 -5
  22. package/dist/runtime/tauri.d.ts +12 -8
  23. package/dist/runtime/tauri.js +1 -22
  24. package/package.json +11 -7
  25. package/wasm/openrtc.d.ts +61 -25
  26. package/wasm/openrtc.js +417 -259
  27. package/wasm/openrtc_bg.wasm +0 -0
  28. package/wasm/openrtc_bg.wasm.d.ts +27 -24
  29. package/wasm/package.json +1 -1
  30. package/dist/OpenRTC.d.ts +0 -71
  31. package/dist/OpenRTC.js +0 -130
  32. package/dist/api/MediaTransport.d.ts +0 -7
  33. package/dist/api/MediaTransport.js +0 -262
  34. package/dist/api/PlutoPeerConnection.d.ts +0 -50
  35. package/dist/api/PlutoPeerConnection.js +0 -434
  36. package/dist/api/PlutoWebSocket.d.ts +0 -24
  37. package/dist/api/PlutoWebSocket.js +0 -113
  38. package/dist/api/PlutoWebTransport.d.ts +0 -29
  39. package/dist/api/PlutoWebTransport.js +0 -105
  40. package/dist/auth/host.d.ts +0 -73
  41. package/dist/auth/host.js +0 -513
  42. package/dist/compat/index.d.ts +0 -20
  43. package/dist/compat/index.js +0 -20
  44. package/dist/core/Client.d.ts +0 -352
  45. package/dist/core/Client.js +0 -3689
  46. package/dist/core/Connection.d.ts +0 -67
  47. package/dist/core/Connection.js +0 -404
  48. package/dist/core/IEngineBridge.d.ts +0 -18
  49. package/dist/core/IEngineBridge.js +0 -1
  50. package/dist/core/ISignalingTransport.d.ts +0 -13
  51. package/dist/core/ISignalingTransport.js +0 -1
  52. package/dist/core/PeerLifecycleManager.d.ts +0 -43
  53. package/dist/core/PeerLifecycleManager.js +0 -330
  54. package/dist/core/PeerProjectionStore.d.ts +0 -43
  55. package/dist/core/PeerProjectionStore.js +0 -327
  56. package/dist/core/PlutoRTC.d.ts +0 -13
  57. package/dist/core/PlutoRTC.js +0 -26
  58. package/dist/core/Room.d.ts +0 -47
  59. package/dist/core/Room.js +0 -376
  60. package/dist/core/Signaling.d.ts +0 -39
  61. package/dist/core/Signaling.js +0 -147
  62. package/dist/core/bridges/BridgeCodecs.d.ts +0 -19
  63. package/dist/core/bridges/BridgeCodecs.js +0 -317
  64. package/dist/core/bridges/BrowserFirestoreRooms.d.ts +0 -36
  65. package/dist/core/bridges/BrowserFirestoreRooms.js +0 -169
  66. package/dist/core/bridges/BrowserFirestoreSignaling.d.ts +0 -111
  67. package/dist/core/bridges/BrowserFirestoreSignaling.js +0 -1010
  68. package/dist/core/bridges/NativeIpcBridge.d.ts +0 -156
  69. package/dist/core/bridges/NativeIpcBridge.js +0 -1472
  70. package/dist/core/bridges/RtdbPresence.d.ts +0 -122
  71. package/dist/core/bridges/RtdbPresence.js +0 -201
  72. package/dist/core/bridges/TauriBridge.d.ts +0 -8
  73. package/dist/core/bridges/TauriBridge.js +0 -7
  74. package/dist/core/bridges/WasmBridge.d.ts +0 -233
  75. package/dist/core/bridges/WasmBridge.js +0 -1811
  76. package/dist/core/config.d.ts +0 -31
  77. package/dist/core/config.js +0 -138
  78. package/dist/core/transports/ITransport.d.ts +0 -12
  79. package/dist/core/transports/ITransport.js +0 -1
  80. package/dist/core/transports/LocalDaemonBridge.d.ts +0 -41
  81. package/dist/core/transports/LocalDaemonBridge.js +0 -227
  82. package/dist/core/transports/TauriSignaling.d.ts +0 -28
  83. package/dist/core/transports/TauriSignaling.js +0 -373
  84. package/dist/core/transports/TransportFactory.d.ts +0 -9
  85. package/dist/core/transports/TransportFactory.js +0 -59
  86. package/dist/core/transports/WasmSignaling.d.ts +0 -78
  87. package/dist/core/transports/WasmSignaling.js +0 -522
  88. package/dist/core/transports/WebRTCTransport.d.ts +0 -23
  89. package/dist/core/transports/WebRTCTransport.js +0 -132
  90. package/dist/core/trust.d.ts +0 -23
  91. package/dist/core/trust.js +0 -99
  92. package/dist/core/types.js +0 -19
  93. package/dist/moq/MoQMediaTransport.d.ts +0 -6
  94. package/dist/moq/MoQMediaTransport.js +0 -106
  95. package/dist/moq/MoQPublisher.d.ts +0 -22
  96. package/dist/moq/MoQPublisher.js +0 -179
  97. package/dist/moq/MoQSession.d.ts +0 -35
  98. package/dist/moq/MoQSession.js +0 -166
  99. package/dist/moq/MoQSubscriber.d.ts +0 -19
  100. package/dist/moq/MoQSubscriber.js +0 -235
  101. package/dist/moq/common.d.ts +0 -47
  102. package/dist/moq/common.js +0 -304
  103. package/dist/moq/messages.d.ts +0 -105
  104. package/dist/moq/messages.js +0 -367
  105. package/dist/presets/plutoHosted.d.ts +0 -13
  106. package/dist/presets/plutoHosted.js +0 -18
  107. package/dist/runtime/DelegatingRuntimeAdapter.d.ts +0 -68
  108. package/dist/runtime/DelegatingRuntimeAdapter.js +0 -250
  109. package/dist/runtime/IpcRuntimeAdapter.d.ts +0 -9
  110. package/dist/runtime/IpcRuntimeAdapter.js +0 -13
  111. package/dist/runtime/RuntimeClient.d.ts +0 -257
  112. package/dist/runtime/RuntimeClient.js +0 -1484
  113. package/dist/runtime/TauriRuntimeAdapter.d.ts +0 -1
  114. package/dist/runtime/TauriRuntimeAdapter.js +0 -1
  115. package/dist/runtime/factories.d.ts +0 -24
  116. package/dist/runtime/factories.js +0 -66
  117. package/dist/runtime/ipc.d.ts +0 -8
  118. package/dist/runtime/ipc.js +0 -65
  119. package/dist/runtime/ipcContract.d.ts +0 -46
  120. package/dist/runtime/ipcContract.js +0 -1
  121. package/dist/runtime/types.d.ts +0 -110
  122. package/dist/runtime/types.js +0 -1
  123. package/dist/transports/TauriTransport.d.ts +0 -17
  124. package/dist/transports/TauriTransport.js +0 -49
  125. package/dist/transports/WebTransport.d.ts +0 -23
  126. package/dist/transports/WebTransport.js +0 -71
  127. package/dist/utils/debug.d.ts +0 -4
  128. package/dist/utils/debug.js +0 -33
  129. package/dist/utils/runtime.d.ts +0 -1
  130. package/dist/utils/runtime.js +0 -10
@@ -1,330 +0,0 @@
1
- function normalizeId(value) {
2
- if (!value)
3
- return null;
4
- const trimmed = value.trim();
5
- return trimmed ? trimmed.toLowerCase() : null;
6
- }
7
- function choosePeerId(input) {
8
- return input.deviceId || input.deviceIdHint || input.peerId || null;
9
- }
10
- function sortedStrings(values) {
11
- return [...values].sort();
12
- }
13
- function samePeerStateIgnoringLastSeen(left, right) {
14
- if (!left) {
15
- return false;
16
- }
17
- return left.peerId === right.peerId
18
- && (left.deviceId ?? null) === (right.deviceId ?? null)
19
- && (left.deviceIdHint ?? null) === (right.deviceIdHint ?? null)
20
- && (left.nodeId ?? null) === (right.nodeId ?? null)
21
- && (left.connectionId ?? null) === (right.connectionId ?? null)
22
- && (left.ticket ?? null) === (right.ticket ?? null)
23
- && !!left.online === !!right.online
24
- && (left.deviceName ?? null) === (right.deviceName ?? null)
25
- && (left.platformType ?? null) === (right.platformType ?? null)
26
- && left.status === right.status
27
- && left.health === right.health
28
- && (left.transportState ?? null) === (right.transportState ?? null)
29
- && (left.protocolState ?? null) === (right.protocolState ?? null)
30
- && !!left.routable === !!right.routable
31
- && (left.error ?? null) === (right.error ?? null)
32
- && JSON.stringify(sortedStrings(left.connectionIds)) === JSON.stringify(sortedStrings(right.connectionIds))
33
- && JSON.stringify(sortedStrings(left.scopes)) === JSON.stringify(sortedStrings(right.scopes));
34
- }
35
- export class PeerLifecycleManager {
36
- constructor() {
37
- this.peers = new Map();
38
- this.aliases = new Map();
39
- this.watchers = new Set();
40
- }
41
- resolveCanonicalPeerId(input) {
42
- const candidates = [
43
- input.deviceId,
44
- input.deviceIdHint,
45
- input.peerId,
46
- // Transport metadata may locate an existing peer, but it must not
47
- // become the canonical public peer identity.
48
- input.connectionId,
49
- ];
50
- for (const candidate of candidates) {
51
- const alias = normalizeId(candidate);
52
- if (!alias)
53
- continue;
54
- const existing = this.aliases.get(alias);
55
- if (existing)
56
- return existing;
57
- }
58
- return choosePeerId(input);
59
- }
60
- setAlias(value, peerId) {
61
- const alias = normalizeId(value);
62
- if (!alias)
63
- return;
64
- this.aliases.set(alias, peerId);
65
- }
66
- snapshot(record) {
67
- return {
68
- peerId: record.peerId,
69
- deviceId: record.deviceId,
70
- deviceIdHint: record.deviceIdHint,
71
- nodeId: record.nodeId,
72
- connectionId: record.connectionId,
73
- connectionIds: Array.from(record.connectionIds.values()),
74
- ticket: record.ticket,
75
- online: record.online,
76
- deviceName: record.deviceName,
77
- platformType: record.platformType,
78
- status: record.status,
79
- health: record.health,
80
- transportState: record.transportState,
81
- protocolState: record.protocolState,
82
- routable: record.routable,
83
- scopes: Array.from(record.scopes.values()),
84
- lastSeenAt: record.lastSeenAt,
85
- error: record.error,
86
- };
87
- }
88
- emit() {
89
- const peers = this.list();
90
- this.watchers.forEach((watcher) => watcher(peers));
91
- }
92
- rekeyPeer(existingPeerId, nextPeerId) {
93
- const existing = this.peers.get(existingPeerId);
94
- if (!existing) {
95
- throw new Error(`Cannot rekey missing peer ${existingPeerId}`);
96
- }
97
- if (existingPeerId === nextPeerId) {
98
- return existing;
99
- }
100
- this.peers.delete(existingPeerId);
101
- existing.peerId = nextPeerId;
102
- this.peers.set(nextPeerId, existing);
103
- for (const [alias, peerId] of this.aliases.entries()) {
104
- if (peerId === existingPeerId) {
105
- this.aliases.set(alias, nextPeerId);
106
- }
107
- }
108
- return existing;
109
- }
110
- upsert(input) {
111
- const canonicalPeerId = this.resolveCanonicalPeerId(input) ?? choosePeerId(input);
112
- if (!canonicalPeerId) {
113
- throw new Error('Peer lifecycle upsert requires a peer identifier.');
114
- }
115
- const preferredPeerId = choosePeerId(input) ?? canonicalPeerId;
116
- let record = this.peers.get(canonicalPeerId);
117
- const existed = !!record;
118
- if (!record) {
119
- record = {
120
- peerId: canonicalPeerId,
121
- connectionIds: new Set(),
122
- status: input.status ?? 'disconnected',
123
- health: input.health ?? 'unknown',
124
- scopes: new Set(),
125
- };
126
- this.peers.set(canonicalPeerId, record);
127
- }
128
- const before = existed ? this.snapshot(record) : undefined;
129
- if (preferredPeerId !== record.peerId) {
130
- const nextPreferred = input.deviceId ||
131
- record.deviceId ||
132
- input.deviceIdHint ||
133
- record.deviceIdHint ||
134
- record.peerId;
135
- if (nextPreferred && nextPreferred !== record.peerId) {
136
- record = this.rekeyPeer(record.peerId, nextPreferred);
137
- }
138
- }
139
- if (input.deviceId)
140
- record.deviceId = input.deviceId;
141
- if (input.deviceIdHint !== undefined)
142
- record.deviceIdHint = input.deviceIdHint;
143
- if (input.nodeId)
144
- record.nodeId = input.nodeId;
145
- if (input.connectionId) {
146
- record.connectionId = input.connectionId;
147
- record.connectionIds.add(input.connectionId);
148
- }
149
- if (input.ticket !== undefined)
150
- record.ticket = input.ticket;
151
- if (input.online !== undefined)
152
- record.online = input.online;
153
- if (input.deviceName !== undefined)
154
- record.deviceName = input.deviceName;
155
- if (input.platformType !== undefined)
156
- record.platformType = input.platformType;
157
- if (input.status) {
158
- const shouldPreserveConnected = record.connectionIds.size > 0 &&
159
- record.status === 'connected' &&
160
- input.status === 'disconnected' &&
161
- !input.connectionId &&
162
- !input.transportState &&
163
- !input.protocolState &&
164
- !input.error;
165
- if (!shouldPreserveConnected) {
166
- record.status = input.status;
167
- }
168
- }
169
- if (input.health)
170
- record.health = input.health;
171
- if (input.transportState !== undefined)
172
- record.transportState = input.transportState;
173
- if (input.protocolState !== undefined)
174
- record.protocolState = input.protocolState;
175
- if (input.routable !== undefined)
176
- record.routable = input.routable;
177
- if (input.error !== undefined)
178
- record.error = input.error;
179
- if (input.lastSeenAt !== undefined)
180
- record.lastSeenAt = input.lastSeenAt;
181
- if (input.scopes) {
182
- for (const scope of input.scopes) {
183
- record.scopes.add(scope);
184
- }
185
- }
186
- this.setAlias(record.peerId, record.peerId);
187
- this.setAlias(record.deviceId, record.peerId);
188
- this.setAlias(record.deviceIdHint, record.peerId);
189
- this.setAlias(record.nodeId, record.peerId);
190
- this.setAlias(record.connectionId, record.peerId);
191
- for (const connectionId of record.connectionIds) {
192
- this.setAlias(connectionId, record.peerId);
193
- }
194
- const snapshot = this.snapshot(record);
195
- if (!samePeerStateIgnoringLastSeen(before, snapshot)) {
196
- this.emit();
197
- }
198
- return snapshot;
199
- }
200
- upsertDevice(device) {
201
- return this.upsert({
202
- deviceId: device.deviceId,
203
- nodeId: device.nodeId,
204
- ticket: device.ticket,
205
- online: device.online,
206
- deviceName: device.deviceName,
207
- platformType: device.platformType,
208
- status: device.online ? undefined : 'disconnected',
209
- health: 'unknown',
210
- lastSeenAt: typeof device.updatedAt === 'number'
211
- ? device.updatedAt
212
- : typeof device.lastSeenAt === 'number'
213
- ? device.lastSeenAt
214
- : Date.now(),
215
- });
216
- }
217
- watch(callback) {
218
- this.watchers.add(callback);
219
- callback(this.list());
220
- return () => {
221
- this.watchers.delete(callback);
222
- };
223
- }
224
- get(id) {
225
- const alias = normalizeId(id);
226
- const peerId = alias ? this.aliases.get(alias) : undefined;
227
- const record = (peerId && this.peers.get(peerId)) || this.peers.get(id);
228
- return record ? this.snapshot(record) : undefined;
229
- }
230
- list() {
231
- return Array.from(this.peers.values())
232
- .map((record) => this.snapshot(record))
233
- .sort((a, b) => {
234
- const aSeen = a.lastSeenAt ?? 0;
235
- const bSeen = b.lastSeenAt ?? 0;
236
- if (aSeen !== bSeen) {
237
- return bSeen - aSeen;
238
- }
239
- return a.peerId.localeCompare(b.peerId);
240
- });
241
- }
242
- listConnected() {
243
- return this.list().filter((peer) => peer.status === 'connected');
244
- }
245
- addScope(id, scope = 'persistent') {
246
- const record = this.resolveRecord(id);
247
- if (!record)
248
- return undefined;
249
- const before = this.snapshot(record);
250
- record.scopes.add(scope);
251
- const snapshot = this.snapshot(record);
252
- if (!samePeerStateIgnoringLastSeen(before, snapshot)) {
253
- this.emit();
254
- }
255
- return snapshot;
256
- }
257
- releaseScope(id, scope) {
258
- const record = this.resolveRecord(id);
259
- if (!record)
260
- return undefined;
261
- const before = this.snapshot(record);
262
- if (scope) {
263
- record.scopes.delete(scope);
264
- }
265
- else {
266
- record.scopes.clear();
267
- }
268
- const snapshot = this.snapshot(record);
269
- if (!samePeerStateIgnoringLastSeen(before, snapshot)) {
270
- this.emit();
271
- }
272
- return snapshot;
273
- }
274
- hasScope(id, scope) {
275
- const record = this.resolveRecord(id);
276
- return record?.scopes.has(scope) ?? false;
277
- }
278
- hasScopes(id) {
279
- const record = this.resolveRecord(id);
280
- return (record?.scopes.size ?? 0) > 0;
281
- }
282
- removeConnection(id) {
283
- const record = this.resolveRecord(id);
284
- if (!record)
285
- return undefined;
286
- const before = this.snapshot(record);
287
- const connectionAlias = normalizeId(id);
288
- if (connectionAlias) {
289
- const directConnectionId = Array.from(record.connectionIds).find((candidate) => normalizeId(candidate) === connectionAlias);
290
- if (directConnectionId) {
291
- record.connectionIds.delete(directConnectionId);
292
- }
293
- }
294
- if (record.connectionId && normalizeId(record.connectionId) === connectionAlias) {
295
- record.connectionId = Array.from(record.connectionIds)[0];
296
- }
297
- if (record.connectionIds.size === 0 && record.status === 'connected') {
298
- record.status = 'closed';
299
- record.routable = false;
300
- record.transportState = 'closed';
301
- record.protocolState = 'closed';
302
- }
303
- const snapshot = this.snapshot(record);
304
- if (!samePeerStateIgnoringLastSeen(before, snapshot)) {
305
- this.emit();
306
- }
307
- return snapshot;
308
- }
309
- updateHealth(id, health, status) {
310
- const record = this.resolveRecord(id);
311
- if (!record)
312
- return undefined;
313
- const before = this.snapshot(record);
314
- record.health = health;
315
- if (status) {
316
- record.status = status;
317
- }
318
- record.lastSeenAt = Date.now();
319
- const snapshot = this.snapshot(record);
320
- if (!samePeerStateIgnoringLastSeen(before, snapshot)) {
321
- this.emit();
322
- }
323
- return snapshot;
324
- }
325
- resolveRecord(id) {
326
- const alias = normalizeId(id);
327
- const peerId = alias ? this.aliases.get(alias) : undefined;
328
- return (peerId && this.peers.get(peerId)) || this.peers.get(id);
329
- }
330
- }
@@ -1,43 +0,0 @@
1
- import type { Device, PeerHealth, PeerLifecycleStatus, PeerScope, PeerState } from './types';
2
- export type PeerProjectionUpsert = {
3
- peerId?: string;
4
- deviceId?: string;
5
- deviceIdHint?: string;
6
- nodeId?: string;
7
- connectionId?: string;
8
- ticket?: string;
9
- online?: boolean;
10
- deviceName?: string;
11
- platformType?: string;
12
- status?: PeerLifecycleStatus;
13
- health?: PeerHealth;
14
- transportState?: string;
15
- protocolState?: string;
16
- routable?: boolean;
17
- promotionEligible?: boolean;
18
- scopes?: Iterable<PeerScope>;
19
- error?: string;
20
- lastSeenAt?: number;
21
- };
22
- export declare class PeerProjectionStore {
23
- private readonly peers;
24
- private readonly aliases;
25
- private readonly watchers;
26
- private resolveCanonicalPeerId;
27
- private setAlias;
28
- private snapshot;
29
- private emit;
30
- private rekeyPeer;
31
- upsert(input: PeerProjectionUpsert): PeerState;
32
- upsertDevice(device: Device): PeerState;
33
- watch(callback: (peers: PeerState[]) => void): () => void;
34
- get(id: string): PeerState | undefined;
35
- list(): PeerState[];
36
- listConnected(): PeerState[];
37
- addScope(id: string, scope?: PeerScope): PeerState | undefined;
38
- releaseScope(id: string, scope?: PeerScope): PeerState | undefined;
39
- hasScopes(id: string): boolean;
40
- removeConnection(id: string): PeerState | undefined;
41
- updateHealth(id: string, health: PeerHealth, status?: PeerLifecycleStatus): PeerState | undefined;
42
- private resolveRecord;
43
- }
@@ -1,327 +0,0 @@
1
- function normalizeId(value) {
2
- if (!value)
3
- return null;
4
- const trimmed = value.trim();
5
- return trimmed ? trimmed.toLowerCase() : null;
6
- }
7
- function choosePeerId(input) {
8
- return (input.deviceId ||
9
- input.deviceIdHint ||
10
- input.nodeId ||
11
- input.peerId ||
12
- input.connectionId ||
13
- null);
14
- }
15
- function sortedStrings(values) {
16
- return [...values].sort();
17
- }
18
- function sameProjectionIgnoringLastSeen(left, right) {
19
- if (!left) {
20
- return false;
21
- }
22
- return left.peerId === right.peerId
23
- && (left.deviceId ?? null) === (right.deviceId ?? null)
24
- && (left.deviceIdHint ?? null) === (right.deviceIdHint ?? null)
25
- && (left.nodeId ?? null) === (right.nodeId ?? null)
26
- && (left.connectionId ?? null) === (right.connectionId ?? null)
27
- && (left.ticket ?? null) === (right.ticket ?? null)
28
- && !!left.online === !!right.online
29
- && (left.deviceName ?? null) === (right.deviceName ?? null)
30
- && (left.platformType ?? null) === (right.platformType ?? null)
31
- && left.status === right.status
32
- && left.health === right.health
33
- && (left.transportState ?? null) === (right.transportState ?? null)
34
- && (left.protocolState ?? null) === (right.protocolState ?? null)
35
- && !!left.routable === !!right.routable
36
- && (left.promotionEligible ?? true) === (right.promotionEligible ?? true)
37
- && (left.error ?? null) === (right.error ?? null)
38
- && JSON.stringify(sortedStrings(left.connectionIds)) === JSON.stringify(sortedStrings(right.connectionIds))
39
- && JSON.stringify(sortedStrings(left.scopes)) === JSON.stringify(sortedStrings(right.scopes));
40
- }
41
- export class PeerProjectionStore {
42
- constructor() {
43
- this.peers = new Map();
44
- this.aliases = new Map();
45
- this.watchers = new Set();
46
- }
47
- resolveCanonicalPeerId(input) {
48
- const candidates = [
49
- input.deviceId,
50
- input.deviceIdHint,
51
- input.nodeId,
52
- input.peerId,
53
- input.connectionId,
54
- ];
55
- for (const candidate of candidates) {
56
- const alias = normalizeId(candidate);
57
- if (!alias)
58
- continue;
59
- const existing = this.aliases.get(alias);
60
- if (existing)
61
- return existing;
62
- }
63
- return choosePeerId(input);
64
- }
65
- setAlias(value, peerId) {
66
- const alias = normalizeId(value);
67
- if (!alias)
68
- return;
69
- this.aliases.set(alias, peerId);
70
- }
71
- snapshot(record) {
72
- return {
73
- peerId: record.peerId,
74
- deviceId: record.deviceId,
75
- deviceIdHint: record.deviceIdHint,
76
- nodeId: record.nodeId,
77
- connectionId: record.connectionId,
78
- connectionIds: Array.from(record.connectionIds.values()),
79
- ticket: record.ticket,
80
- online: record.online,
81
- deviceName: record.deviceName,
82
- platformType: record.platformType,
83
- status: record.status,
84
- health: record.health,
85
- transportState: record.transportState,
86
- protocolState: record.protocolState,
87
- routable: record.routable,
88
- promotionEligible: record.promotionEligible,
89
- scopes: Array.from(record.scopes.values()),
90
- lastSeenAt: record.lastSeenAt,
91
- error: record.error,
92
- };
93
- }
94
- emit() {
95
- const peers = this.list();
96
- this.watchers.forEach((watcher) => watcher(peers));
97
- }
98
- rekeyPeer(existingPeerId, nextPeerId) {
99
- const existing = this.peers.get(existingPeerId);
100
- if (!existing) {
101
- throw new Error(`Cannot rekey missing peer ${existingPeerId}`);
102
- }
103
- if (existingPeerId === nextPeerId) {
104
- return existing;
105
- }
106
- this.peers.delete(existingPeerId);
107
- existing.peerId = nextPeerId;
108
- this.peers.set(nextPeerId, existing);
109
- for (const [alias, peerId] of this.aliases.entries()) {
110
- if (peerId === existingPeerId) {
111
- this.aliases.set(alias, nextPeerId);
112
- }
113
- }
114
- return existing;
115
- }
116
- upsert(input) {
117
- const canonicalPeerId = this.resolveCanonicalPeerId(input) ?? choosePeerId(input);
118
- if (!canonicalPeerId) {
119
- throw new Error('Peer projection upsert requires at least one peer identifier.');
120
- }
121
- const preferredPeerId = choosePeerId(input) ?? canonicalPeerId;
122
- let record = this.peers.get(canonicalPeerId);
123
- const existed = !!record;
124
- if (!record) {
125
- record = {
126
- peerId: canonicalPeerId,
127
- connectionIds: new Set(),
128
- status: input.status ?? 'disconnected',
129
- health: input.health ?? 'unknown',
130
- scopes: new Set(),
131
- };
132
- this.peers.set(canonicalPeerId, record);
133
- }
134
- const before = existed ? this.snapshot(record) : undefined;
135
- if (preferredPeerId !== record.peerId) {
136
- const nextPreferred = input.deviceId ||
137
- record.deviceId ||
138
- input.deviceIdHint ||
139
- record.deviceIdHint ||
140
- input.nodeId ||
141
- record.nodeId ||
142
- record.peerId;
143
- if (nextPreferred && nextPreferred !== record.peerId) {
144
- record = this.rekeyPeer(record.peerId, nextPreferred);
145
- }
146
- }
147
- if (input.deviceId)
148
- record.deviceId = input.deviceId;
149
- if (input.deviceIdHint !== undefined)
150
- record.deviceIdHint = input.deviceIdHint;
151
- if (input.nodeId)
152
- record.nodeId = input.nodeId;
153
- if (input.connectionId) {
154
- record.connectionId = input.connectionId;
155
- record.connectionIds.add(input.connectionId);
156
- }
157
- if (input.ticket !== undefined)
158
- record.ticket = input.ticket;
159
- if (input.online !== undefined)
160
- record.online = input.online;
161
- if (input.deviceName !== undefined)
162
- record.deviceName = input.deviceName;
163
- if (input.platformType !== undefined)
164
- record.platformType = input.platformType;
165
- if (input.status) {
166
- record.status = input.status;
167
- }
168
- if (input.health)
169
- record.health = input.health;
170
- if (input.transportState !== undefined)
171
- record.transportState = input.transportState;
172
- if (input.protocolState !== undefined)
173
- record.protocolState = input.protocolState;
174
- if (input.routable !== undefined)
175
- record.routable = input.routable;
176
- if (input.promotionEligible !== undefined)
177
- record.promotionEligible = input.promotionEligible;
178
- if (input.error !== undefined)
179
- record.error = input.error;
180
- if (input.lastSeenAt !== undefined)
181
- record.lastSeenAt = input.lastSeenAt;
182
- if (input.scopes) {
183
- for (const scope of input.scopes) {
184
- record.scopes.add(scope);
185
- }
186
- }
187
- this.setAlias(record.peerId, record.peerId);
188
- this.setAlias(record.deviceId, record.peerId);
189
- this.setAlias(record.deviceIdHint, record.peerId);
190
- this.setAlias(record.nodeId, record.peerId);
191
- this.setAlias(record.connectionId, record.peerId);
192
- for (const connectionId of record.connectionIds) {
193
- this.setAlias(connectionId, record.peerId);
194
- }
195
- const snapshot = this.snapshot(record);
196
- if (!sameProjectionIgnoringLastSeen(before, snapshot)) {
197
- this.emit();
198
- }
199
- return snapshot;
200
- }
201
- upsertDevice(device) {
202
- return this.upsert({
203
- deviceId: device.deviceId,
204
- nodeId: device.nodeId,
205
- ticket: device.ticket,
206
- online: device.online,
207
- deviceName: device.deviceName,
208
- platformType: device.platformType,
209
- status: device.online ? undefined : 'disconnected',
210
- health: 'unknown',
211
- lastSeenAt: typeof device.updatedAt === 'number'
212
- ? device.updatedAt
213
- : typeof device.lastSeenAt === 'number'
214
- ? device.lastSeenAt
215
- : Date.now(),
216
- });
217
- }
218
- watch(callback) {
219
- this.watchers.add(callback);
220
- callback(this.list());
221
- return () => {
222
- this.watchers.delete(callback);
223
- };
224
- }
225
- get(id) {
226
- const alias = normalizeId(id);
227
- const peerId = alias ? this.aliases.get(alias) : undefined;
228
- const record = (peerId && this.peers.get(peerId)) || this.peers.get(id);
229
- return record ? this.snapshot(record) : undefined;
230
- }
231
- list() {
232
- return Array.from(this.peers.values())
233
- .map((record) => this.snapshot(record))
234
- .sort((a, b) => {
235
- const aSeen = a.lastSeenAt ?? 0;
236
- const bSeen = b.lastSeenAt ?? 0;
237
- if (aSeen !== bSeen) {
238
- return bSeen - aSeen;
239
- }
240
- return a.peerId.localeCompare(b.peerId);
241
- });
242
- }
243
- listConnected() {
244
- return this.list().filter((peer) => peer.status === 'connected');
245
- }
246
- addScope(id, scope = 'persistent') {
247
- const record = this.resolveRecord(id);
248
- if (!record)
249
- return undefined;
250
- const before = this.snapshot(record);
251
- record.scopes.add(scope);
252
- const snapshot = this.snapshot(record);
253
- if (!sameProjectionIgnoringLastSeen(before, snapshot)) {
254
- this.emit();
255
- }
256
- return snapshot;
257
- }
258
- releaseScope(id, scope) {
259
- const record = this.resolveRecord(id);
260
- if (!record)
261
- return undefined;
262
- const before = this.snapshot(record);
263
- if (scope) {
264
- record.scopes.delete(scope);
265
- }
266
- else {
267
- record.scopes.clear();
268
- }
269
- const snapshot = this.snapshot(record);
270
- if (!sameProjectionIgnoringLastSeen(before, snapshot)) {
271
- this.emit();
272
- }
273
- return snapshot;
274
- }
275
- hasScopes(id) {
276
- const record = this.resolveRecord(id);
277
- return (record?.scopes.size ?? 0) > 0;
278
- }
279
- removeConnection(id) {
280
- const record = this.resolveRecord(id);
281
- if (!record)
282
- return undefined;
283
- const before = this.snapshot(record);
284
- const connectionAlias = normalizeId(id);
285
- if (connectionAlias) {
286
- const directConnectionId = Array.from(record.connectionIds).find((candidate) => normalizeId(candidate) === connectionAlias);
287
- if (directConnectionId) {
288
- record.connectionIds.delete(directConnectionId);
289
- }
290
- }
291
- if (record.connectionId && normalizeId(record.connectionId) === connectionAlias) {
292
- record.connectionId = Array.from(record.connectionIds)[0];
293
- }
294
- if (record.connectionIds.size === 0 && record.status === 'connected') {
295
- record.status = 'closed';
296
- record.routable = false;
297
- record.transportState = 'closed';
298
- record.protocolState = 'closed';
299
- }
300
- const snapshot = this.snapshot(record);
301
- if (!sameProjectionIgnoringLastSeen(before, snapshot)) {
302
- this.emit();
303
- }
304
- return snapshot;
305
- }
306
- updateHealth(id, health, status) {
307
- const record = this.resolveRecord(id);
308
- if (!record)
309
- return undefined;
310
- const before = this.snapshot(record);
311
- record.health = health;
312
- if (status) {
313
- record.status = status;
314
- }
315
- record.lastSeenAt = Date.now();
316
- const snapshot = this.snapshot(record);
317
- if (!sameProjectionIgnoringLastSeen(before, snapshot)) {
318
- this.emit();
319
- }
320
- return snapshot;
321
- }
322
- resolveRecord(id) {
323
- const alias = normalizeId(id);
324
- const peerId = alias ? this.aliases.get(alias) : undefined;
325
- return (peerId && this.peers.get(peerId)) || this.peers.get(id);
326
- }
327
- }