@phystack/hub-client 4.5.19-dev → 4.5.20-dev
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.
- package/dist/index.d.ts +22 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +252 -378
- package/dist/index.js.map +1 -1
- package/dist/peripheral-twin.d.ts +34 -0
- package/dist/peripheral-twin.d.ts.map +1 -0
- package/dist/peripheral-twin.js +234 -0
- package/dist/peripheral-twin.js.map +1 -0
- package/dist/services/phyhub-connection.service.d.ts +1 -0
- package/dist/services/phyhub-connection.service.d.ts.map +1 -1
- package/dist/services/phyhub-connection.service.js +17 -0
- package/dist/services/phyhub-connection.service.js.map +1 -1
- package/dist/services/phyhub-direct-connection.service.d.ts +21 -0
- package/dist/services/phyhub-direct-connection.service.d.ts.map +1 -0
- package/dist/services/phyhub-direct-connection.service.js +101 -0
- package/dist/services/phyhub-direct-connection.service.js.map +1 -0
- package/dist/services/webrtc/data-channel-handler.d.ts +45 -0
- package/dist/services/webrtc/data-channel-handler.d.ts.map +1 -0
- package/dist/services/webrtc/data-channel-handler.js +260 -0
- package/dist/services/webrtc/data-channel-handler.js.map +1 -0
- package/dist/services/webrtc/index.d.ts +8 -0
- package/dist/services/webrtc/index.d.ts.map +1 -0
- package/dist/services/webrtc/index.js +18 -0
- package/dist/services/webrtc/index.js.map +1 -0
- package/dist/services/webrtc/media-stream-handler.d.ts +57 -0
- package/dist/services/webrtc/media-stream-handler.d.ts.map +1 -0
- package/dist/services/webrtc/media-stream-handler.js +383 -0
- package/dist/services/webrtc/media-stream-handler.js.map +1 -0
- package/dist/services/webrtc/peer-connection-manager.d.ts +40 -0
- package/dist/services/webrtc/peer-connection-manager.d.ts.map +1 -0
- package/dist/services/webrtc/peer-connection-manager.js +336 -0
- package/dist/services/webrtc/peer-connection-manager.js.map +1 -0
- package/dist/services/webrtc/types.d.ts +134 -0
- package/dist/services/webrtc/types.d.ts.map +1 -0
- package/dist/services/webrtc/types.js +12 -0
- package/dist/services/webrtc/types.js.map +1 -0
- package/dist/services/webrtc/webrtc-globals.d.ts +4 -0
- package/dist/services/webrtc/webrtc-globals.d.ts.map +1 -0
- package/dist/services/webrtc/webrtc-globals.js +72 -0
- package/dist/services/webrtc/webrtc-globals.js.map +1 -0
- package/dist/services/webrtc/webrtc-manager.d.ts +35 -0
- package/dist/services/webrtc/webrtc-manager.d.ts.map +1 -0
- package/dist/services/webrtc/webrtc-manager.js +274 -0
- package/dist/services/webrtc/webrtc-manager.js.map +1 -0
- package/dist/test/communication-comprehensive-test.d.ts +8 -0
- package/dist/test/communication-comprehensive-test.d.ts.map +1 -0
- package/dist/test/communication-comprehensive-test.js +356 -0
- package/dist/test/communication-comprehensive-test.js.map +1 -0
- package/dist/test/webrtc-channel-names-test.d.ts +2 -0
- package/dist/test/webrtc-channel-names-test.d.ts.map +1 -0
- package/dist/test/webrtc-channel-names-test.js +177 -0
- package/dist/test/webrtc-channel-names-test.js.map +1 -0
- package/dist/test/webrtc-comprehensive-test.d.ts +2 -0
- package/dist/test/webrtc-comprehensive-test.d.ts.map +1 -0
- package/dist/test/webrtc-comprehensive-test.js +328 -0
- package/dist/test/webrtc-comprehensive-test.js.map +1 -0
- package/dist/test/webrtc-reconnect-test.d.ts +4 -0
- package/dist/test/webrtc-reconnect-test.d.ts.map +1 -0
- package/dist/test/webrtc-reconnect-test.js +244 -0
- package/dist/test/webrtc-reconnect-test.js.map +1 -0
- package/dist/test/webrtc-test-harness.d.ts +4 -0
- package/dist/test/webrtc-test-harness.d.ts.map +1 -0
- package/dist/test/webrtc-test-harness.js +169 -0
- package/dist/test/webrtc-test-harness.js.map +1 -0
- package/dist/twin-messaging.d.ts +20 -0
- package/dist/twin-messaging.d.ts.map +1 -0
- package/dist/twin-messaging.js +94 -0
- package/dist/twin-messaging.js.map +1 -0
- package/dist/twin-registry.d.ts +9 -0
- package/dist/twin-registry.d.ts.map +1 -0
- package/dist/twin-registry.js +26 -0
- package/dist/twin-registry.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +20 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/twin.types.d.ts +62 -14
- package/dist/types/twin.types.d.ts.map +1 -1
- package/dist/types/twin.types.js +8 -1
- package/dist/types/twin.types.js.map +1 -1
- package/docs/webrtc-howto.md +398 -0
- package/docs/webrtc-test.md +330 -0
- package/package.json +3 -3
- package/scripts/webrtc-test.sh +401 -0
- package/src/index.ts +378 -568
- package/src/peripheral-twin.ts +337 -0
- package/src/services/phyhub-connection.service.ts +24 -0
- package/src/services/phyhub-direct-connection.service.ts +159 -0
- package/src/services/webrtc/data-channel-handler.ts +362 -0
- package/src/services/webrtc/index.ts +36 -0
- package/src/services/webrtc/media-stream-handler.ts +536 -0
- package/src/services/webrtc/peer-connection-manager.ts +467 -0
- package/src/services/webrtc/types.ts +273 -0
- package/src/services/webrtc/webrtc-globals.ts +108 -0
- package/src/services/webrtc/webrtc-manager.ts +490 -0
- package/src/test/communication-comprehensive-test.ts +533 -0
- package/src/test/webrtc-channel-names-test.ts +266 -0
- package/src/test/webrtc-comprehensive-test.ts +494 -0
- package/src/test/webrtc-reconnect-test.ts +345 -0
- package/src/test/webrtc-test-harness.ts +254 -0
- package/src/twin-messaging.ts +184 -0
- package/src/twin-registry.ts +39 -0
- package/src/types/index.ts +3 -0
- package/src/types/twin.types.ts +80 -14
- package/dist/services/webrtc/datachannel.d.ts +0 -10
- package/dist/services/webrtc/datachannel.d.ts.map +0 -1
- package/dist/services/webrtc/datachannel.js +0 -290
- package/dist/services/webrtc/datachannel.js.map +0 -1
- package/dist/services/webrtc/mediastream.d.ts +0 -10
- package/dist/services/webrtc/mediastream.d.ts.map +0 -1
- package/dist/services/webrtc/mediastream.js +0 -396
- package/dist/services/webrtc/mediastream.js.map +0 -1
- package/dist/services/webrtc/peer-connection-ice.d.ts +0 -32
- package/dist/services/webrtc/peer-connection-ice.d.ts.map +0 -1
- package/dist/services/webrtc/peer-connection-ice.js +0 -483
- package/dist/services/webrtc/peer-connection-ice.js.map +0 -1
- package/src/services/webrtc/datachannel.ts +0 -421
- package/src/services/webrtc/mediastream.ts +0 -602
- package/src/services/webrtc/peer-connection-ice.ts +0 -689
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Twin Messaging Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates emit/on/to methods for any twin type.
|
|
5
|
+
* Provides a consistent messaging API across all instance types.
|
|
6
|
+
*
|
|
7
|
+
* API Design:
|
|
8
|
+
* - emit(type, payload): Fire-and-forget broadcast to subscribers
|
|
9
|
+
* - to(targetId).emit(type, payload): Fire-and-forget to specific twin
|
|
10
|
+
* - to(targetId).emit(type, payload, callback): Request-response to specific twin
|
|
11
|
+
* - on(type, callback): Listen for messages, with optional respond callback
|
|
12
|
+
*
|
|
13
|
+
* Backwards Compatibility:
|
|
14
|
+
* - Message format: `${typePrefix}:${eventType}` is unchanged from pre-PR 144
|
|
15
|
+
* - Old callback signature (data) => {...} works - the respond parameter is optional
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { TwinMessageResult, TwinMessageResultStatus } from './types/twin.types';
|
|
19
|
+
|
|
20
|
+
export interface TwinMessagingContext {
|
|
21
|
+
/** Function to send events via the hub */
|
|
22
|
+
sendEvent: (targetTwinId: string, payload: any) => void;
|
|
23
|
+
/** Function to register message listeners */
|
|
24
|
+
onTwinMessage: (twinId: string, callback: (payload: any) => void) => void;
|
|
25
|
+
/** The twin ID for this instance */
|
|
26
|
+
twinId: string;
|
|
27
|
+
/** Source device ID for message metadata */
|
|
28
|
+
deviceId: string;
|
|
29
|
+
/** Type prefix for message routing (e.g., 'edgeInstance', 'peripheralInstance') */
|
|
30
|
+
typePrefix: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TwinMessagingMethods {
|
|
34
|
+
/** Send a fire-and-forget event to subscribers */
|
|
35
|
+
emit: (type: string, payload: any) => void;
|
|
36
|
+
/** Listen for events, with optional respond callback for request-response */
|
|
37
|
+
on: (type: string, callback: (message: any, respond?: (result: TwinMessageResult) => void) => void) => void;
|
|
38
|
+
/** Target a specific twin for messaging */
|
|
39
|
+
to: (targetTwinId: string) => {
|
|
40
|
+
/** Send event or action to the targeted twin */
|
|
41
|
+
emit: {
|
|
42
|
+
(type: string, payload: any): void;
|
|
43
|
+
(type: string, payload: any, callback: (response: TwinMessageResult) => void): Promise<TwinMessageResult>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const REQUEST_TIMEOUT = 10000;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates messaging methods (emit, on, to) for a twin instance.
|
|
52
|
+
* This provides a consistent API across all instance types (Edge, Screen, Peripheral, etc.)
|
|
53
|
+
*/
|
|
54
|
+
export function createTwinMessaging(context: TwinMessagingContext): TwinMessagingMethods {
|
|
55
|
+
const { sendEvent, onTwinMessage, twinId, deviceId, typePrefix } = context;
|
|
56
|
+
|
|
57
|
+
// Registry for pending requests - enables request-response pattern
|
|
58
|
+
const pendingRequests = new Map<string, {
|
|
59
|
+
resolve: (value: TwinMessageResult) => void;
|
|
60
|
+
reject: (reason: TwinMessageResult) => void;
|
|
61
|
+
timeoutId: ReturnType<typeof setTimeout>;
|
|
62
|
+
}>();
|
|
63
|
+
|
|
64
|
+
const generateRequestId = () => `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
65
|
+
|
|
66
|
+
// Build message payload with metadata
|
|
67
|
+
const buildMessage = (type: string, data: any) => ({
|
|
68
|
+
type: `${typePrefix}:${type}`,
|
|
69
|
+
sourceTwinId: twinId,
|
|
70
|
+
sourceDeviceId: deviceId,
|
|
71
|
+
data,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Send fire-and-forget event
|
|
75
|
+
const sendMessage = (targetTwinId: string, type: string, payload: any): void => {
|
|
76
|
+
sendEvent(targetTwinId, buildMessage(type, payload));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Send request and wait for response (used by to().emit() with callback)
|
|
80
|
+
const sendRequest = (
|
|
81
|
+
targetTwinId: string,
|
|
82
|
+
type: string,
|
|
83
|
+
payload: any,
|
|
84
|
+
callback: (response: TwinMessageResult) => void
|
|
85
|
+
): Promise<TwinMessageResult> => {
|
|
86
|
+
const requestId = generateRequestId();
|
|
87
|
+
|
|
88
|
+
return new Promise<TwinMessageResult>((resolve, reject) => {
|
|
89
|
+
const timeoutId = setTimeout(() => {
|
|
90
|
+
pendingRequests.delete(requestId);
|
|
91
|
+
const error: TwinMessageResult = {
|
|
92
|
+
status: TwinMessageResultStatus.Error,
|
|
93
|
+
message: `Request timed out after ${REQUEST_TIMEOUT}ms`,
|
|
94
|
+
};
|
|
95
|
+
reject(error);
|
|
96
|
+
}, REQUEST_TIMEOUT);
|
|
97
|
+
|
|
98
|
+
pendingRequests.set(requestId, { resolve, reject, timeoutId });
|
|
99
|
+
|
|
100
|
+
// Send with requestId embedded
|
|
101
|
+
sendEvent(targetTwinId, buildMessage(type, { ...payload, requestId }));
|
|
102
|
+
}).then(result => {
|
|
103
|
+
callback(result);
|
|
104
|
+
return result;
|
|
105
|
+
}).catch(error => {
|
|
106
|
+
callback(error);
|
|
107
|
+
throw error;
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Set up response listener - handles all incoming responses for this twin
|
|
112
|
+
onTwinMessage(twinId, (payload: any) => {
|
|
113
|
+
const messageType = payload.data?.type || payload.type;
|
|
114
|
+
if (messageType === `${typePrefix}:response`) {
|
|
115
|
+
const responseData = payload.data?.data || payload.data;
|
|
116
|
+
const requestId = responseData?.requestId;
|
|
117
|
+
const pending = pendingRequests.get(requestId);
|
|
118
|
+
if (pending) {
|
|
119
|
+
clearTimeout(pending.timeoutId);
|
|
120
|
+
pendingRequests.delete(requestId);
|
|
121
|
+
// Extract result without requestId
|
|
122
|
+
const { requestId: _, ...result } = responseData;
|
|
123
|
+
pending.resolve(result as TwinMessageResult);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// emit: Fire-and-forget broadcast to subscribers
|
|
129
|
+
const emit = (type: string, payload: any): void => {
|
|
130
|
+
sendMessage(twinId, type, payload);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// on: Listen for events, with respond callback for request-response
|
|
134
|
+
const on = (
|
|
135
|
+
type: string,
|
|
136
|
+
callback: (message: any, respond?: (result: TwinMessageResult) => void) => void
|
|
137
|
+
) => {
|
|
138
|
+
onTwinMessage(twinId, (payload: any) => {
|
|
139
|
+
const messageType = payload.data?.type || payload.type;
|
|
140
|
+
if (messageType === `${typePrefix}:${type}`) {
|
|
141
|
+
const messageData = payload.data?.data || payload.data;
|
|
142
|
+
const requestId = messageData?.requestId;
|
|
143
|
+
const sourceTwinId = payload.sourceTwinId;
|
|
144
|
+
|
|
145
|
+
// Create respond callback if this is a request (has requestId and source)
|
|
146
|
+
const respond = (requestId && sourceTwinId)
|
|
147
|
+
? (result: TwinMessageResult) => {
|
|
148
|
+
sendEvent(sourceTwinId, buildMessage('response', { ...result, requestId }));
|
|
149
|
+
}
|
|
150
|
+
: undefined;
|
|
151
|
+
|
|
152
|
+
// Pass message without requestId (strip it from the payload)
|
|
153
|
+
// Preserve null/undefined if original messageData was falsy
|
|
154
|
+
if (!messageData) {
|
|
155
|
+
callback(messageData, respond);
|
|
156
|
+
} else {
|
|
157
|
+
const { requestId: _, ...cleanData } = messageData;
|
|
158
|
+
callback(cleanData, respond);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// to: Target a specific twin for messaging
|
|
165
|
+
const to = (targetTwinId: string) => {
|
|
166
|
+
function emit(type: string, payload: any): void;
|
|
167
|
+
function emit(type: string, payload: any, callback: (response: TwinMessageResult) => void): Promise<TwinMessageResult>;
|
|
168
|
+
function emit(
|
|
169
|
+
type: string,
|
|
170
|
+
payload: any,
|
|
171
|
+
callback?: (response: TwinMessageResult) => void
|
|
172
|
+
): void | Promise<TwinMessageResult> {
|
|
173
|
+
if (callback) {
|
|
174
|
+
return sendRequest(targetTwinId, type, payload, callback);
|
|
175
|
+
} else {
|
|
176
|
+
sendMessage(targetTwinId, type, payload);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { emit };
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
return { emit, on, to };
|
|
184
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { PhyHubClient } from ".";
|
|
2
|
+
import { PeripheralTwinInstance } from "./peripheral-twin";
|
|
3
|
+
import { IPeripheralTwinInstance } from "./types/twin.types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* registry to create, store and retrieve instances of different types of twins e.g. Device, Screen, Peripheral
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export class TwinRegistry {
|
|
10
|
+
private peripheralInstances: Map<string, IPeripheralTwinInstance> = new Map();
|
|
11
|
+
|
|
12
|
+
constructor(private phyHubClient: PhyHubClient) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// create an instance of PeripheralTwinInstance class
|
|
16
|
+
async getPeripheralInstance(twinId: string) : Promise<IPeripheralTwinInstance | null> {
|
|
17
|
+
try {
|
|
18
|
+
|
|
19
|
+
// check whether we've already created instance for this twinId
|
|
20
|
+
if (this.peripheralInstances.has(twinId)) {
|
|
21
|
+
return this.peripheralInstances.get(twinId)!;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// create an object of PeripheralTwinInstance class
|
|
25
|
+
const peripheralTwinInstance = new PeripheralTwinInstance(this.phyHubClient, twinId);
|
|
26
|
+
|
|
27
|
+
// class object is created, but perform sanity checks such as whether PhyHubClient is valid, twinId is valid etc.
|
|
28
|
+
// to make sure we are able to correct and return a valid instance
|
|
29
|
+
await peripheralTwinInstance.initialize();
|
|
30
|
+
|
|
31
|
+
// set the newly created instance in the map for future use
|
|
32
|
+
this.peripheralInstances.set(twinId, peripheralTwinInstance);
|
|
33
|
+
|
|
34
|
+
return peripheralTwinInstance;
|
|
35
|
+
} catch(error) {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/types/twin.types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PhyHubClient } from '..';
|
|
2
|
+
import { PhygridDataChannel, PhygridMediaStream } from '../services/webrtc/types';
|
|
2
3
|
|
|
3
4
|
export enum TwinTypeEnum {
|
|
4
5
|
Device = 'Device',
|
|
@@ -626,30 +627,50 @@ export interface PeripheralTwinResponse extends TwinResponseBase {
|
|
|
626
627
|
}
|
|
627
628
|
|
|
628
629
|
export interface PeripheralInstance extends PeripheralTwinResponse {
|
|
629
|
-
|
|
630
|
-
|
|
630
|
+
/**
|
|
631
|
+
* Send a message to this peripheral.
|
|
632
|
+
* - Without callback: Fire-and-forget (returns void)
|
|
633
|
+
* - With callback: Request-response pattern (returns Promise)
|
|
634
|
+
*/
|
|
635
|
+
emit: {
|
|
636
|
+
(type: string, payload: any): void;
|
|
637
|
+
(type: string, payload: any, callback: (response: TwinMessageResult) => void): Promise<TwinMessageResult>;
|
|
638
|
+
};
|
|
639
|
+
/** Listen for events/actions of a given type */
|
|
640
|
+
on: (type: string, callback: (message: any, respond?: (result: TwinMessageResult) => void) => void) => void;
|
|
641
|
+
/** Target a different twin for messaging */
|
|
631
642
|
to: (targetTwinId: string) => {
|
|
632
|
-
emit:
|
|
643
|
+
emit: {
|
|
644
|
+
(type: string, payload: any): void;
|
|
645
|
+
(type: string, payload: any, callback: (response: TwinMessageResult) => void): Promise<TwinMessageResult>;
|
|
646
|
+
};
|
|
633
647
|
};
|
|
634
648
|
updateReported: (properties: Record<string, any>) => Promise<TwinResponse>;
|
|
635
649
|
updateDesired: (properties: Record<string, any>) => Promise<TwinResponse>;
|
|
636
650
|
onUpdateReported: (callback: (reportedProperties: Record<string, any>) => void) => void;
|
|
637
651
|
onUpdateDesired: (callback: (desiredProperties: Record<string, any>) => void) => void;
|
|
638
652
|
remove: () => Promise<TwinResponse | void>;
|
|
639
|
-
getDataChannel: () => Promise<PhygridDataChannel>;
|
|
640
|
-
onDataChannel: (callback: (dc: PhygridDataChannel) => void) => void;
|
|
641
|
-
getMediaStream: () => Promise<{
|
|
653
|
+
getDataChannel: (channelName?: string) => Promise<PhygridDataChannel>;
|
|
654
|
+
onDataChannel: (callback: (dc: PhygridDataChannel) => void, channelName?: string) => void;
|
|
655
|
+
getMediaStream: (channelName?: string) => Promise<{
|
|
642
656
|
stream: PhygridMediaStream;
|
|
643
657
|
close: () => void;
|
|
644
658
|
}>;
|
|
645
|
-
onMediaStream: (callback: (stream: PhygridMediaStream) => void) => Promise<void>;
|
|
659
|
+
onMediaStream: (callback: (stream: PhygridMediaStream) => void, channelName?: string) => Promise<void>;
|
|
646
660
|
}
|
|
647
661
|
|
|
648
662
|
export interface Instance {
|
|
663
|
+
/** Send a fire-and-forget event to subscribers (broadcast) */
|
|
649
664
|
emit: (type: string, payload: any) => void;
|
|
650
|
-
|
|
665
|
+
/** Listen for events/actions of a given type */
|
|
666
|
+
on: (type: string, callback: (message: any, respond?: (result: TwinMessageResult) => void) => void) => void;
|
|
667
|
+
/** Target a specific twin for messaging */
|
|
651
668
|
to: (twinId: string) => {
|
|
652
|
-
|
|
669
|
+
/** Send event or action to the targeted twin */
|
|
670
|
+
emit: {
|
|
671
|
+
(type: string, payload: any): void;
|
|
672
|
+
(type: string, payload: any, callback: (response: TwinMessageResult) => void): Promise<TwinMessageResult>;
|
|
673
|
+
};
|
|
653
674
|
};
|
|
654
675
|
createPeripheralTwin: (
|
|
655
676
|
peripheralName: string,
|
|
@@ -658,11 +679,11 @@ export interface Instance {
|
|
|
658
679
|
descriptors?: Record<string, string>
|
|
659
680
|
) => Promise<TwinResponse>;
|
|
660
681
|
getPeripheralTwins: (twinId?: string) => Promise<PeripheralTwinResponse[]>;
|
|
661
|
-
getDataChannel: () => Promise<PhygridDataChannel>;
|
|
662
|
-
onDataChannel: (callback: (dc: PhygridDataChannel) => void) => void;
|
|
682
|
+
getDataChannel: (channelName?: string) => Promise<PhygridDataChannel>;
|
|
683
|
+
onDataChannel: (callback: (dc: PhygridDataChannel) => void, channelName?: string) => void;
|
|
663
684
|
updateReported: (properties: Record<string, any>) => Promise<TwinResponse>;
|
|
664
|
-
getMediaStream: () => Promise<{ stream: PhygridMediaStream; close: () => void }>;
|
|
665
|
-
onMediaStream: (callback: (stream: PhygridMediaStream) => void) => Promise<void>;
|
|
685
|
+
getMediaStream: (channelName?: string) => Promise<{ stream: PhygridMediaStream; close: () => void }>;
|
|
686
|
+
onMediaStream: (callback: (stream: PhygridMediaStream) => void, channelName?: string) => Promise<void>;
|
|
666
687
|
[key: string]: any; // Allow any additional properties
|
|
667
688
|
}
|
|
668
689
|
|
|
@@ -801,3 +822,48 @@ export interface MediaOptions {
|
|
|
801
822
|
isMediaConnection?: boolean;
|
|
802
823
|
mediaDirection?: string;
|
|
803
824
|
}
|
|
825
|
+
|
|
826
|
+
export enum TwinMessageResultStatus {
|
|
827
|
+
Success = 'success',
|
|
828
|
+
Error = 'error',
|
|
829
|
+
Warning = 'warning',
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
export interface TwinMessageResult {
|
|
833
|
+
status: TwinMessageResultStatus;
|
|
834
|
+
message: string;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
export interface IPeripheralTwinInstance {
|
|
838
|
+
phyHubClient: PhyHubClient;
|
|
839
|
+
twinId: string;
|
|
840
|
+
edgeInstance: Instance | null;
|
|
841
|
+
peripheralTwinResponse: TwinResponse | null;
|
|
842
|
+
/**
|
|
843
|
+
* Send a message to this peripheral.
|
|
844
|
+
* - Without callback: Fire-and-forget (returns void)
|
|
845
|
+
* - With callback: Request-response pattern (returns Promise)
|
|
846
|
+
*/
|
|
847
|
+
emit: {
|
|
848
|
+
(eventType: string, payload: any): void;
|
|
849
|
+
(eventType: string, payload: any, callback: (response: TwinMessageResult) => void): Promise<TwinMessageResult>;
|
|
850
|
+
};
|
|
851
|
+
/** Listen for events/actions, with optional respond callback for request-response */
|
|
852
|
+
on: (eventType: string, callback: (message: any, respond?: (result: TwinMessageResult) => void) => void) => void;
|
|
853
|
+
/** Target a different twin for messaging */
|
|
854
|
+
to: (targetTwinId: string) => {
|
|
855
|
+
emit: {
|
|
856
|
+
(eventType: string, payload: any): void;
|
|
857
|
+
(eventType: string, payload: any, callback: (response: TwinMessageResult) => void): Promise<TwinMessageResult>;
|
|
858
|
+
};
|
|
859
|
+
};
|
|
860
|
+
getDataChannel: () => Promise<PhygridDataChannel | undefined>;
|
|
861
|
+
onDataChannel: (callback: (dc: PhygridDataChannel) => void) => Promise<void>;
|
|
862
|
+
getMediaStream: () => Promise<{stream: PhygridMediaStream; close: () => void} | undefined>;
|
|
863
|
+
onMediaStream: (callback: (stream: PhygridMediaStream) => void) => Promise<void>;
|
|
864
|
+
updateReported: (properties: Record<string, any>) => Promise<TwinResponse>;
|
|
865
|
+
onUpdateReported: (callback: (reportedProperties: Record<string, any>) => void) => void;
|
|
866
|
+
updateDesired: (properties: Record<string, any>) => Promise<TwinResponse>;
|
|
867
|
+
onUpdateDesired: (callback: (reportedProperties: Record<string, any>) => void) => void;
|
|
868
|
+
remove : () => Promise<TwinResponse | void>;
|
|
869
|
+
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { PhygridDataChannel, WebRTCConnectionOptions } from '../../types/webrtc.types';
|
|
2
|
-
export interface WebRTCDataChannelResult {
|
|
3
|
-
dataChannel: PhygridDataChannel;
|
|
4
|
-
close: () => void;
|
|
5
|
-
_rawPeerConnection?: RTCPeerConnection;
|
|
6
|
-
_rawDataChannel?: RTCDataChannel;
|
|
7
|
-
}
|
|
8
|
-
export type WebRTCDataChannelConnectionResult = WebRTCDataChannelResult;
|
|
9
|
-
export declare function createWebRTCDataChannelConnection(options: WebRTCConnectionOptions): Promise<WebRTCDataChannelConnectionResult>;
|
|
10
|
-
//# sourceMappingURL=datachannel.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"datachannel.d.ts","sourceRoot":"","sources":["../../../src/services/webrtc/datachannel.ts"],"names":[],"mappings":"AAyCA,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AA4CvF,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,kBAAkB,CAAC;IAChC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,kBAAkB,CAAC,EAAE,iBAAiB,CAAC;IACvC,eAAe,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,MAAM,iCAAiC,GAAG,uBAAuB,CAAC;AAOxE,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,iCAAiC,CAAC,CAyK5C"}
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createWebRTCDataChannelConnection = createWebRTCDataChannelConnection;
|
|
4
|
-
(async function initializeWebRTCGlobals() {
|
|
5
|
-
if (typeof window !== 'undefined')
|
|
6
|
-
return;
|
|
7
|
-
try {
|
|
8
|
-
const wrtc = require('@roamhq/wrtc');
|
|
9
|
-
if (typeof global.MediaStream === 'undefined' && wrtc.MediaStream) {
|
|
10
|
-
global.MediaStream = wrtc.MediaStream;
|
|
11
|
-
console.log('Global MediaStream initialized');
|
|
12
|
-
}
|
|
13
|
-
if (typeof global.RTCDataChannel === 'undefined' && wrtc.RTCDataChannel) {
|
|
14
|
-
global.RTCDataChannel = wrtc.RTCDataChannel;
|
|
15
|
-
console.log('Global RTCDataChannel initialized');
|
|
16
|
-
}
|
|
17
|
-
if (typeof global.RTCPeerConnection === 'undefined' && wrtc.RTCPeerConnection) {
|
|
18
|
-
global.RTCPeerConnection = wrtc.RTCPeerConnection;
|
|
19
|
-
console.log('Global RTCPeerConnection initialized');
|
|
20
|
-
}
|
|
21
|
-
if (typeof global.RTCSessionDescription === 'undefined' && wrtc.RTCSessionDescription) {
|
|
22
|
-
global.RTCSessionDescription = wrtc.RTCSessionDescription;
|
|
23
|
-
console.log('Global RTCSessionDescription initialized');
|
|
24
|
-
}
|
|
25
|
-
if (typeof global.RTCIceCandidate === 'undefined' && wrtc.RTCIceCandidate) {
|
|
26
|
-
global.RTCIceCandidate = wrtc.RTCIceCandidate;
|
|
27
|
-
console.log('Global RTCIceCandidate initialized');
|
|
28
|
-
}
|
|
29
|
-
console.log('WebRTC globals successfully initialized');
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
console.error('Failed to initialize WebRTC globals:', error);
|
|
33
|
-
}
|
|
34
|
-
})().catch(err => console.error('Error in WebRTC globals initialization:', err));
|
|
35
|
-
const peer_connection_ice_1 = require("./peer-connection-ice");
|
|
36
|
-
(async function initializeWebRTCGlobals() {
|
|
37
|
-
if (typeof window !== 'undefined')
|
|
38
|
-
return;
|
|
39
|
-
try {
|
|
40
|
-
const wrtc = require('@roamhq/wrtc');
|
|
41
|
-
if (typeof global.MediaStream === 'undefined' && wrtc.MediaStream) {
|
|
42
|
-
global.MediaStream = wrtc.MediaStream;
|
|
43
|
-
console.log('Global MediaStream initialized');
|
|
44
|
-
}
|
|
45
|
-
if (typeof global.RTCPeerConnection === 'undefined' && wrtc.RTCPeerConnection) {
|
|
46
|
-
global.RTCPeerConnection = wrtc.RTCPeerConnection;
|
|
47
|
-
console.log('Global RTCPeerConnection initialized');
|
|
48
|
-
}
|
|
49
|
-
if (typeof global.RTCSessionDescription === 'undefined' && wrtc.RTCSessionDescription) {
|
|
50
|
-
global.RTCSessionDescription = wrtc.RTCSessionDescription;
|
|
51
|
-
console.log('Global RTCSessionDescription initialized');
|
|
52
|
-
}
|
|
53
|
-
if (typeof global.RTCIceCandidate === 'undefined' && wrtc.RTCIceCandidate) {
|
|
54
|
-
global.RTCIceCandidate = wrtc.RTCIceCandidate;
|
|
55
|
-
console.log('Global RTCIceCandidate initialized');
|
|
56
|
-
}
|
|
57
|
-
console.log('WebRTC globals successfully initialized');
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
console.error('Failed to initialize WebRTC globals:', error);
|
|
61
|
-
}
|
|
62
|
-
})().catch(err => console.error('Error in WebRTC globals initialization:', err));
|
|
63
|
-
const CONNECTION_TIMEOUT = 15000;
|
|
64
|
-
const RECONNECT_DELAY = 1000;
|
|
65
|
-
const INITIAL_RETRY_DELAY = 1000;
|
|
66
|
-
const MAX_RETRY_DELAY = 30000;
|
|
67
|
-
async function createWebRTCDataChannelConnection(options) {
|
|
68
|
-
let pc = null;
|
|
69
|
-
let isShuttingDown = false;
|
|
70
|
-
let isReconnecting = false;
|
|
71
|
-
let currentConnectionId = 0;
|
|
72
|
-
let pendingCandidates = [];
|
|
73
|
-
let activeDataChannel = null;
|
|
74
|
-
const { targetTwinId, sendTwinMessage, subscribeTwin, onTwinMessage, offTwinMessage, useStun = true, isInitiator = true, channelPrefix = 'channel', mediaOptions = { isMediaConnection: false }, } = options;
|
|
75
|
-
const { persistentChannel, setupDataChannel, setCleanupFn } = createPersistentDataChannel();
|
|
76
|
-
setCleanupFn(() => {
|
|
77
|
-
if (mediaOptions.isMediaConnection && mediaOptions.localStream) {
|
|
78
|
-
mediaOptions.localStream.getTracks().forEach(track => track.stop());
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
const callbacks = {
|
|
82
|
-
async onCreateConnection(pcInstance, connectionId, localInitiator) {
|
|
83
|
-
console.log(`Creating data channel for connection ID: ${connectionId} ${JSON.stringify(localInitiator)}`);
|
|
84
|
-
const channelId = `${channelPrefix}-${targetTwinId}`;
|
|
85
|
-
return new Promise(resolve => {
|
|
86
|
-
if (isInitiator) {
|
|
87
|
-
const dataChannel = pcInstance.createDataChannel(channelId);
|
|
88
|
-
dataChannel.binaryType = 'arraybuffer';
|
|
89
|
-
setupDataChannel(dataChannel, isReconnecting, isShuttingDown, triggerReconnect);
|
|
90
|
-
resolve(dataChannel);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
pcInstance.ondatachannel = event => {
|
|
94
|
-
console.log('Data channel received in hub-client');
|
|
95
|
-
const dataChannel = event.channel;
|
|
96
|
-
dataChannel.binaryType = 'arraybuffer';
|
|
97
|
-
setupDataChannel(dataChannel, isReconnecting, isShuttingDown, triggerReconnect);
|
|
98
|
-
};
|
|
99
|
-
resolve(null);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
},
|
|
103
|
-
onCloseCleanup() {
|
|
104
|
-
if (mediaOptions.isMediaConnection && mediaOptions.localStream) {
|
|
105
|
-
mediaOptions.localStream.getTracks().forEach(track => track.stop());
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
async onSubscribe() {
|
|
109
|
-
await subscribeTwin(targetTwinId);
|
|
110
|
-
},
|
|
111
|
-
async sendSignalingMessage(type, data) {
|
|
112
|
-
await sendTwinMessage(targetTwinId, {
|
|
113
|
-
type: `${channelPrefix}-${targetTwinId}:${type}`,
|
|
114
|
-
data,
|
|
115
|
-
});
|
|
116
|
-
},
|
|
117
|
-
setupMessageHandling(messageHandler) {
|
|
118
|
-
onTwinMessage(targetTwinId, messageHandler);
|
|
119
|
-
},
|
|
120
|
-
removeMessageHandling(messageHandler) {
|
|
121
|
-
console.log(`Removing message listener for twin ${targetTwinId}`);
|
|
122
|
-
offTwinMessage(targetTwinId, messageHandler);
|
|
123
|
-
},
|
|
124
|
-
reportConnectionState(info) {
|
|
125
|
-
console.log(info);
|
|
126
|
-
},
|
|
127
|
-
reportError(info, error) {
|
|
128
|
-
console.error(info, error || '');
|
|
129
|
-
},
|
|
130
|
-
async retryConnection(retries, toggledUseStun, nextDelay) {
|
|
131
|
-
isReconnecting = false;
|
|
132
|
-
return (0, peer_connection_ice_1.attemptConnection)(state, { ...options, useStun: toggledUseStun !== null && toggledUseStun !== void 0 ? toggledUseStun : useStun }, retries, toggledUseStun !== null && toggledUseStun !== void 0 ? toggledUseStun : useStun, nextDelay !== null && nextDelay !== void 0 ? nextDelay : INITIAL_RETRY_DELAY, INITIAL_RETRY_DELAY, CONNECTION_TIMEOUT, MAX_RETRY_DELAY, callbacks);
|
|
133
|
-
},
|
|
134
|
-
safeReject(error) {
|
|
135
|
-
throw error;
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
const state = {
|
|
139
|
-
pc,
|
|
140
|
-
isShuttingDown,
|
|
141
|
-
isReconnecting,
|
|
142
|
-
currentConnectionId,
|
|
143
|
-
pendingCandidates,
|
|
144
|
-
activeDataChannel,
|
|
145
|
-
useStun,
|
|
146
|
-
channelPrefix,
|
|
147
|
-
targetTwinId,
|
|
148
|
-
isInitiator,
|
|
149
|
-
connectionType: 'datachannel',
|
|
150
|
-
};
|
|
151
|
-
function triggerReconnect(attempt) {
|
|
152
|
-
if (isReconnecting || isShuttingDown) {
|
|
153
|
-
console.log(`triggerReconnect() called but already reconnecting or shutting down (attempt ${attempt})`);
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
isReconnecting = true;
|
|
157
|
-
(0, peer_connection_ice_1.triggerReconnect)(state, attempt, RECONNECT_DELAY, callbacks);
|
|
158
|
-
}
|
|
159
|
-
const result = await (0, peer_connection_ice_1.attemptConnection)(state, { ...options }, 0, useStun, INITIAL_RETRY_DELAY, INITIAL_RETRY_DELAY, CONNECTION_TIMEOUT, MAX_RETRY_DELAY, callbacks);
|
|
160
|
-
return {
|
|
161
|
-
dataChannel: persistentChannel,
|
|
162
|
-
_rawPeerConnection: result._rawPeerConnection,
|
|
163
|
-
_rawDataChannel: result._rawDataChannel,
|
|
164
|
-
close: () => {
|
|
165
|
-
isShuttingDown = true;
|
|
166
|
-
if (result.close) {
|
|
167
|
-
result.close();
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
const createPersistentDataChannel = () => {
|
|
173
|
-
const messageListeners = [];
|
|
174
|
-
const closeListeners = [];
|
|
175
|
-
let currentDataChannel = null;
|
|
176
|
-
let activeDataChannel = null;
|
|
177
|
-
let reconnectAttempts = 0;
|
|
178
|
-
let cleanupFn = null;
|
|
179
|
-
const setCleanupFn = (fn) => {
|
|
180
|
-
cleanupFn = fn;
|
|
181
|
-
};
|
|
182
|
-
const persistentChannel = {
|
|
183
|
-
send: (data) => {
|
|
184
|
-
if (activeDataChannel && activeDataChannel.readyState === 'open') {
|
|
185
|
-
try {
|
|
186
|
-
if (typeof data === 'object' && data !== null) {
|
|
187
|
-
activeDataChannel.send(JSON.stringify(data));
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
activeDataChannel.send(data);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
console.error('Error sending data:', error);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
console.warn('Channel not ready, dropping message. State:', activeDataChannel ? activeDataChannel.readyState : 'no channel');
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
onMessage: (callback) => {
|
|
202
|
-
console.log('Adding message listener to persistent channel');
|
|
203
|
-
messageListeners.push(callback);
|
|
204
|
-
},
|
|
205
|
-
offMessage: (callback) => {
|
|
206
|
-
const index = messageListeners.indexOf(callback);
|
|
207
|
-
if (index !== -1) {
|
|
208
|
-
messageListeners.splice(index, 1);
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
|
-
onClose: (callback) => {
|
|
212
|
-
closeListeners.push(callback);
|
|
213
|
-
},
|
|
214
|
-
offClose: (callback) => {
|
|
215
|
-
const index = closeListeners.indexOf(callback);
|
|
216
|
-
if (index !== -1) {
|
|
217
|
-
closeListeners.splice(index, 1);
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
close: () => {
|
|
221
|
-
if (currentDataChannel && currentDataChannel.readyState !== 'closed') {
|
|
222
|
-
currentDataChannel.close();
|
|
223
|
-
}
|
|
224
|
-
if (cleanupFn)
|
|
225
|
-
cleanupFn();
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
const setupDataChannel = (dc, isReconnecting, isShuttingDown, triggerReconnect) => {
|
|
229
|
-
console.log('Setting up data channel with listeners');
|
|
230
|
-
currentDataChannel = dc;
|
|
231
|
-
activeDataChannel = dc;
|
|
232
|
-
dc.onopen = () => {
|
|
233
|
-
console.log('Data channel opened in setupDataChannel, readyState:', dc.readyState);
|
|
234
|
-
currentDataChannel = dc;
|
|
235
|
-
activeDataChannel = dc;
|
|
236
|
-
};
|
|
237
|
-
dc.onmessage = event => {
|
|
238
|
-
console.log('Received message in data channel:', typeof event.data);
|
|
239
|
-
let parsedData = event.data;
|
|
240
|
-
if (typeof event.data === 'string') {
|
|
241
|
-
try {
|
|
242
|
-
parsedData = JSON.parse(event.data);
|
|
243
|
-
}
|
|
244
|
-
catch (e) {
|
|
245
|
-
parsedData = event.data;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (messageListeners.length === 0) {
|
|
249
|
-
console.warn('No message listeners registered for data channel');
|
|
250
|
-
}
|
|
251
|
-
messageListeners.forEach(listener => {
|
|
252
|
-
try {
|
|
253
|
-
listener(parsedData);
|
|
254
|
-
}
|
|
255
|
-
catch (error) {
|
|
256
|
-
console.error('Error handling message:', error);
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
};
|
|
260
|
-
dc.onclose = () => {
|
|
261
|
-
console.log('Data channel closed');
|
|
262
|
-
closeListeners.forEach(listener => {
|
|
263
|
-
try {
|
|
264
|
-
listener();
|
|
265
|
-
}
|
|
266
|
-
catch (error) {
|
|
267
|
-
console.error('Error in close listener:', error);
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
if (!isShuttingDown && !isReconnecting) {
|
|
271
|
-
console.log('Data channel closed unexpectedly, triggering reconnect');
|
|
272
|
-
triggerReconnect(reconnectAttempts++);
|
|
273
|
-
}
|
|
274
|
-
activeDataChannel = null;
|
|
275
|
-
};
|
|
276
|
-
dc.onerror = error => {
|
|
277
|
-
console.error('Data channel error:', error);
|
|
278
|
-
if (!isShuttingDown && !isReconnecting) {
|
|
279
|
-
console.log('Data channel error, triggering reconnect');
|
|
280
|
-
triggerReconnect(reconnectAttempts++);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
};
|
|
284
|
-
return {
|
|
285
|
-
persistentChannel,
|
|
286
|
-
setupDataChannel,
|
|
287
|
-
setCleanupFn,
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
//# sourceMappingURL=datachannel.js.map
|