@device-portal/react 0.0.18 → 0.0.20
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/__tests__/DevicePortalConsumer.test.d.ts +1 -0
- package/dist/__tests__/helpers.d.ts +20 -0
- package/dist/__tests__/integration.test.d.ts +1 -0
- package/dist/__tests__/setup.d.ts +1 -0
- package/dist/__tests__/useDevicePortalConsumer.test.d.ts +1 -0
- package/dist/__tests__/useDevicePortalProvider.test.d.ts +1 -0
- package/dist/_virtual/_rollupPluginBabelHelpers.js +1 -49
- package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
- package/dist/consumer/DevicePortalConsumer.d.ts +1 -1
- package/dist/consumer/DevicePortalConsumer.js.map +1 -1
- package/dist/consumer/useDevicePortalConsumer.d.ts +14 -11
- package/dist/consumer/useDevicePortalConsumer.js +109 -97
- package/dist/consumer/useDevicePortalConsumer.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/provider/DevicePortalProvider.d.ts +1 -1
- package/dist/provider/DevicePortalProvider.js +10 -10
- package/dist/provider/DevicePortalProvider.js.map +1 -1
- package/dist/provider/useDevicePortalPeer.d.ts +2 -2
- package/dist/provider/useDevicePortalPeer.js +5 -5
- package/dist/provider/useDevicePortalPeer.js.map +1 -1
- package/dist/provider/useDevicePortalProvider.d.ts +3 -3
- package/dist/provider/useDevicePortalProvider.js +26 -15
- package/dist/provider/useDevicePortalProvider.js.map +1 -1
- package/package.json +10 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PeerId } from '@device-portal/client';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export declare function uniqueRoom(): string;
|
|
4
|
+
export declare const directOnlyOptions: {
|
|
5
|
+
webSocketSignalingServer: null;
|
|
6
|
+
browserDirect: "same-window-only";
|
|
7
|
+
};
|
|
8
|
+
export declare function TestProvider({ room, value, onMessageFromConsumer, maxClients, }: {
|
|
9
|
+
room: string;
|
|
10
|
+
value?: string;
|
|
11
|
+
onMessageFromConsumer?: (value: string, peerId: PeerId) => void;
|
|
12
|
+
maxClients?: number;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function TestConsumer({ room, onSendRef, }: {
|
|
15
|
+
room: string;
|
|
16
|
+
onSendRef?: React.MutableRefObject<((msg: string) => void) | null>;
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare function SuspenseWrapper({ children }: {
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -6,54 +6,6 @@ function _arrayLikeToArray(r, a) {
|
|
|
6
6
|
function _arrayWithHoles(r) {
|
|
7
7
|
if (Array.isArray(r)) return r;
|
|
8
8
|
}
|
|
9
|
-
function _createForOfIteratorHelper(r, e) {
|
|
10
|
-
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
11
|
-
if (!t) {
|
|
12
|
-
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
|
|
13
|
-
t && (r = t);
|
|
14
|
-
var n = 0,
|
|
15
|
-
F = function () {};
|
|
16
|
-
return {
|
|
17
|
-
s: F,
|
|
18
|
-
n: function () {
|
|
19
|
-
return n >= r.length ? {
|
|
20
|
-
done: true
|
|
21
|
-
} : {
|
|
22
|
-
done: false,
|
|
23
|
-
value: r[n++]
|
|
24
|
-
};
|
|
25
|
-
},
|
|
26
|
-
e: function (r) {
|
|
27
|
-
throw r;
|
|
28
|
-
},
|
|
29
|
-
f: F
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
33
|
-
}
|
|
34
|
-
var o,
|
|
35
|
-
a = true,
|
|
36
|
-
u = false;
|
|
37
|
-
return {
|
|
38
|
-
s: function () {
|
|
39
|
-
t = t.call(r);
|
|
40
|
-
},
|
|
41
|
-
n: function () {
|
|
42
|
-
var r = t.next();
|
|
43
|
-
return a = r.done, r;
|
|
44
|
-
},
|
|
45
|
-
e: function (r) {
|
|
46
|
-
u = true, o = r;
|
|
47
|
-
},
|
|
48
|
-
f: function () {
|
|
49
|
-
try {
|
|
50
|
-
a || null == t.return || t.return();
|
|
51
|
-
} finally {
|
|
52
|
-
if (u) throw o;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
9
|
function _iterableToArrayLimit(r, l) {
|
|
58
10
|
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
59
11
|
if (null != t) {
|
|
@@ -112,5 +64,5 @@ function _unsupportedIterableToArray(r, a) {
|
|
|
112
64
|
}
|
|
113
65
|
}
|
|
114
66
|
|
|
115
|
-
export { _arrayLikeToArray as arrayLikeToArray, _arrayWithHoles as arrayWithHoles,
|
|
67
|
+
export { _arrayLikeToArray as arrayLikeToArray, _arrayWithHoles as arrayWithHoles, _iterableToArrayLimit as iterableToArrayLimit, _nonIterableRest as nonIterableRest, _objectWithoutProperties as objectWithoutProperties, _objectWithoutPropertiesLoose as objectWithoutPropertiesLoose, _slicedToArray as slicedToArray, _unsupportedIterableToArray as unsupportedIterableToArray };
|
|
116
68
|
//# sourceMappingURL=_rollupPluginBabelHelpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_rollupPluginBabelHelpers.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_rollupPluginBabelHelpers.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type FunctionComponent, type ReactNode } from 'react';
|
|
2
1
|
import { type BrowserDirectOption } from '@device-portal/client';
|
|
2
|
+
import { type FunctionComponent, type ReactNode } from 'react';
|
|
3
3
|
export declare const DevicePortalConsumer: FunctionComponent<{
|
|
4
4
|
room: string;
|
|
5
5
|
webSocketSignalingServer?: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevicePortalConsumer.js","sources":["../../src/consumer/DevicePortalConsumer.tsx"],"sourcesContent":["import { type
|
|
1
|
+
{"version":3,"file":"DevicePortalConsumer.js","sources":["../../src/consumer/DevicePortalConsumer.tsx"],"sourcesContent":["import { type BrowserDirectOption } from '@device-portal/client'\nimport { type FunctionComponent, type ReactNode } from 'react'\nimport { useDevicePortalConsumer } from './useDevicePortalConsumer'\n\nexport const DevicePortalConsumer: FunctionComponent<{\n\troom: string\n\twebSocketSignalingServer?: string | null\n\tbrowserDirect?: BrowserDirectOption\n\tchildren: (data: {\n\t\tvalue: string\n\t\tsendMessageToProvider: (message: string) => void\n\t}) => ReactNode\n}> = ({ room, webSocketSignalingServer, browserDirect, children }) => {\n\tconst { value, sendMessageToProvider } = useDevicePortalConsumer(room, {\n\t\twebSocketSignalingServer,\n\t\tbrowserDirect,\n\t})\n\treturn <>{children({ value, sendMessageToProvider })}</>\n}\n"],"names":["DevicePortalConsumer","t0","$","_c","room","webSocketSignalingServer","browserDirect","children","t1","_useDevicePortalConsu","useDevicePortalConsumer","value","sendMessageToProvider","t2","_jsx","_Fragment"],"mappings":";;;;IAIaA,oBAAoB,GAQ5B,SARQA,oBAAoBA,CAAAC,EAAA,EAAA;EAAA,IAAAC,CAAA,GAAAC,CAAA,CAAA,CAAA,CAAA;AAQ3B,EAAA,IAAAC,IAAA,GAAAH,EAA2D,CAAAG,IAAA;IAAAC,wBAAA,GAA3DJ,EAA2D,CAAAI,wBAAA;IAAAC,aAAA,GAA3DL,EAA2D,CAAAK,aAAA;IAAAC,QAAA,GAA3DN,EAA2D,CAAAM,QAAA;AAAA,EAAA,IAAAC,EAAA;AAAA,EAAA,IAAAN,CAAA,CAAAI,CAAAA,CAAAA,KAAAA,aAAA,IAAAJ,CAAA,QAAAG,wBAAA,EAAA;IACOG,EAAA,GAAA;AAAAH,MAAAA,wBAAA,EACtEA,wBAAwB;AAAAC,MAAAA,aAAA,EACxBA;KACA;AAAAJ,IAAAA,CAAA,MAAAI,aAAA;AAAAJ,IAAAA,CAAA,MAAAG,wBAAA;AAAAH,IAAAA,CAAA,MAAAM,EAAA;AAAA,GAAA,MAAA;AAAAA,IAAAA,EAAA,GAAAN,CAAA,CAAA,CAAA,CAAA;AAAA;AAHD,EAAA,IAAAO,qBAAA,GAAyCC,uBAAuB,CAACN,IAAI,EAAEI,EAGtE,CAAC;IAAAG,KAAA,GAAAF,qBAAA,CAAAE,KAAA;IAAAC,qBAAA,GAAAH,qBAAA,CAAAG,qBAAA;AAAA,EAAA,IAAAC,EAAA;EAAA,IAAAX,CAAA,CAAAK,CAAAA,CAAAA,KAAAA,QAAA,IAAAL,CAAA,QAAAU,qBAAA,IAAAV,CAAA,CAAA,CAAA,CAAA,KAAAS,KAAA,EAAA;AACKE,IAAAA,EAAA,GAAAC,GAAA,CAAAC,QAAA,EAAA;MAAAR,QAAA,EAAGA,QAAQ,CAAC;AAAAI,QAAAA,KAAA,EAAEA,KAAK;AAAAC,QAAAA,qBAAA,EAAEA;OAAuB;AAAC,KAAA,CAAI;AAAAV,IAAAA,CAAA,MAAAK,QAAA;AAAAL,IAAAA,CAAA,MAAAU,qBAAA;AAAAV,IAAAA,CAAA,MAAAS,KAAA;AAAAT,IAAAA,CAAA,MAAAW,EAAA;AAAA,GAAA,MAAA;AAAAA,IAAAA,EAAA,GAAAX,CAAA,CAAA,CAAA,CAAA;AAAA;AAAA,EAAA,OAAjDW,EAAiD;AAAA;;;;"}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import { type BrowserDirectOption } from '@device-portal/client';
|
|
2
|
-
type State = {
|
|
3
|
-
room: string;
|
|
4
|
-
value: string;
|
|
5
|
-
sendMessageToProvider: (value: string) => void;
|
|
6
|
-
};
|
|
7
2
|
/**
|
|
8
3
|
* Configuration options for the Device Portal Consumer.
|
|
9
4
|
*/
|
|
@@ -13,15 +8,23 @@ export type DevicePortalConsumerOptions = {
|
|
|
13
8
|
/** Browser direct signaling options. */
|
|
14
9
|
browserDirect?: BrowserDirectOption;
|
|
15
10
|
/** Whether to automatically send the last 'send()' value back to the provider on reconnect. Default: false. */
|
|
16
|
-
|
|
11
|
+
sendLastMessageOnReconnect?: boolean;
|
|
17
12
|
};
|
|
18
13
|
/**
|
|
19
|
-
* A React hook that joins a Device Portal room and receives
|
|
20
|
-
*
|
|
14
|
+
* A React hook that joins a Device Portal room and receives values from the
|
|
15
|
+
* provider. **Suspends** the component until the first value is received —
|
|
16
|
+
* wrap the consumer in a `<Suspense>` boundary.
|
|
17
|
+
*
|
|
18
|
+
* Requires React 19+. Uses `use()` for Suspense integration.
|
|
19
|
+
*
|
|
20
|
+
* Safe under React Strict Mode and concurrent features: the underlying
|
|
21
|
+
* Consumer is cached with a grace-period cleanup so synchronous
|
|
22
|
+
* unmount/remount cycles do not create duplicate connections.
|
|
21
23
|
*
|
|
22
24
|
* @param room - The unique room ID.
|
|
23
25
|
* @param options - Consumer configuration options.
|
|
24
|
-
* @returns An object containing the current value and a function to send messages back to the provider.
|
|
25
26
|
*/
|
|
26
|
-
export declare const useDevicePortalConsumer: (room: string, options?: DevicePortalConsumerOptions) =>
|
|
27
|
-
|
|
27
|
+
export declare const useDevicePortalConsumer: (room: string, options?: DevicePortalConsumerOptions) => {
|
|
28
|
+
value: string;
|
|
29
|
+
sendMessageToProvider: (message: string) => void;
|
|
30
|
+
};
|
|
@@ -1,116 +1,128 @@
|
|
|
1
|
-
import { slicedToArray as _slicedToArray
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { slicedToArray as _slicedToArray } from '../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import { generatePeerId, Consumer } from '@device-portal/client';
|
|
3
|
+
import { useRef, use, useState, useEffect, useCallback } from 'react';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
var entries = new Map();
|
|
6
|
+
function optionsKey(options) {
|
|
7
|
+
var _options$webSocketSig, _options$browserDirec, _options$sendLastMess;
|
|
8
|
+
return "".concat((_options$webSocketSig = options.webSocketSignalingServer) !== null && _options$webSocketSig !== void 0 ? _options$webSocketSig : '', "\0").concat((_options$browserDirec = options.browserDirect) !== null && _options$browserDirec !== void 0 ? _options$browserDirec : '', "\0").concat((_options$sendLastMess = options.sendLastMessageOnReconnect) !== null && _options$sendLastMess !== void 0 ? _options$sendLastMess : '');
|
|
9
|
+
}
|
|
10
|
+
function getOrCreateEntry(key, room, peerId, options) {
|
|
11
|
+
var _options$sendLastMess2;
|
|
12
|
+
var existing = entries.get(key);
|
|
13
|
+
var newOptionsKey = optionsKey(options);
|
|
14
|
+
if (existing) {
|
|
15
|
+
// Cancel any pending destruction
|
|
16
|
+
if (existing.destroyTimer !== null) {
|
|
17
|
+
clearTimeout(existing.destroyTimer);
|
|
18
|
+
existing.destroyTimer = null;
|
|
19
|
+
}
|
|
20
|
+
// Reuse if room + options unchanged
|
|
21
|
+
if (existing.room === room && existing.optionsSnapshot === newOptionsKey) {
|
|
22
|
+
return existing;
|
|
23
|
+
}
|
|
24
|
+
// Room/options changed – destroy old entry synchronously
|
|
25
|
+
existing.consumer.destroy();
|
|
26
|
+
entries["delete"](key);
|
|
9
27
|
}
|
|
10
|
-
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
reject = rej;
|
|
28
|
+
// Create a deferred promise for Suspense via use()
|
|
29
|
+
var resolveFirst;
|
|
30
|
+
var firstValuePromise = new Promise(function (resolve) {
|
|
31
|
+
resolveFirst = resolve;
|
|
15
32
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
var sendLastMessageOnReconnect = (_options$sendLastMess2 = options.sendLastMessageOnReconnect) !== null && _options$sendLastMess2 !== void 0 ? _options$sendLastMess2 : false;
|
|
34
|
+
var entry = {
|
|
35
|
+
consumer: undefined,
|
|
36
|
+
// assigned below
|
|
37
|
+
firstValuePromise: firstValuePromise,
|
|
38
|
+
latestValue: undefined,
|
|
39
|
+
lastSentValue: undefined,
|
|
40
|
+
setValue: null,
|
|
41
|
+
destroyTimer: null,
|
|
42
|
+
room: room,
|
|
43
|
+
optionsSnapshot: newOptionsKey
|
|
20
44
|
};
|
|
21
|
-
|
|
22
|
-
|
|
45
|
+
entry.consumer = new Consumer(room, {
|
|
46
|
+
onMessage: function onMessage(value) {
|
|
47
|
+
var _entry$setValue;
|
|
48
|
+
entry.latestValue = value;
|
|
49
|
+
resolveFirst(value);
|
|
50
|
+
(_entry$setValue = entry.setValue) === null || _entry$setValue === void 0 || _entry$setValue.call(entry, value);
|
|
51
|
+
},
|
|
52
|
+
onConnected: function onConnected() {
|
|
53
|
+
if (sendLastMessageOnReconnect && entry.lastSentValue !== undefined) {
|
|
54
|
+
entry.consumer.send(entry.lastSentValue);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
webSocketSignalingServer: options.webSocketSignalingServer,
|
|
58
|
+
browserDirect: options.browserDirect,
|
|
59
|
+
peerId: peerId
|
|
60
|
+
});
|
|
61
|
+
entries.set(key, entry);
|
|
62
|
+
return entry;
|
|
63
|
+
}
|
|
64
|
+
function scheduleDestroy(key) {
|
|
65
|
+
var entry = entries.get(key);
|
|
66
|
+
if (!entry || entry.destroyTimer !== null) return;
|
|
67
|
+
entry.destroyTimer = setTimeout(function () {
|
|
68
|
+
entry.consumer.destroy();
|
|
69
|
+
entries["delete"](key);
|
|
70
|
+
}, 5000);
|
|
71
|
+
}
|
|
23
72
|
/**
|
|
24
|
-
* A React hook that joins a Device Portal room and receives
|
|
25
|
-
*
|
|
73
|
+
* A React hook that joins a Device Portal room and receives values from the
|
|
74
|
+
* provider. **Suspends** the component until the first value is received —
|
|
75
|
+
* wrap the consumer in a `<Suspense>` boundary.
|
|
76
|
+
*
|
|
77
|
+
* Requires React 19+. Uses `use()` for Suspense integration.
|
|
78
|
+
*
|
|
79
|
+
* Safe under React Strict Mode and concurrent features: the underlying
|
|
80
|
+
* Consumer is cached with a grace-period cleanup so synchronous
|
|
81
|
+
* unmount/remount cycles do not create duplicate connections.
|
|
26
82
|
*
|
|
27
83
|
* @param room - The unique room ID.
|
|
28
84
|
* @param options - Consumer configuration options.
|
|
29
|
-
* @returns An object containing the current value and a function to send messages back to the provider.
|
|
30
85
|
*/
|
|
31
86
|
var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
|
|
32
87
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
33
|
-
|
|
88
|
+
// Key by room + options rather than useId(), because useId() is not
|
|
89
|
+
// stable across Suspense re-throws when sibling state updates cause
|
|
90
|
+
// the component tree to re-mount.
|
|
91
|
+
var cacheKey = "".concat(room, "\0").concat(optionsKey(options));
|
|
92
|
+
var peerIdRef = useRef(generatePeerId());
|
|
93
|
+
var entry = getOrCreateEntry(cacheKey, room, peerIdRef.current, options);
|
|
94
|
+
// Suspends until the first value arrives (React 19 use() API).
|
|
95
|
+
var firstValue = use(entry.firstValuePromise);
|
|
96
|
+
// After the first value, track subsequent updates via useState.
|
|
97
|
+
var _useState = useState(function () {
|
|
98
|
+
var _entry$latestValue;
|
|
99
|
+
return (_entry$latestValue = entry.latestValue) !== null && _entry$latestValue !== void 0 ? _entry$latestValue : firstValue;
|
|
100
|
+
}),
|
|
34
101
|
_useState2 = _slicedToArray(_useState, 2),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
var _options$sendLastValu;
|
|
40
|
-
var _withResolvers = withResolvers(),
|
|
41
|
-
firstValuePromise = _withResolvers.promise,
|
|
42
|
-
firstValueResolve = _withResolvers.resolve;
|
|
43
|
-
var sendMessageToProvider = function sendMessageToProvider(value) {
|
|
44
|
-
responders[room].responder.send(value);
|
|
45
|
-
};
|
|
46
|
-
var responder = new Responder(room, {
|
|
47
|
-
onMessage: function onMessage(value_0) {
|
|
48
|
-
var consumer = {
|
|
49
|
-
value: value_0,
|
|
50
|
-
sendMessageToProvider: sendMessageToProvider
|
|
51
|
-
};
|
|
52
|
-
responders[room].consumer = consumer;
|
|
53
|
-
// Resolve the suspension promise
|
|
54
|
-
firstValueResolve(value_0);
|
|
55
|
-
// Only trigger state updates for components that have already mounted
|
|
56
|
-
// components currently suspended will re-render when firstValuePromise resolves
|
|
57
|
-
var _iterator = _createForOfIteratorHelper(responders[room].setValueStates),
|
|
58
|
-
_step;
|
|
59
|
-
try {
|
|
60
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
61
|
-
var setState = _step.value;
|
|
62
|
-
setState({
|
|
63
|
-
room: room,
|
|
64
|
-
value: value_0,
|
|
65
|
-
sendMessageToProvider: sendMessageToProvider
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
} catch (err) {
|
|
69
|
-
_iterator.e(err);
|
|
70
|
-
} finally {
|
|
71
|
-
_iterator.f();
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
sendLastValueOnConnectAndReconnect: (_options$sendLastValu = options.sendLastValueOnConnectAndReconnect) !== null && _options$sendLastValu !== void 0 ? _options$sendLastValu : false,
|
|
75
|
-
webSocketSignalingServer: options.webSocketSignalingServer,
|
|
76
|
-
browserDirect: options.browserDirect
|
|
77
|
-
});
|
|
78
|
-
responders[room] = {
|
|
79
|
-
responder: responder,
|
|
80
|
-
firstValuePromise: firstValuePromise,
|
|
81
|
-
consumer: null,
|
|
82
|
-
setValueStates: new Set()
|
|
83
|
-
};
|
|
84
|
-
}
|
|
102
|
+
value = _useState2[0],
|
|
103
|
+
setValue = _useState2[1];
|
|
104
|
+
// Wire up the entry's setValue so the Consumer can push updates.
|
|
105
|
+
entry.setValue = setValue;
|
|
85
106
|
useEffect(function () {
|
|
86
|
-
var
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
setValueState({
|
|
92
|
-
room: room,
|
|
93
|
-
value: consumer_0.value,
|
|
94
|
-
sendMessageToProvider: consumer_0.sendMessageToProvider
|
|
95
|
-
});
|
|
107
|
+
var e = getOrCreateEntry(cacheKey, room, peerIdRef.current, options);
|
|
108
|
+
e.setValue = setValue;
|
|
109
|
+
// Sync in case a value arrived between render and effect commit.
|
|
110
|
+
if (e.latestValue !== undefined) {
|
|
111
|
+
setValue(e.latestValue);
|
|
96
112
|
}
|
|
97
113
|
return function () {
|
|
98
|
-
|
|
114
|
+
e.setValue = null;
|
|
115
|
+
scheduleDestroy(cacheKey);
|
|
99
116
|
};
|
|
100
|
-
}, [room,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (responders[room].consumer) {
|
|
110
|
-
return responders[room].consumer;
|
|
111
|
-
}
|
|
112
|
-
// 3. Otherwise, suspend on the first value promise
|
|
113
|
-
throw responders[room].firstValuePromise;
|
|
117
|
+
}, [cacheKey, room, options.webSocketSignalingServer, options.browserDirect, options.sendLastMessageOnReconnect]);
|
|
118
|
+
var sendMessageToProvider = useCallback(function (message) {
|
|
119
|
+
entry.lastSentValue = message;
|
|
120
|
+
entry.consumer.send(message);
|
|
121
|
+
}, [entry]);
|
|
122
|
+
return {
|
|
123
|
+
value: value,
|
|
124
|
+
sendMessageToProvider: sendMessageToProvider
|
|
125
|
+
};
|
|
114
126
|
};
|
|
115
127
|
|
|
116
128
|
export { useDevicePortalConsumer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDevicePortalConsumer.js","sources":["../../src/consumer/useDevicePortalConsumer.ts"],"sourcesContent":["import { useEffect, useState, type Dispatch, type SetStateAction } from 'react'\nimport { Responder, type BrowserDirectOption } from '@device-portal/client'\n\ntype State = {\n\troom: string\n\tvalue: string\n\tsendMessageToProvider: (value: string) => void\n}\n\n/** Polyfill/helper for Promise.withResolvers */\nconst withResolvers = <T>() => {\n\tif ((Promise as any).withResolvers) {\n\t\treturn (Promise as any).withResolvers() as {\n\t\t\tpromise: Promise<T>\n\t\t\tresolve: (value: T) => void\n\t\t\treject: (reason?: any) => void\n\t\t}\n\t}\n\tlet resolve: (value: T) => void\n\tlet reject: (reason?: any) => void\n\tconst promise = new Promise<T>((res, rej) => {\n\t\tresolve = res\n\t\treject = rej\n\t})\n\treturn { promise, resolve: resolve!, reject: reject! }\n}\n\nconst responders: {\n\t[room: string]: {\n\t\tresponder: Responder\n\t\tfirstValuePromise: Promise<string>\n\t\tconsumer: null | {\n\t\t\tvalue: string\n\t\t\tsendMessageToProvider: (value: string) => void\n\t\t}\n\t\tsetValueStates: Set<Dispatch<SetStateAction<State | null>>>\n\t}\n} = {}\n\n/**\n * Configuration options for the Device Portal Consumer.\n */\nexport type DevicePortalConsumerOptions = {\n\t/** URL of the signaling server, or null to disable. */\n\twebSocketSignalingServer?: string | null\n\t/** Browser direct signaling options. */\n\tbrowserDirect?: BrowserDirectOption\n\t/** Whether to automatically send the last 'send()' value back to the provider on reconnect. Default: false. */\n\tsendLastValueOnConnectAndReconnect?: boolean\n}\n\n/**\n * A React hook that joins a Device Portal room and receives value from the provider.\n * This hook suspends the component until the first value is received.\n *\n * @param room - The unique room ID.\n * @param options - Consumer configuration options.\n * @returns An object containing the current value and a function to send messages back to the provider.\n */\nexport const useDevicePortalConsumer = (\n\troom: string,\n\toptions: DevicePortalConsumerOptions = {},\n): Pick<State, 'value' | 'sendMessageToProvider'> => {\n\tconst [valueState, setValueState] = useState<State | null>(null)\n\n\t// Initialize the responder synchronously if it doesn't exist\n\tif (!responders[room]) {\n\t\tconsole.log(\n\t\t\t`[useDevicePortalConsumer] Creating new Responder for room: ${room}`,\n\t\t)\n\n\t\tconst { promise: firstValuePromise, resolve: firstValueResolve } =\n\t\t\twithResolvers<string>()\n\n\t\tconst sendMessageToProvider = (value: string) => {\n\t\t\tresponders[room].responder.send(value)\n\t\t}\n\n\t\tconst responder = new Responder(room, {\n\t\t\tonMessage: (value) => {\n\t\t\t\tconst consumer = { value, sendMessageToProvider }\n\t\t\t\tresponders[room].consumer = consumer\n\n\t\t\t\t// Resolve the suspension promise\n\t\t\t\tfirstValueResolve(value)\n\n\t\t\t\t// Only trigger state updates for components that have already mounted\n\t\t\t\t// components currently suspended will re-render when firstValuePromise resolves\n\t\t\t\tfor (const setState of responders[room].setValueStates) {\n\t\t\t\t\tsetState({ room, value, sendMessageToProvider })\n\t\t\t\t}\n\t\t\t},\n\t\t\tsendLastValueOnConnectAndReconnect:\n\t\t\t\toptions.sendLastValueOnConnectAndReconnect ?? false,\n\t\t\twebSocketSignalingServer: options.webSocketSignalingServer,\n\t\t\tbrowserDirect: options.browserDirect,\n\t\t})\n\n\t\tresponders[room] = {\n\t\t\tresponder,\n\t\t\tfirstValuePromise,\n\t\t\tconsumer: null,\n\t\t\tsetValueStates: new Set(),\n\t\t}\n\t}\n\n\tuseEffect(() => {\n\t\tconst roomEntry = responders[room]\n\t\troomEntry.setValueStates.add(setValueState)\n\n\t\t// If we already have a value, sync it immediately on mount\n\t\tif (roomEntry.consumer) {\n\t\t\tconst consumer = roomEntry.consumer!\n\t\t\tsetValueState({\n\t\t\t\troom,\n\t\t\t\tvalue: consumer.value,\n\t\t\t\tsendMessageToProvider: consumer.sendMessageToProvider,\n\t\t\t})\n\t\t}\n\n\t\treturn () => {\n\t\t\troomEntry.setValueStates.delete(setValueState)\n\t\t}\n\t}, [room, setValueState])\n\n\t// 1. If we have local state, use it\n\tif (valueState && valueState.room === room) {\n\t\treturn {\n\t\t\tvalue: valueState.value,\n\t\t\tsendMessageToProvider: valueState.sendMessageToProvider,\n\t\t}\n\t}\n\n\t// 2. If the shared responder already has a value, use it (handles re-renders after suspension)\n\tif (responders[room].consumer) {\n\t\treturn responders[room].consumer!\n\t}\n\n\t// 3. Otherwise, suspend on the first value promise\n\tthrow responders[room].firstValuePromise\n}\n"],"names":["withResolvers","Promise","resolve","reject","promise","res","rej","responders","useDevicePortalConsumer","room","options","arguments","length","undefined","_useState","useState","_useState2","_slicedToArray","valueState","setValueState","_options$sendLastValu","_withResolvers","firstValuePromise","firstValueResolve","sendMessageToProvider","value","responder","send","Responder","onMessage","consumer","_iterator","_createForOfIteratorHelper","setValueStates","_step","s","n","done","setState","err","e","f","sendLastValueOnConnectAndReconnect","webSocketSignalingServer","browserDirect","Set","useEffect","roomEntry","add"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useDevicePortalConsumer.js","sources":["../../src/consumer/useDevicePortalConsumer.ts"],"sourcesContent":["import {\n\tConsumer,\n\tgeneratePeerId,\n\ttype BrowserDirectOption,\n\ttype PeerId,\n} from '@device-portal/client'\nimport { use, useCallback, useEffect, useRef, useState } from 'react'\n\n/**\n * Configuration options for the Device Portal Consumer.\n */\nexport type DevicePortalConsumerOptions = {\n\t/** URL of the signaling server, or null to disable. */\n\twebSocketSignalingServer?: string | null\n\t/** Browser direct signaling options. */\n\tbrowserDirect?: BrowserDirectOption\n\t/** Whether to automatically send the last 'send()' value back to the provider on reconnect. Default: false. */\n\tsendLastMessageOnReconnect?: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Module-level cache for Consumer entries.\n//\n// Why module-level? The cache must survive React Suspense throws (which\n// discard in-progress render state) and React Strict Mode's synchronous\n// unmount/remount cycle. The entry is keyed by room + options, which is\n// stable across both scenarios.\n//\n// Cleanup uses a grace-period timeout so that Strict Mode's immediate\n// remount can reclaim the entry before it's destroyed.\n// ---------------------------------------------------------------------------\n\ntype ConsumerEntry = {\n\tconsumer: Consumer\n\tfirstValuePromise: Promise<string>\n\tlatestValue: string | undefined\n\tlastSentValue: string | undefined\n\tsetValue: ((value: string) => void) | null\n\tdestroyTimer: ReturnType<typeof setTimeout> | null\n\troom: string\n\toptionsSnapshot: string\n}\n\nconst entries = new Map<string, ConsumerEntry>()\n\nfunction optionsKey(options: DevicePortalConsumerOptions): string {\n\treturn `${options.webSocketSignalingServer ?? ''}\\0${options.browserDirect ?? ''}\\0${options.sendLastMessageOnReconnect ?? ''}`\n}\n\nfunction getOrCreateEntry(\n\tkey: string,\n\troom: string,\n\tpeerId: PeerId,\n\toptions: DevicePortalConsumerOptions,\n): ConsumerEntry {\n\tconst existing = entries.get(key)\n\tconst newOptionsKey = optionsKey(options)\n\n\tif (existing) {\n\t\t// Cancel any pending destruction\n\t\tif (existing.destroyTimer !== null) {\n\t\t\tclearTimeout(existing.destroyTimer)\n\t\t\texisting.destroyTimer = null\n\t\t}\n\t\t// Reuse if room + options unchanged\n\t\tif (existing.room === room && existing.optionsSnapshot === newOptionsKey) {\n\t\t\treturn existing\n\t\t}\n\t\t// Room/options changed – destroy old entry synchronously\n\t\texisting.consumer.destroy()\n\t\tentries.delete(key)\n\t}\n\n\t// Create a deferred promise for Suspense via use()\n\tlet resolveFirst!: (value: string) => void\n\tconst firstValuePromise = new Promise<string>((resolve) => {\n\t\tresolveFirst = resolve\n\t})\n\n\tconst sendLastMessageOnReconnect = options.sendLastMessageOnReconnect ?? false\n\n\tconst entry: ConsumerEntry = {\n\t\tconsumer: undefined!, // assigned below\n\t\tfirstValuePromise,\n\t\tlatestValue: undefined,\n\t\tlastSentValue: undefined,\n\t\tsetValue: null,\n\t\tdestroyTimer: null,\n\t\troom,\n\t\toptionsSnapshot: newOptionsKey,\n\t}\n\n\tentry.consumer = new Consumer(room, {\n\t\tonMessage: (value) => {\n\t\t\tentry.latestValue = value\n\t\t\tresolveFirst(value)\n\t\t\tentry.setValue?.(value)\n\t\t},\n\t\tonConnected: () => {\n\t\t\tif (sendLastMessageOnReconnect && entry.lastSentValue !== undefined) {\n\t\t\t\tentry.consumer.send(entry.lastSentValue)\n\t\t\t}\n\t\t},\n\t\twebSocketSignalingServer: options.webSocketSignalingServer,\n\t\tbrowserDirect: options.browserDirect,\n\t\tpeerId,\n\t})\n\n\tentries.set(key, entry)\n\treturn entry\n}\n\nfunction scheduleDestroy(key: string) {\n\tconst entry = entries.get(key)\n\tif (!entry || entry.destroyTimer !== null) return\n\n\tentry.destroyTimer = setTimeout(() => {\n\t\tentry.consumer.destroy()\n\t\tentries.delete(key)\n\t}, 5_000)\n}\n\n/**\n * A React hook that joins a Device Portal room and receives values from the\n * provider. **Suspends** the component until the first value is received —\n * wrap the consumer in a `<Suspense>` boundary.\n *\n * Requires React 19+. Uses `use()` for Suspense integration.\n *\n * Safe under React Strict Mode and concurrent features: the underlying\n * Consumer is cached with a grace-period cleanup so synchronous\n * unmount/remount cycles do not create duplicate connections.\n *\n * @param room - The unique room ID.\n * @param options - Consumer configuration options.\n */\nexport const useDevicePortalConsumer = (\n\troom: string,\n\toptions: DevicePortalConsumerOptions = {},\n): {\n\tvalue: string\n\tsendMessageToProvider: (message: string) => void\n} => {\n\t// Key by room + options rather than useId(), because useId() is not\n\t// stable across Suspense re-throws when sibling state updates cause\n\t// the component tree to re-mount.\n\tconst cacheKey = `${room}\\0${optionsKey(options)}`\n\tconst peerIdRef = useRef<PeerId>(generatePeerId())\n\n\tconst entry = getOrCreateEntry(cacheKey, room, peerIdRef.current, options)\n\n\t// Suspends until the first value arrives (React 19 use() API).\n\tconst firstValue = use(entry.firstValuePromise)\n\n\t// After the first value, track subsequent updates via useState.\n\tconst [value, setValue] = useState(() => entry.latestValue ?? firstValue)\n\n\t// Wire up the entry's setValue so the Consumer can push updates.\n\tentry.setValue = setValue\n\n\tuseEffect(() => {\n\t\tconst e = getOrCreateEntry(cacheKey, room, peerIdRef.current, options)\n\t\te.setValue = setValue\n\n\t\t// Sync in case a value arrived between render and effect commit.\n\t\tif (e.latestValue !== undefined) {\n\t\t\tsetValue(e.latestValue)\n\t\t}\n\n\t\treturn () => {\n\t\t\te.setValue = null\n\t\t\tscheduleDestroy(cacheKey)\n\t\t}\n\t}, [\n\t\tcacheKey,\n\t\troom,\n\t\toptions.webSocketSignalingServer,\n\t\toptions.browserDirect,\n\t\toptions.sendLastMessageOnReconnect,\n\t])\n\n\tconst sendMessageToProvider = useCallback(\n\t\t(message: string) => {\n\t\t\tentry.lastSentValue = message\n\t\t\tentry.consumer.send(message)\n\t\t},\n\t\t[entry],\n\t)\n\n\treturn { value, sendMessageToProvider }\n}\n"],"names":["entries","Map","optionsKey","options","_options$webSocketSig","_options$browserDirec","_options$sendLastMess","concat","webSocketSignalingServer","browserDirect","sendLastMessageOnReconnect","getOrCreateEntry","key","room","peerId","_options$sendLastMess2","existing","get","newOptionsKey","destroyTimer","clearTimeout","optionsSnapshot","consumer","destroy","resolveFirst","firstValuePromise","Promise","resolve","entry","undefined","latestValue","lastSentValue","setValue","Consumer","onMessage","value","_entry$setValue","call","onConnected","send","set","scheduleDestroy","setTimeout","useDevicePortalConsumer","arguments","length","cacheKey","peerIdRef","useRef","generatePeerId","current","firstValue","use","_useState","useState","_entry$latestValue","_useState2","_slicedToArray","useEffect","e","sendMessageToProvider","useCallback","message"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export * from './provider/useDevicePortalProvider';
|
|
|
3
3
|
export * from './provider/useDevicePortalPeer';
|
|
4
4
|
export * from './consumer/DevicePortalConsumer';
|
|
5
5
|
export * from './consumer/useDevicePortalConsumer';
|
|
6
|
-
export { type PeerId, type
|
|
6
|
+
export { type PeerId, type Provider } from '@device-portal/client';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type FunctionComponent, type ReactNode } from 'react';
|
|
2
1
|
import { type PeerId } from '@device-portal/client';
|
|
2
|
+
import { type FunctionComponent, type ReactNode } from 'react';
|
|
3
3
|
import { PeerOptions } from './useDevicePortalPeer';
|
|
4
4
|
import { type DevicePortalProviderOptions } from './useDevicePortalProvider';
|
|
5
5
|
export declare const DevicePortalProvider: FunctionComponent<{
|
|
@@ -27,35 +27,35 @@ var DevicePortalProvider = function DevicePortalProvider(t0) {
|
|
|
27
27
|
}
|
|
28
28
|
var _useDevicePortalProvi = useDevicePortalProvider(room, options),
|
|
29
29
|
peers = _useDevicePortalProvi.peers,
|
|
30
|
-
|
|
31
|
-
if (!
|
|
30
|
+
provider = _useDevicePortalProvi.provider;
|
|
31
|
+
if (!provider || !children) {
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
var t1;
|
|
35
|
-
if ($[4] !== children || $[5] !==
|
|
35
|
+
if ($[4] !== children || $[5] !== peers || $[6] !== provider) {
|
|
36
36
|
var t2;
|
|
37
|
-
if ($[8] !== children || $[9] !==
|
|
37
|
+
if ($[8] !== children || $[9] !== provider) {
|
|
38
38
|
t2 = function t2(peerId) {
|
|
39
39
|
return jsx(Fragment, {
|
|
40
40
|
children: children(function (options_0) {
|
|
41
41
|
return jsx(PeerBase, {
|
|
42
42
|
peerId: peerId,
|
|
43
|
-
|
|
43
|
+
provider: provider,
|
|
44
44
|
options: options_0
|
|
45
45
|
});
|
|
46
46
|
}, peerId)
|
|
47
47
|
}, peerId);
|
|
48
48
|
};
|
|
49
49
|
$[8] = children;
|
|
50
|
-
$[9] =
|
|
50
|
+
$[9] = provider;
|
|
51
51
|
$[10] = t2;
|
|
52
52
|
} else {
|
|
53
53
|
t2 = $[10];
|
|
54
54
|
}
|
|
55
55
|
t1 = peers.map(t2);
|
|
56
56
|
$[4] = children;
|
|
57
|
-
$[5] =
|
|
58
|
-
$[6] =
|
|
57
|
+
$[5] = peers;
|
|
58
|
+
$[6] = provider;
|
|
59
59
|
$[7] = t1;
|
|
60
60
|
} else {
|
|
61
61
|
t1 = $[7];
|
|
@@ -64,9 +64,9 @@ var DevicePortalProvider = function DevicePortalProvider(t0) {
|
|
|
64
64
|
};
|
|
65
65
|
var PeerBase = function PeerBase(t0) {
|
|
66
66
|
var peerId = t0.peerId,
|
|
67
|
-
|
|
67
|
+
provider = t0.provider,
|
|
68
68
|
options = t0.options;
|
|
69
|
-
useDevicePortalPeer(
|
|
69
|
+
useDevicePortalPeer(provider, peerId, options);
|
|
70
70
|
return null;
|
|
71
71
|
};
|
|
72
72
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevicePortalProvider.js","sources":["../../src/provider/DevicePortalProvider.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"DevicePortalProvider.js","sources":["../../src/provider/DevicePortalProvider.tsx"],"sourcesContent":["import { type PeerId, type Provider } from '@device-portal/client'\nimport { Fragment, type FunctionComponent, type ReactNode } from 'react'\nimport { PeerOptions, useDevicePortalPeer } from './useDevicePortalPeer'\nimport {\n\tuseDevicePortalProvider,\n\ttype DevicePortalProviderOptions,\n} from './useDevicePortalProvider'\n\nexport const DevicePortalProvider: FunctionComponent<\n\t{\n\t\troom: string\n\t} & Omit<DevicePortalProviderOptions, 'value'> &\n\t\t(\n\t\t\t| {\n\t\t\t\t\tvalue?: undefined\n\t\t\t\t\tchildren?: (\n\t\t\t\t\t\tPeer: FunctionComponent<PeerOptions>,\n\t\t\t\t\t\tpeerId: PeerId,\n\t\t\t\t\t) => ReactNode\n\t\t\t }\n\t\t\t| {\n\t\t\t\t\tvalue: string\n\t\t\t\t\tchildren?: undefined\n\t\t\t }\n\t\t)\n> = ({ room, children, ...options }) => {\n\tconst { peers, provider } = useDevicePortalProvider(room, options)\n\n\tif (!provider || !children) {\n\t\treturn null\n\t}\n\n\treturn peers.map((peerId) => (\n\t\t<Fragment key={peerId}>\n\t\t\t{children(\n\t\t\t\t(options) => (\n\t\t\t\t\t<PeerBase peerId={peerId} provider={provider} options={options} />\n\t\t\t\t),\n\t\t\t\tpeerId,\n\t\t\t)}\n\t\t</Fragment>\n\t))\n}\n\nconst PeerBase: FunctionComponent<{\n\tpeerId: PeerId\n\tprovider: Provider\n\toptions: PeerOptions\n}> = ({ peerId, provider, options }) => {\n\tuseDevicePortalPeer(provider, peerId, options)\n\treturn null\n}\n"],"names":["DevicePortalProvider","t0","$","_c","children","options","room","_t","_objectWithoutProperties","_excluded","_useDevicePortalProvi","useDevicePortalProvider","peers","provider","t1","t2","peerId","_jsx","Fragment","options_0","PeerBase","map","useDevicePortalPeer"],"mappings":";;;;;;;;IAQaA,oBAAoB,GAiB7B,SAjBSA,oBAAoBA,CAAAC,EAAA,EAAA;EAAA,IAAAC,CAAA,GAAAC,CAAA,CAAA,EAAA,CAAA;AAAA,EAAA,IAAAC,QAAA;AAAA,EAAA,IAAAC,OAAA;AAAA,EAAA,IAAAC,IAAA;EAAA,IAAAJ,CAAA,QAAAD,EAAA,EAAA;IAAA,IAAAM,EAAA,GAiB5BN,EAA8B;IAAAK,IAAA,GAAAC,EAAA,CAAAD,IAAA;IAAAF,QAAA,GAAAG,EAAA,CAAAH,QAAA;AAAAC,IAAAA,OAAA,GAAAG,wBAAA,CAAAD,EAAA,EAAAE,SAAA,CAAA;AAAAP,IAAAA,CAAA,MAAAD,EAAA;AAAAC,IAAAA,CAAA,MAAAE,QAAA;AAAAF,IAAAA,CAAA,MAAAG,OAAA;AAAAH,IAAAA,CAAA,MAAAI,IAAA;AAAA,GAAA,MAAA;AAAAF,IAAAA,QAAA,GAAAF,CAAA,CAAA,CAAA,CAAA;AAAAG,IAAAA,OAAA,GAAAH,CAAA,CAAA,CAAA,CAAA;AAAAI,IAAAA,IAAA,GAAAJ,CAAA,CAAA,CAAA,CAAA;AAAA;AAClC,EAAA,IAAAQ,qBAAA,GAA4BC,uBAAuB,CAACL,IAAI,EAAED,OAAO,CAAC;IAAAO,KAAA,GAAAF,qBAAA,CAAAE,KAAA;IAAAC,QAAA,GAAAH,qBAAA,CAAAG,QAAA;AAElE,EAAA,IAAI,CAACA,QAAqB,IAAtB,CAAcT,QAAQ,EAAA;AAAA,IAAA,OAClB,IAAI;AAAA;AACX,EAAA,IAAAU,EAAA;EAAA,IAAAZ,CAAA,CAAAE,CAAAA,CAAAA,KAAAA,QAAA,IAAAF,CAAA,QAAAU,KAAA,IAAAV,CAAA,CAAA,CAAA,CAAA,KAAAW,QAAA,EAAA;AAAA,IAAA,IAAAE,EAAA;AAAA,IAAA,IAAAb,CAAA,CAAAE,CAAAA,CAAAA,KAAAA,QAAA,IAAAF,CAAA,QAAAW,QAAA,EAAA;MAEgBE,EAAA,GAAA,SAAAA,GAAAC,MAAA,EAAA;QAAA,OAChBC,IAACC,QAAQ,EAAA;AAAAd,UAAAA,QAAA,EACPA,QAAQ,CACR,UAAAe,SAAA,EAAA;YAAA,OACCF,GAAA,CAACG,QAAQ,EAAC;AAAAJ,cAAAA,MAAA,EAAQA,MAAM;AAAAH,cAAAA,QAAA,EAAYA,QAAQ;AAAAR,cAAAA,OAAA,EAAWA;AAAW,aAAA,CAAA;AAAA,WAClE,EACDW,MAAM;WALOA,MAAM,CAOV;OACX;AAAAd,MAAAA,CAAA,MAAAE,QAAA;AAAAF,MAAAA,CAAA,MAAAW,QAAA;AAAAX,MAAAA,CAAA,OAAAa,EAAA;AAAA,KAAA,MAAA;AAAAA,MAAAA,EAAA,GAAAb,CAAA,CAAA,EAAA,CAAA;AAAA;AATMY,IAAAA,EAAA,GAAAF,KAAK,CAAAS,GAAI,CAACN,EAShB,CAAC;AAAAb,IAAAA,CAAA,MAAAE,QAAA;AAAAF,IAAAA,CAAA,MAAAU,KAAA;AAAAV,IAAAA,CAAA,MAAAW,QAAA;AAAAX,IAAAA,CAAA,MAAAY,EAAA;AAAA,GAAA,MAAA;AAAAA,IAAAA,EAAA,GAAAZ,CAAA,CAAA,CAAA,CAAA;AAAA;AAAA,EAAA,OATKY,EASL;AAAA;AAGH,IAAMM,QAAQ,GAIT,SAJCA,QAAQA,CAAAnB,EAAA,EAAA;AAIR,EAAA,IAAAe,MAAA,GAAAf,EAA6B,CAAAe,MAAA;IAAAH,QAAA,GAA7BZ,EAA6B,CAAAY,QAAA;IAAAR,OAAA,GAA7BJ,EAA6B,CAAAI,OAAA;AAClCiB,EAAAA,mBAAmB,CAACT,QAAQ,EAAEG,MAAM,EAAEX,OAAO,CAAC;AAAA,EAAA,OACvC,IAAI;AAAA,CACX;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Provider, type PeerId } from '@device-portal/client';
|
|
2
2
|
export type PeerOptions = {
|
|
3
3
|
value?: string;
|
|
4
4
|
onMessageFromConsumer?: (value: string) => void;
|
|
5
5
|
};
|
|
6
|
-
export declare const useDevicePortalPeer: (
|
|
6
|
+
export declare const useDevicePortalPeer: (provider: Provider, peerId: PeerId, options?: PeerOptions) => void;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { useRef, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
var useDevicePortalPeer = function useDevicePortalPeer(
|
|
3
|
+
var useDevicePortalPeer = function useDevicePortalPeer(provider, peerId) {
|
|
4
4
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
5
5
|
var onMessageFromConsumerRef = useRef(options.onMessageFromConsumer);
|
|
6
6
|
onMessageFromConsumerRef.current = options.onMessageFromConsumer;
|
|
7
7
|
useEffect(function () {
|
|
8
|
-
var unsubscribe =
|
|
8
|
+
var unsubscribe = provider.addPeerListener(peerId, function (value) {
|
|
9
9
|
var _onMessageFromConsume;
|
|
10
10
|
(_onMessageFromConsume = onMessageFromConsumerRef.current) === null || _onMessageFromConsume === void 0 || _onMessageFromConsume.call(onMessageFromConsumerRef, value);
|
|
11
11
|
});
|
|
12
12
|
return unsubscribe;
|
|
13
|
-
}, [
|
|
13
|
+
}, [provider, peerId]);
|
|
14
14
|
useEffect(function () {
|
|
15
15
|
if (options.value === undefined) {
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
}, [
|
|
18
|
+
provider.sendToPeer(peerId, options.value);
|
|
19
|
+
}, [provider, peerId, options.value]);
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export { useDevicePortalPeer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDevicePortalPeer.js","sources":["../../src/provider/useDevicePortalPeer.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useDevicePortalPeer.js","sources":["../../src/provider/useDevicePortalPeer.ts"],"sourcesContent":["import { Provider, type PeerId } from '@device-portal/client'\nimport { useEffect, useRef } from 'react'\n\nexport type PeerOptions = {\n\tvalue?: string\n\tonMessageFromConsumer?: (value: string) => void\n}\n\nexport const useDevicePortalPeer = (\n\tprovider: Provider,\n\tpeerId: PeerId,\n\toptions: PeerOptions = {},\n) => {\n\tconst onMessageFromConsumerRef = useRef(options.onMessageFromConsumer)\n\tonMessageFromConsumerRef.current = options.onMessageFromConsumer\n\n\tuseEffect(() => {\n\t\tconst unsubscribe = provider.addPeerListener(peerId, (value) => {\n\t\t\tonMessageFromConsumerRef.current?.(value)\n\t\t})\n\n\t\treturn unsubscribe\n\t}, [provider, peerId])\n\n\tuseEffect(() => {\n\t\tif (options.value === undefined) {\n\t\t\treturn\n\t\t}\n\t\tprovider.sendToPeer(peerId, options.value)\n\t}, [provider, peerId, options.value])\n}\n"],"names":["useDevicePortalPeer","provider","peerId","options","arguments","length","undefined","onMessageFromConsumerRef","useRef","onMessageFromConsumer","current","useEffect","unsubscribe","addPeerListener","value","_onMessageFromConsume","call","sendToPeer"],"mappings":";;AAQO,IAAMA,mBAAmB,GAAG,SAAtBA,mBAAmBA,CAC/BC,QAAkB,EAClBC,MAAc,EAEX;AAAA,EAAA,IADHC,OAAA,GAAAC,SAAA,CAAAC,MAAA,GAAA,CAAA,IAAAD,SAAA,CAAA,CAAA,CAAA,KAAAE,SAAA,GAAAF,SAAA,CAAA,CAAA,CAAA,GAAuB,EAAE;AAEzB,EAAA,IAAMG,wBAAwB,GAAGC,MAAM,CAACL,OAAO,CAACM,qBAAqB,CAAC;AACtEF,EAAAA,wBAAwB,CAACG,OAAO,GAAGP,OAAO,CAACM,qBAAqB;AAEhEE,EAAAA,SAAS,CAAC,YAAK;IACd,IAAMC,WAAW,GAAGX,QAAQ,CAACY,eAAe,CAACX,MAAM,EAAE,UAACY,KAAK,EAAI;AAAA,MAAA,IAAAC,qBAAA;AAC9D,MAAA,CAAAA,qBAAA,GAAAR,wBAAwB,CAACG,OAAO,MAAAK,IAAAA,IAAAA,qBAAA,KAAhCA,MAAAA,IAAAA,qBAAA,CAAAC,IAAA,CAAAT,wBAAwB,EAAWO,KAAK,CAAC;AAC1C,KAAC,CAAC;AAEF,IAAA,OAAOF,WAAW;AACnB,GAAC,EAAE,CAACX,QAAQ,EAAEC,MAAM,CAAC,CAAC;AAEtBS,EAAAA,SAAS,CAAC,YAAK;AACd,IAAA,IAAIR,OAAO,CAACW,KAAK,KAAKR,SAAS,EAAE;AAChC,MAAA;AACD;IACAL,QAAQ,CAACgB,UAAU,CAACf,MAAM,EAAEC,OAAO,CAACW,KAAK,CAAC;GAC1C,EAAE,CAACb,QAAQ,EAAEC,MAAM,EAAEC,OAAO,CAACW,KAAK,CAAC,CAAC;AACtC;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Provider, type BrowserDirectOption, type PeerId } from '@device-portal/client';
|
|
2
2
|
/**
|
|
3
3
|
* Configuration options for the Device Portal Provider.
|
|
4
4
|
*/
|
|
@@ -21,9 +21,9 @@ export type DevicePortalProviderOptions = {
|
|
|
21
21
|
*
|
|
22
22
|
* @param room - The unique room ID.
|
|
23
23
|
* @param options - Provider configuration options.
|
|
24
|
-
* @returns An object containing the list of connected peers and the underlying
|
|
24
|
+
* @returns An object containing the list of connected peers and the underlying Provider instance.
|
|
25
25
|
*/
|
|
26
26
|
export declare const useDevicePortalProvider: (room: string, options?: DevicePortalProviderOptions) => {
|
|
27
27
|
peers: PeerId[];
|
|
28
|
-
|
|
28
|
+
provider: Provider | null;
|
|
29
29
|
};
|
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
import { slicedToArray as _slicedToArray } from '../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import { generatePeerId, Provider } from '@device-portal/client';
|
|
2
3
|
import { useState, useRef, useEffect } from 'react';
|
|
3
|
-
import { Initiator } from '@device-portal/client';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A React hook that creates a Device Portal room and shares a value with all joining consumers.
|
|
7
7
|
*
|
|
8
8
|
* @param room - The unique room ID.
|
|
9
9
|
* @param options - Provider configuration options.
|
|
10
|
-
* @returns An object containing the list of connected peers and the underlying
|
|
10
|
+
* @returns An object containing the list of connected peers and the underlying Provider instance.
|
|
11
11
|
*/
|
|
12
12
|
var useDevicePortalProvider = function useDevicePortalProvider(room) {
|
|
13
|
+
var _options$sendLastValu;
|
|
13
14
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
14
15
|
var _useState = useState(null),
|
|
15
16
|
_useState2 = _slicedToArray(_useState, 2),
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
provider = _useState2[0],
|
|
18
|
+
setProvider = _useState2[1];
|
|
18
19
|
var _useState3 = useState([]),
|
|
19
20
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
20
21
|
peers = _useState4[0],
|
|
21
22
|
setPeers = _useState4[1];
|
|
22
23
|
var onMessageFromConsumerRef = useRef(options.onMessageFromConsumer);
|
|
23
24
|
onMessageFromConsumerRef.current = options.onMessageFromConsumer;
|
|
25
|
+
// Stable peer ID that survives React Strict Mode unmount/remount cycles
|
|
26
|
+
var peerIdRef = useRef(generatePeerId());
|
|
27
|
+
var lastSentValueRef = useRef(undefined);
|
|
28
|
+
var sendLastValueOnConnectAndReconnect = (_options$sendLastValu = options.sendLastValueOnConnectAndReconnect) !== null && _options$sendLastValu !== void 0 ? _options$sendLastValu : true;
|
|
24
29
|
useEffect(function () {
|
|
25
|
-
var
|
|
30
|
+
var newProvider = new Provider(room, {
|
|
26
31
|
onMessage: function onMessage(value, peerId) {
|
|
27
32
|
var _onMessageFromConsume;
|
|
28
33
|
(_onMessageFromConsume = onMessageFromConsumerRef.current) === null || _onMessageFromConsume === void 0 || _onMessageFromConsume.call(onMessageFromConsumerRef, value, peerId);
|
|
@@ -30,28 +35,34 @@ var useDevicePortalProvider = function useDevicePortalProvider(room) {
|
|
|
30
35
|
onPeersChange: function onPeersChange(peers_0) {
|
|
31
36
|
setPeers(peers_0);
|
|
32
37
|
},
|
|
33
|
-
|
|
38
|
+
onPeerConnected: function onPeerConnected() {
|
|
39
|
+
if (sendLastValueOnConnectAndReconnect && lastSentValueRef.current !== undefined) {
|
|
40
|
+
newProvider.send(lastSentValueRef.current);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
34
43
|
webSocketSignalingServer: options.webSocketSignalingServer,
|
|
35
44
|
maxClients: options.maxClients,
|
|
36
|
-
browserDirect: options.browserDirect
|
|
45
|
+
browserDirect: options.browserDirect,
|
|
46
|
+
peerId: peerIdRef.current
|
|
37
47
|
});
|
|
38
|
-
|
|
39
|
-
setPeers(
|
|
48
|
+
setProvider(newProvider);
|
|
49
|
+
setPeers(newProvider.peers);
|
|
40
50
|
return function () {
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
newProvider.destroy();
|
|
52
|
+
setProvider(null);
|
|
43
53
|
setPeers([]);
|
|
44
54
|
};
|
|
45
|
-
}, [room, options.webSocketSignalingServer, options.maxClients, options.browserDirect]);
|
|
55
|
+
}, [room, sendLastValueOnConnectAndReconnect, options.webSocketSignalingServer, options.maxClients, options.browserDirect]);
|
|
46
56
|
useEffect(function () {
|
|
47
57
|
if (options.value === undefined) {
|
|
48
58
|
return;
|
|
49
59
|
}
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
lastSentValueRef.current = options.value;
|
|
61
|
+
provider === null || provider === void 0 || provider.send(options.value);
|
|
62
|
+
}, [options.value, provider]);
|
|
52
63
|
return {
|
|
53
64
|
peers: peers,
|
|
54
|
-
|
|
65
|
+
provider: provider
|
|
55
66
|
};
|
|
56
67
|
};
|
|
57
68
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDevicePortalProvider.js","sources":["../../src/provider/useDevicePortalProvider.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"useDevicePortalProvider.js","sources":["../../src/provider/useDevicePortalProvider.ts"],"sourcesContent":["import {\n\tProvider,\n\tgeneratePeerId,\n\ttype BrowserDirectOption,\n\ttype PeerId,\n} from '@device-portal/client'\nimport { useEffect, useRef, useState } from 'react'\n\n// @TODO: warn if one room is used by multiple useDevicePortalProvider hooks more than once at the same time\n\n/**\n * Configuration options for the Device Portal Provider.\n */\nexport type DevicePortalProviderOptions = {\n\t/** The value to share with all connected consumers. */\n\tvalue?: string\n\t/** URL of the signaling server, or null to disable. */\n\twebSocketSignalingServer?: string | null\n\t/** Callback when a consumer sends a message back to the provider. */\n\tonMessageFromConsumer?: (value: string, peerId: PeerId) => void\n\t/** Whether to automatically send the last 'value' to new consumers. Default: true. */\n\tsendLastValueOnConnectAndReconnect?: boolean\n\t/** Maximum number of concurrent consumer connections. Default: 1. */\n\tmaxClients?: number\n\t/** Browser direct signaling options. */\n\tbrowserDirect?: BrowserDirectOption\n}\n\n/**\n * A React hook that creates a Device Portal room and shares a value with all joining consumers.\n *\n * @param room - The unique room ID.\n * @param options - Provider configuration options.\n * @returns An object containing the list of connected peers and the underlying Provider instance.\n */\nexport const useDevicePortalProvider = (\n\troom: string,\n\toptions: DevicePortalProviderOptions = {},\n) => {\n\tconst [provider, setProvider] = useState<Provider | null>(null)\n\tconst [peers, setPeers] = useState<PeerId[]>([])\n\tconst onMessageFromConsumerRef = useRef(options.onMessageFromConsumer)\n\tonMessageFromConsumerRef.current = options.onMessageFromConsumer\n\t// Stable peer ID that survives React Strict Mode unmount/remount cycles\n\tconst peerIdRef = useRef<PeerId>(generatePeerId())\n\tconst lastSentValueRef = useRef<string | undefined>(undefined)\n\tconst sendLastValueOnConnectAndReconnect =\n\t\toptions.sendLastValueOnConnectAndReconnect ?? true\n\n\tuseEffect(() => {\n\t\tconst newProvider = new Provider(room, {\n\t\t\tonMessage: (value, peerId) => {\n\t\t\t\tonMessageFromConsumerRef.current?.(value, peerId)\n\t\t\t},\n\t\t\tonPeersChange: (peers) => {\n\t\t\t\tsetPeers(peers)\n\t\t\t},\n\t\t\tonPeerConnected: () => {\n\t\t\t\tif (\n\t\t\t\t\tsendLastValueOnConnectAndReconnect &&\n\t\t\t\t\tlastSentValueRef.current !== undefined\n\t\t\t\t) {\n\t\t\t\t\tnewProvider.send(lastSentValueRef.current)\n\t\t\t\t}\n\t\t\t},\n\t\t\twebSocketSignalingServer: options.webSocketSignalingServer,\n\t\t\tmaxClients: options.maxClients,\n\t\t\tbrowserDirect: options.browserDirect,\n\t\t\tpeerId: peerIdRef.current,\n\t\t})\n\t\tsetProvider(newProvider)\n\t\tsetPeers(newProvider.peers)\n\n\t\treturn () => {\n\t\t\tnewProvider.destroy()\n\t\t\tsetProvider(null)\n\t\t\tsetPeers([])\n\t\t}\n\t}, [\n\t\troom,\n\t\tsendLastValueOnConnectAndReconnect,\n\t\toptions.webSocketSignalingServer,\n\t\toptions.maxClients,\n\t\toptions.browserDirect,\n\t])\n\n\tuseEffect(() => {\n\t\tif (options.value === undefined) {\n\t\t\treturn\n\t\t}\n\t\tlastSentValueRef.current = options.value\n\t\tprovider?.send(options.value)\n\t}, [options.value, provider])\n\n\treturn { peers, provider }\n}\n"],"names":["useDevicePortalProvider","room","_options$sendLastValu","options","arguments","length","undefined","_useState","useState","_useState2","_slicedToArray","provider","setProvider","_useState3","_useState4","peers","setPeers","onMessageFromConsumerRef","useRef","onMessageFromConsumer","current","peerIdRef","generatePeerId","lastSentValueRef","sendLastValueOnConnectAndReconnect","useEffect","newProvider","Provider","onMessage","value","peerId","_onMessageFromConsume","call","onPeersChange","onPeerConnected","send","webSocketSignalingServer","maxClients","browserDirect","destroy"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@device-portal/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "Simple WebRTC data channel for React.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"build": "npm run build:package && npm run build:storybook",
|
|
18
18
|
"dev:package": "rollup -c -w",
|
|
19
19
|
"build:package": "rollup -c",
|
|
20
|
-
"test:package": "
|
|
20
|
+
"test:package": "vitest run",
|
|
21
21
|
"dev:storybook": "storybook dev -p 7007",
|
|
22
22
|
"build:storybook": "storybook build"
|
|
23
23
|
},
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
},
|
|
37
37
|
"license": "ISC",
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"react": "
|
|
39
|
+
"react": ">=19"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@device-portal/client": "^0.0.
|
|
42
|
+
"@device-portal/client": "^0.0.20"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@babel/core": "^7.24.0",
|
|
@@ -55,9 +55,13 @@
|
|
|
55
55
|
"@storybook/react": "^10.2.10",
|
|
56
56
|
"@storybook/react-vite": "^10.2.10",
|
|
57
57
|
"@tanstack/react-query": "^5.59.20",
|
|
58
|
+
"@testing-library/dom": "^10.4.0",
|
|
59
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
60
|
+
"@testing-library/react": "^16.3.0",
|
|
58
61
|
"@types/react": "^19.2.14",
|
|
59
62
|
"@types/react-dom": "^19.2.3",
|
|
60
63
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
64
|
+
"jsdom": "^26.1.0",
|
|
61
65
|
"react": "^19.2.4",
|
|
62
66
|
"react-dom": "^19.2.4",
|
|
63
67
|
"rollup": "^4.2.0",
|
|
@@ -68,7 +72,8 @@
|
|
|
68
72
|
"rollup-plugin-typescript2": "^0.36.0",
|
|
69
73
|
"storybook": "^10.2.10",
|
|
70
74
|
"typescript": "^5.9.3",
|
|
71
|
-
"vite": "^5.0.0"
|
|
75
|
+
"vite": "^5.0.0",
|
|
76
|
+
"vitest": "^3.1.1"
|
|
72
77
|
},
|
|
73
78
|
"files": [
|
|
74
79
|
"/dist/"
|