@device-portal/react 0.0.16 → 0.0.18

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/README.md CHANGED
@@ -32,7 +32,7 @@ const AppA = () => {
32
32
  const [value, setValue] = useState(0)
33
33
  useDevicePortalProvider('my-test-room', {
34
34
  value: value.toString(),
35
- websocketSignalingServer: 'wss://device-portal.filipchalupa.cz',
35
+ webSocketSignalingServer: 'wss://device-portal.filipchalupa.cz',
36
36
  })
37
37
 
38
38
  return (
@@ -69,7 +69,7 @@ const AppB = () => {
69
69
 
70
70
  const ConsumerComponent = () => {
71
71
  const { value } = useDevicePortalConsumer('my-test-room', {
72
- websocketSignalingServer: 'wss://device-portal.filipchalupa.cz',
72
+ webSocketSignalingServer: 'wss://device-portal.filipchalupa.cz',
73
73
  })
74
74
 
75
75
  return (
@@ -157,24 +157,26 @@ const ConsumerComponent = () => {
157
157
 
158
158
  The WebRTC connection is designed to be resilient. If the connection to the signaling server is temporarily lost, any established peer-to-peer connections will remain active. The client will attempt to reconnect to the signaling server in the background to handle any future connection negotiations.
159
159
 
160
- ### Local Signaling Shortcut
160
+ ### Browser Direct Communication
161
161
 
162
- When peers are in the same browser (different tabs or same tab), the library automatically uses `BroadcastChannel` for signaling. This results in a near-instant connection and reduces reliance on the external signaling server.
162
+ When peers are in the same browser (different tabs or same tab), the library automatically uses direct browser APIs for communication instead of WebRTC. This results in a near-instant connection and works offline.
163
163
 
164
- ### Local Only Mode
164
+ By default, `browserDirect` is `true`.
165
165
 
166
- For privacy or offline-only applications where you know all peers are on the same local device/browser, you can disable external signaling entirely by passing `localDeviceOnly: true`:
166
+ ### Browser Direct Only Mode
167
+
168
+ For privacy or offline-only applications where you know all peers are on the same browser, you can disable external signaling entirely by passing `webSocketSignalingServer: null`:
167
169
 
168
170
  ```jsx
169
171
  useDevicePortalProvider('my-room', {
170
172
  value: 'secret-data',
171
- localDeviceOnly: true,
173
+ webSocketSignalingServer: null,
172
174
  })
173
175
 
174
176
  // ...
175
177
 
176
178
  useDevicePortalConsumer('my-room', {
177
- localDeviceOnly: true,
179
+ webSocketSignalingServer: null,
178
180
  })
179
181
  ```
180
182
 
@@ -1,8 +1,9 @@
1
1
  import { type FunctionComponent, type ReactNode } from 'react';
2
+ import { type BrowserDirectOption } from '@device-portal/client';
2
3
  export declare const DevicePortalConsumer: FunctionComponent<{
3
4
  room: string;
4
- websocketSignalingServer?: string;
5
- localDeviceOnly?: boolean;
5
+ webSocketSignalingServer?: string | null;
6
+ browserDirect?: BrowserDirectOption;
6
7
  children: (data: {
7
8
  value: string;
8
9
  sendMessageToProvider: (message: string) => void;
@@ -5,17 +5,17 @@ import { useDevicePortalConsumer } from './useDevicePortalConsumer.js';
5
5
  var DevicePortalConsumer = function DevicePortalConsumer(t0) {
6
6
  var $ = c(7);
7
7
  var room = t0.room,
8
- websocketSignalingServer = t0.websocketSignalingServer,
9
- localDeviceOnly = t0.localDeviceOnly,
8
+ webSocketSignalingServer = t0.webSocketSignalingServer,
9
+ browserDirect = t0.browserDirect,
10
10
  children = t0.children;
11
11
  var t1;
12
- if ($[0] !== localDeviceOnly || $[1] !== websocketSignalingServer) {
12
+ if ($[0] !== browserDirect || $[1] !== webSocketSignalingServer) {
13
13
  t1 = {
14
- websocketSignalingServer: websocketSignalingServer,
15
- localDeviceOnly: localDeviceOnly
14
+ webSocketSignalingServer: webSocketSignalingServer,
15
+ browserDirect: browserDirect
16
16
  };
17
- $[0] = localDeviceOnly;
18
- $[1] = websocketSignalingServer;
17
+ $[0] = browserDirect;
18
+ $[1] = webSocketSignalingServer;
19
19
  $[2] = t1;
20
20
  } else {
21
21
  t1 = $[2];
@@ -1 +1 @@
1
- {"version":3,"file":"DevicePortalConsumer.js","sources":["../../src/consumer/DevicePortalConsumer.tsx"],"sourcesContent":["import { type FunctionComponent, type ReactNode } from 'react'\nimport { useDevicePortalConsumer } from './useDevicePortalConsumer'\n\nexport const DevicePortalConsumer: FunctionComponent<{\n\troom: string\n\twebsocketSignalingServer?: string\n\tlocalDeviceOnly?: boolean\n\tchildren: (data: {\n\t\tvalue: string\n\t\tsendMessageToProvider: (message: string) => void\n\t}) => ReactNode\n}> = ({ room, websocketSignalingServer, localDeviceOnly, children }) => {\n\tconst { value, sendMessageToProvider } = useDevicePortalConsumer(room, {\n\t\twebsocketSignalingServer,\n\t\tlocalDeviceOnly,\n\t})\n\treturn <>{children({ value, sendMessageToProvider })}</>\n}\n"],"names":["DevicePortalConsumer","t0","$","_c","room","websocketSignalingServer","localDeviceOnly","children","t1","_useDevicePortalConsu","useDevicePortalConsumer","value","sendMessageToProvider","t2","_jsx","_Fragment"],"mappings":";;;;IAGaA,oBAAoB,GAQ5B,SARQA,oBAAoBA,CAAAC,EAAA,EAAA;EAAA,IAAAC,CAAA,GAAAC,CAAA,CAAA,CAAA,CAAA;AAQ3B,EAAA,IAAAC,IAAA,GAAAH,EAA6D,CAAAG,IAAA;IAAAC,wBAAA,GAA7DJ,EAA6D,CAAAI,wBAAA;IAAAC,eAAA,GAA7DL,EAA6D,CAAAK,eAAA;IAAAC,QAAA,GAA7DN,EAA6D,CAAAM,QAAA;AAAA,EAAA,IAAAC,EAAA;AAAA,EAAA,IAAAN,CAAA,CAAAI,CAAAA,CAAAA,KAAAA,eAAA,IAAAJ,CAAA,QAAAG,wBAAA,EAAA;IACKG,EAAA,GAAA;AAAAH,MAAAA,wBAAA,EACtEA,wBAAwB;AAAAC,MAAAA,eAAA,EACxBA;KACA;AAAAJ,IAAAA,CAAA,MAAAI,eAAA;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
+ {"version":3,"file":"DevicePortalConsumer.js","sources":["../../src/consumer/DevicePortalConsumer.tsx"],"sourcesContent":["import { type FunctionComponent, type ReactNode } from 'react'\nimport { type BrowserDirectOption } from '@device-portal/client'\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,10 +1,27 @@
1
+ import { type BrowserDirectOption } from '@device-portal/client';
1
2
  type State = {
2
3
  room: string;
3
4
  value: string;
4
5
  sendMessageToProvider: (value: string) => void;
5
6
  };
6
- export declare const useDevicePortalConsumer: (room: string, options?: {
7
- websocketSignalingServer?: string;
8
- localDeviceOnly?: boolean;
9
- }) => Pick<State, "value" | "sendMessageToProvider">;
7
+ /**
8
+ * Configuration options for the Device Portal Consumer.
9
+ */
10
+ export type DevicePortalConsumerOptions = {
11
+ /** URL of the signaling server, or null to disable. */
12
+ webSocketSignalingServer?: string | null;
13
+ /** Browser direct signaling options. */
14
+ browserDirect?: BrowserDirectOption;
15
+ /** Whether to automatically send the last 'send()' value back to the provider on reconnect. Default: false. */
16
+ sendLastValueOnConnectAndReconnect?: boolean;
17
+ };
18
+ /**
19
+ * A React hook that joins a Device Portal room and receives value from the provider.
20
+ * This hook suspends the component until the first value is received.
21
+ *
22
+ * @param room - The unique room ID.
23
+ * @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
+ export declare const useDevicePortalConsumer: (room: string, options?: DevicePortalConsumerOptions) => Pick<State, "value" | "sendMessageToProvider">;
10
27
  export {};
@@ -1,86 +1,115 @@
1
1
  import { slicedToArray as _slicedToArray, createForOfIteratorHelper as _createForOfIteratorHelper } from '../_virtual/_rollupPluginBabelHelpers.js';
2
- import { useState, useMemo } from 'react';
2
+ import { useState, useEffect } from 'react';
3
3
  import { Responder } from '@device-portal/client';
4
4
 
5
+ /** Polyfill/helper for Promise.withResolvers */
6
+ var withResolvers = function withResolvers() {
7
+ if (Promise.withResolvers) {
8
+ return Promise.withResolvers();
9
+ }
10
+ var resolve;
11
+ var reject;
12
+ var promise = new Promise(function (res, rej) {
13
+ resolve = res;
14
+ reject = rej;
15
+ });
16
+ return {
17
+ promise: promise,
18
+ resolve: resolve,
19
+ reject: reject
20
+ };
21
+ };
5
22
  var responders = {};
23
+ /**
24
+ * A React hook that joins a Device Portal room and receives value from the provider.
25
+ * This hook suspends the component until the first value is received.
26
+ *
27
+ * @param room - The unique room ID.
28
+ * @param options - Consumer configuration options.
29
+ * @returns An object containing the current value and a function to send messages back to the provider.
30
+ */
6
31
  var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
7
32
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
8
33
  var _useState = useState(null),
9
34
  _useState2 = _slicedToArray(_useState, 2),
10
35
  valueState = _useState2[0],
11
36
  setValueState = _useState2[1];
12
- var currentConsumer = useMemo(function () {
13
- if (!responders[room]) {
14
- var _Promise$withResolver, _Promise$withResolver2;
15
- var withResolvers = function withResolvers() {
16
- var resolve;
17
- var reject;
18
- var promise = new Promise(function (res, rej) {
19
- resolve = res;
20
- reject = rej;
21
- });
22
- return {
23
- promise: promise,
24
- resolve: resolve,
25
- reject: reject
37
+ // Initialize the responder synchronously if it doesn't exist
38
+ if (!responders[room]) {
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
26
51
  };
27
- };
28
- var _ref = (_Promise$withResolver = (_Promise$withResolver2 = Promise.withResolvers) === null || _Promise$withResolver2 === void 0 ? void 0 : _Promise$withResolver2.call(Promise)) !== null && _Promise$withResolver !== void 0 ? _Promise$withResolver : withResolvers(),
29
- firstValuePromise = _ref.promise,
30
- firstValueResolve = _ref.resolve;
31
- var sendMessageToProvider = function sendMessageToProvider(value) {
32
- responders[room].responder.send(value);
33
- };
34
- var responder = new Responder(room, {
35
- onMessage: function onMessage(value_0, peerId) {
36
- var consumer = {
37
- value: value_0,
38
- sendMessageToProvider: sendMessageToProvider
39
- };
40
- responders[room].consumer = consumer;
41
- var _iterator = _createForOfIteratorHelper(responders[room].setValueStates),
42
- _step;
43
- try {
44
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
45
- var setState = _step.value;
46
- setState({
47
- room: room,
48
- value: value_0,
49
- sendMessageToProvider: sendMessageToProvider
50
- });
51
- }
52
- } catch (err) {
53
- _iterator.e(err);
54
- } finally {
55
- _iterator.f();
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
+ });
56
67
  }
57
- firstValueResolve(value_0);
58
- },
59
- sendLastValueOnConnectAndReconnect: false,
60
- websocketSignalingServer: options.websocketSignalingServer,
61
- localDeviceOnly: options.localDeviceOnly
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
+ }
85
+ useEffect(function () {
86
+ var roomEntry = responders[room];
87
+ roomEntry.setValueStates.add(setValueState);
88
+ // If we already have a value, sync it immediately on mount
89
+ if (roomEntry.consumer) {
90
+ var consumer_0 = roomEntry.consumer;
91
+ setValueState({
92
+ room: room,
93
+ value: consumer_0.value,
94
+ sendMessageToProvider: consumer_0.sendMessageToProvider
62
95
  });
63
- responders[room] = {
64
- responder: responder,
65
- firstValuePromise: firstValuePromise,
66
- consumer: null,
67
- setValueStates: new Set()
68
- };
69
96
  }
70
- responders[room].setValueStates.add(setValueState);
71
- return responders[room].consumer;
72
- }, [room, options.websocketSignalingServer, options.localDeviceOnly, setValueState]);
73
- // Cleanup on unmount (though this hook is currently designed for global persistence)
74
- // useEffect(() => () => { responders[room]?.setValueStates.delete(setValueState) }, [room, setValueState]);
97
+ return function () {
98
+ roomEntry.setValueStates["delete"](setValueState);
99
+ };
100
+ }, [room, setValueState]);
101
+ // 1. If we have local state, use it
75
102
  if (valueState && valueState.room === room) {
76
103
  return {
77
104
  value: valueState.value,
78
105
  sendMessageToProvider: valueState.sendMessageToProvider
79
106
  };
80
107
  }
81
- if (currentConsumer) {
82
- return currentConsumer;
108
+ // 2. If the shared responder already has a value, use it (handles re-renders after suspension)
109
+ if (responders[room].consumer) {
110
+ return responders[room].consumer;
83
111
  }
112
+ // 3. Otherwise, suspend on the first value promise
84
113
  throw responders[room].firstValuePromise;
85
114
  };
86
115
 
@@ -1 +1 @@
1
- {"version":3,"file":"useDevicePortalConsumer.js","sources":["../../src/consumer/useDevicePortalConsumer.ts"],"sourcesContent":["import { useMemo, useState, type Dispatch, type SetStateAction } from 'react'\nimport { Responder } from '@device-portal/client'\n\ntype State = {\n\troom: string\n\tvalue: string\n\tsendMessageToProvider: (value: string) => void\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\nexport const useDevicePortalConsumer = (\n\troom: string,\n\toptions: {\n\t\twebsocketSignalingServer?: string\n\t\tlocalDeviceOnly?: boolean\n\t} = {},\n): Pick<State, 'value' | 'sendMessageToProvider'> => {\n\tconst [valueState, setValueState] = useState<State | null>(null)\n\n\tconst currentConsumer = useMemo(() => {\n\t\tif (!responders[room]) {\n\t\t\tconsole.log(\n\t\t\t\t`[useDevicePortalConsumer] Creating new Responder for room: ${room}`,\n\t\t\t)\n\t\t\tconst withResolvers = () => {\n\t\t\t\tlet resolve: (value: string) => void\n\t\t\t\tlet reject: (reason?: any) => void\n\t\t\t\tconst promise = new Promise<string>((res, rej) => {\n\t\t\t\t\tresolve = res\n\t\t\t\t\treject = rej\n\t\t\t\t})\n\t\t\t\treturn { promise, resolve: resolve!, reject: reject! }\n\t\t\t}\n\n\t\t\tconst { promise: firstValuePromise, resolve: firstValueResolve } =\n\t\t\t\t(Promise as any).withResolvers?.() ?? withResolvers()\n\n\t\t\tconst sendMessageToProvider = (value: string) => {\n\t\t\t\tresponders[room].responder.send(value)\n\t\t\t}\n\n\t\t\tconst responder = new Responder(room, {\n\t\t\t\tonMessage: (value, peerId) => {\n\t\t\t\t\tconst consumer = { value, sendMessageToProvider }\n\t\t\t\t\tresponders[room].consumer = consumer\n\t\t\t\t\tfor (const setState of responders[room].setValueStates) {\n\t\t\t\t\t\tsetState({ room, value, sendMessageToProvider })\n\t\t\t\t\t}\n\t\t\t\t\tfirstValueResolve(value)\n\t\t\t\t},\n\t\t\t\tsendLastValueOnConnectAndReconnect: false,\n\t\t\t\twebsocketSignalingServer: options.websocketSignalingServer,\n\t\t\t\tlocalDeviceOnly: options.localDeviceOnly,\n\t\t\t})\n\n\t\t\tresponders[room] = {\n\t\t\t\tresponder,\n\t\t\t\tfirstValuePromise,\n\t\t\t\tconsumer: null,\n\t\t\t\tsetValueStates: new Set(),\n\t\t\t}\n\t\t}\n\n\t\tresponders[room].setValueStates.add(setValueState)\n\n\t\treturn responders[room].consumer\n\t}, [\n\t\troom,\n\t\toptions.websocketSignalingServer,\n\t\toptions.localDeviceOnly,\n\t\tsetValueState,\n\t])\n\n\t// Cleanup on unmount (though this hook is currently designed for global persistence)\n\t// useEffect(() => () => { responders[room]?.setValueStates.delete(setValueState) }, [room, setValueState]);\n\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\tif (currentConsumer) {\n\t\treturn currentConsumer\n\t}\n\n\tthrow responders[room].firstValuePromise\n}\n"],"names":["responders","useDevicePortalConsumer","room","options","arguments","length","undefined","_useState","useState","_useState2","_slicedToArray","valueState","setValueState","currentConsumer","useMemo","_Promise$withResolver","_Promise$withResolver2","withResolvers","resolve","reject","promise","Promise","res","rej","_ref","call","firstValuePromise","firstValueResolve","sendMessageToProvider","value","responder","send","Responder","onMessage","peerId","consumer","_iterator","_createForOfIteratorHelper","setValueStates","_step","s","n","done","setState","err","e","f","sendLastValueOnConnectAndReconnect","websocketSignalingServer","localDeviceOnly","Set","add"],"mappings":"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"}
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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"}
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 } from '@device-portal/client';
6
+ export { type PeerId, type Initiator } from '@device-portal/client';
@@ -1,11 +1,28 @@
1
- import { Initiator, type PeerId } from '@device-portal/client';
1
+ import { Initiator, type PeerId, type BrowserDirectOption } from '@device-portal/client';
2
+ /**
3
+ * Configuration options for the Device Portal Provider.
4
+ */
2
5
  export type DevicePortalProviderOptions = {
6
+ /** The value to share with all connected consumers. */
3
7
  value?: string;
4
- websocketSignalingServer?: string;
8
+ /** URL of the signaling server, or null to disable. */
9
+ webSocketSignalingServer?: string | null;
10
+ /** Callback when a consumer sends a message back to the provider. */
5
11
  onMessageFromConsumer?: (value: string, peerId: PeerId) => void;
12
+ /** Whether to automatically send the last 'value' to new consumers. Default: true. */
13
+ sendLastValueOnConnectAndReconnect?: boolean;
14
+ /** Maximum number of concurrent consumer connections. Default: 1. */
6
15
  maxClients?: number;
7
- localDeviceOnly?: boolean;
16
+ /** Browser direct signaling options. */
17
+ browserDirect?: BrowserDirectOption;
8
18
  };
19
+ /**
20
+ * A React hook that creates a Device Portal room and shares a value with all joining consumers.
21
+ *
22
+ * @param room - The unique room ID.
23
+ * @param options - Provider configuration options.
24
+ * @returns An object containing the list of connected peers and the underlying Initiator instance.
25
+ */
9
26
  export declare const useDevicePortalProvider: (room: string, options?: DevicePortalProviderOptions) => {
10
27
  peers: PeerId[];
11
28
  initiator: Initiator | null;
@@ -2,6 +2,13 @@ import { slicedToArray as _slicedToArray } from '../_virtual/_rollupPluginBabelH
2
2
  import { useState, useRef, useEffect } from 'react';
3
3
  import { Initiator } from '@device-portal/client';
4
4
 
5
+ /**
6
+ * A React hook that creates a Device Portal room and shares a value with all joining consumers.
7
+ *
8
+ * @param room - The unique room ID.
9
+ * @param options - Provider configuration options.
10
+ * @returns An object containing the list of connected peers and the underlying Initiator instance.
11
+ */
5
12
  var useDevicePortalProvider = function useDevicePortalProvider(room) {
6
13
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
7
14
  var _useState = useState(null),
@@ -23,9 +30,10 @@ var useDevicePortalProvider = function useDevicePortalProvider(room) {
23
30
  onPeersChange: function onPeersChange(peers_0) {
24
31
  setPeers(peers_0);
25
32
  },
26
- websocketSignalingServer: options.websocketSignalingServer,
33
+ sendLastValueOnConnectAndReconnect: options.sendLastValueOnConnectAndReconnect,
34
+ webSocketSignalingServer: options.webSocketSignalingServer,
27
35
  maxClients: options.maxClients,
28
- localDeviceOnly: options.localDeviceOnly
36
+ browserDirect: options.browserDirect
29
37
  });
30
38
  setInitiator(initiator_0);
31
39
  setPeers(initiator_0.peers);
@@ -34,7 +42,7 @@ var useDevicePortalProvider = function useDevicePortalProvider(room) {
34
42
  setInitiator(null);
35
43
  setPeers([]);
36
44
  };
37
- }, [room, options.websocketSignalingServer, options.maxClients, options.localDeviceOnly]);
45
+ }, [room, options.webSocketSignalingServer, options.maxClients, options.browserDirect]);
38
46
  useEffect(function () {
39
47
  if (options.value === undefined) {
40
48
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"useDevicePortalProvider.js","sources":["../../src/provider/useDevicePortalProvider.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { Initiator, type PeerId } from '@device-portal/client'\n\n// @TODO: warn if one room is used by multiple useDevicePortalProvider hooks more than once at the same time\n\nexport type DevicePortalProviderOptions = {\n\tvalue?: string\n\twebsocketSignalingServer?: string\n\tonMessageFromConsumer?: (value: string, peerId: PeerId) => void\n\tmaxClients?: number\n\tlocalDeviceOnly?: boolean\n}\n\nexport const useDevicePortalProvider = (\n\troom: string,\n\toptions: DevicePortalProviderOptions = {},\n) => {\n\tconst [initiator, setInitiator] = useState<Initiator | null>(null)\n\tconst [peers, setPeers] = useState<PeerId[]>([])\n\tconst onMessageFromConsumerRef = useRef(options.onMessageFromConsumer)\n\tonMessageFromConsumerRef.current = options.onMessageFromConsumer\n\n\tuseEffect(() => {\n\t\tconst initiator = new Initiator(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\twebsocketSignalingServer: options.websocketSignalingServer,\n\t\t\tmaxClients: options.maxClients,\n\t\t\tlocalDeviceOnly: options.localDeviceOnly,\n\t\t})\n\t\tsetInitiator(initiator)\n\t\tsetPeers(initiator.peers)\n\n\t\treturn () => {\n\t\t\tinitiator.destroy()\n\t\t\tsetInitiator(null)\n\t\t\tsetPeers([])\n\t\t}\n\t}, [\n\t\troom,\n\t\toptions.websocketSignalingServer,\n\t\toptions.maxClients,\n\t\toptions.localDeviceOnly,\n\t])\n\n\tuseEffect(() => {\n\t\tif (options.value === undefined) {\n\t\t\treturn\n\t\t}\n\t\tinitiator?.send(options.value)\n\t}, [options.value, initiator])\n\n\treturn { peers, initiator }\n}\n"],"names":["useDevicePortalProvider","room","options","arguments","length","undefined","_useState","useState","_useState2","_slicedToArray","initiator","setInitiator","_useState3","_useState4","peers","setPeers","onMessageFromConsumerRef","useRef","onMessageFromConsumer","current","useEffect","Initiator","onMessage","value","peerId","_onMessageFromConsume","call","onPeersChange","websocketSignalingServer","maxClients","localDeviceOnly","destroy","send"],"mappings":";;;;IAaaA,uBAAuB,GAAG,SAA1BA,uBAAuBA,CACnCC,IAAY,EAET;AAAA,EAAA,IADHC,OAAA,GAAAC,SAAA,CAAAC,MAAA,GAAA,CAAA,IAAAD,SAAA,CAAA,CAAA,CAAA,KAAAE,SAAA,GAAAF,SAAA,CAAA,CAAA,CAAA,GAAuC,EAAE;AAEzC,EAAA,IAAAG,SAAA,GAAkCC,QAAQ,CAAmB,IAAI,CAAC;IAAAC,UAAA,GAAAC,cAAA,CAAAH,SAAA,EAAA,CAAA,CAAA;AAA3DI,IAAAA,SAAS,GAAAF,UAAA,CAAA,CAAA,CAAA;AAAEG,IAAAA,YAAY,GAAAH,UAAA,CAAA,CAAA,CAAA;AAC9B,EAAA,IAAAI,UAAA,GAA0BL,QAAQ,CAAW,EAAE,CAAC;IAAAM,UAAA,GAAAJ,cAAA,CAAAG,UAAA,EAAA,CAAA,CAAA;AAAzCE,IAAAA,KAAK,GAAAD,UAAA,CAAA,CAAA,CAAA;AAAEE,IAAAA,QAAQ,GAAAF,UAAA,CAAA,CAAA,CAAA;AACtB,EAAA,IAAMG,wBAAwB,GAAGC,MAAM,CAACf,OAAO,CAACgB,qBAAqB,CAAC;AACtEF,EAAAA,wBAAwB,CAACG,OAAO,GAAGjB,OAAO,CAACgB,qBAAqB;AAEhEE,EAAAA,SAAS,CAAC,YAAK;AACd,IAAA,IAAMV,WAAS,GAAG,IAAIW,SAAS,CAACpB,IAAI,EAAE;AACrCqB,MAAAA,SAAS,EAAE,SAAXA,SAASA,CAAGC,KAAK,EAAEC,MAAM,EAAI;AAAA,QAAA,IAAAC,qBAAA;AAC5B,QAAA,CAAAA,qBAAA,GAAAT,wBAAwB,CAACG,OAAO,cAAAM,qBAAA,KAAA,MAAA,IAAhCA,qBAAA,CAAAC,IAAA,CAAAV,wBAAwB,EAAWO,KAAK,EAAEC,MAAM,CAAC;OACjD;AACDG,MAAAA,aAAa,EAAE,SAAfA,aAAaA,CAAGb,OAAK,EAAI;QACxBC,QAAQ,CAACD,OAAK,CAAC;OACf;MACDc,wBAAwB,EAAE1B,OAAO,CAAC0B,wBAAwB;MAC1DC,UAAU,EAAE3B,OAAO,CAAC2B,UAAU;MAC9BC,eAAe,EAAE5B,OAAO,CAAC4B;AACzB,KAAA,CAAC;IACFnB,YAAY,CAACD,WAAS,CAAC;AACvBK,IAAAA,QAAQ,CAACL,WAAS,CAACI,KAAK,CAAC;AAEzB,IAAA,OAAO,YAAK;MACXJ,WAAS,CAACqB,OAAO,EAAE;MACnBpB,YAAY,CAAC,IAAI,CAAC;MAClBI,QAAQ,CAAC,EAAE,CAAC;KACZ;AACF,GAAC,EAAE,CACFd,IAAI,EACJC,OAAO,CAAC0B,wBAAwB,EAChC1B,OAAO,CAAC2B,UAAU,EAClB3B,OAAO,CAAC4B,eAAe,CACvB,CAAC;AAEFV,EAAAA,SAAS,CAAC,YAAK;AACd,IAAA,IAAIlB,OAAO,CAACqB,KAAK,KAAKlB,SAAS,EAAE;AAChC,MAAA;AACD;IACAK,SAAS,KAAA,IAAA,IAATA,SAAS,KAAA,MAAA,IAATA,SAAS,CAAEsB,IAAI,CAAC9B,OAAO,CAACqB,KAAK,CAAC;GAC9B,EAAE,CAACrB,OAAO,CAACqB,KAAK,EAAEb,SAAS,CAAC,CAAC;EAE9B,OAAO;AAAEI,IAAAA,KAAK,EAALA,KAAK;AAAEJ,IAAAA,SAAS,EAATA;GAAW;AAC5B;;;;"}
1
+ {"version":3,"file":"useDevicePortalProvider.js","sources":["../../src/provider/useDevicePortalProvider.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport {\n\tInitiator,\n\ttype PeerId,\n\ttype BrowserDirectOption,\n} from '@device-portal/client'\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 Initiator instance.\n */\nexport const useDevicePortalProvider = (\n\troom: string,\n\toptions: DevicePortalProviderOptions = {},\n) => {\n\tconst [initiator, setInitiator] = useState<Initiator | null>(null)\n\tconst [peers, setPeers] = useState<PeerId[]>([])\n\tconst onMessageFromConsumerRef = useRef(options.onMessageFromConsumer)\n\tonMessageFromConsumerRef.current = options.onMessageFromConsumer\n\n\tuseEffect(() => {\n\t\tconst initiator = new Initiator(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\tsendLastValueOnConnectAndReconnect:\n\t\t\t\toptions.sendLastValueOnConnectAndReconnect,\n\t\t\twebSocketSignalingServer: options.webSocketSignalingServer,\n\t\t\tmaxClients: options.maxClients,\n\t\t\tbrowserDirect: options.browserDirect,\n\t\t})\n\t\tsetInitiator(initiator)\n\t\tsetPeers(initiator.peers)\n\n\t\treturn () => {\n\t\t\tinitiator.destroy()\n\t\t\tsetInitiator(null)\n\t\t\tsetPeers([])\n\t\t}\n\t}, [\n\t\troom,\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\tinitiator?.send(options.value)\n\t}, [options.value, initiator])\n\n\treturn { peers, initiator }\n}\n"],"names":["useDevicePortalProvider","room","options","arguments","length","undefined","_useState","useState","_useState2","_slicedToArray","initiator","setInitiator","_useState3","_useState4","peers","setPeers","onMessageFromConsumerRef","useRef","onMessageFromConsumer","current","useEffect","Initiator","onMessage","value","peerId","_onMessageFromConsume","call","onPeersChange","sendLastValueOnConnectAndReconnect","webSocketSignalingServer","maxClients","browserDirect","destroy","send"],"mappings":";;;;AA2BA;;;;;;AAMG;IACUA,uBAAuB,GAAG,SAA1BA,uBAAuBA,CACnCC,IAAY,EAET;AAAA,EAAA,IADHC,OAAA,GAAAC,SAAA,CAAAC,MAAA,GAAA,CAAA,IAAAD,SAAA,CAAA,CAAA,CAAA,KAAAE,SAAA,GAAAF,SAAA,CAAA,CAAA,CAAA,GAAuC,EAAE;AAEzC,EAAA,IAAAG,SAAA,GAAkCC,QAAQ,CAAmB,IAAI,CAAC;IAAAC,UAAA,GAAAC,cAAA,CAAAH,SAAA,EAAA,CAAA,CAAA;AAA3DI,IAAAA,SAAS,GAAAF,UAAA,CAAA,CAAA,CAAA;AAAEG,IAAAA,YAAY,GAAAH,UAAA,CAAA,CAAA,CAAA;AAC9B,EAAA,IAAAI,UAAA,GAA0BL,QAAQ,CAAW,EAAE,CAAC;IAAAM,UAAA,GAAAJ,cAAA,CAAAG,UAAA,EAAA,CAAA,CAAA;AAAzCE,IAAAA,KAAK,GAAAD,UAAA,CAAA,CAAA,CAAA;AAAEE,IAAAA,QAAQ,GAAAF,UAAA,CAAA,CAAA,CAAA;AACtB,EAAA,IAAMG,wBAAwB,GAAGC,MAAM,CAACf,OAAO,CAACgB,qBAAqB,CAAC;AACtEF,EAAAA,wBAAwB,CAACG,OAAO,GAAGjB,OAAO,CAACgB,qBAAqB;AAEhEE,EAAAA,SAAS,CAAC,YAAK;AACd,IAAA,IAAMV,WAAS,GAAG,IAAIW,SAAS,CAACpB,IAAI,EAAE;AACrCqB,MAAAA,SAAS,EAAE,SAAXA,SAASA,CAAGC,KAAK,EAAEC,MAAM,EAAI;AAAA,QAAA,IAAAC,qBAAA;AAC5B,QAAA,CAAAA,qBAAA,GAAAT,wBAAwB,CAACG,OAAO,cAAAM,qBAAA,KAAA,MAAA,IAAhCA,qBAAA,CAAAC,IAAA,CAAAV,wBAAwB,EAAWO,KAAK,EAAEC,MAAM,CAAC;OACjD;AACDG,MAAAA,aAAa,EAAE,SAAfA,aAAaA,CAAGb,OAAK,EAAI;QACxBC,QAAQ,CAACD,OAAK,CAAC;OACf;MACDc,kCAAkC,EACjC1B,OAAO,CAAC0B,kCAAkC;MAC3CC,wBAAwB,EAAE3B,OAAO,CAAC2B,wBAAwB;MAC1DC,UAAU,EAAE5B,OAAO,CAAC4B,UAAU;MAC9BC,aAAa,EAAE7B,OAAO,CAAC6B;AACvB,KAAA,CAAC;IACFpB,YAAY,CAACD,WAAS,CAAC;AACvBK,IAAAA,QAAQ,CAACL,WAAS,CAACI,KAAK,CAAC;AAEzB,IAAA,OAAO,YAAK;MACXJ,WAAS,CAACsB,OAAO,EAAE;MACnBrB,YAAY,CAAC,IAAI,CAAC;MAClBI,QAAQ,CAAC,EAAE,CAAC;KACZ;AACF,GAAC,EAAE,CACFd,IAAI,EACJC,OAAO,CAAC2B,wBAAwB,EAChC3B,OAAO,CAAC4B,UAAU,EAClB5B,OAAO,CAAC6B,aAAa,CACrB,CAAC;AAEFX,EAAAA,SAAS,CAAC,YAAK;AACd,IAAA,IAAIlB,OAAO,CAACqB,KAAK,KAAKlB,SAAS,EAAE;AAChC,MAAA;AACD;IACAK,SAAS,KAAA,IAAA,IAATA,SAAS,KAAA,MAAA,IAATA,SAAS,CAAEuB,IAAI,CAAC/B,OAAO,CAACqB,KAAK,CAAC;GAC9B,EAAE,CAACrB,OAAO,CAACqB,KAAK,EAAEb,SAAS,CAAC,CAAC;EAE9B,OAAO;AAAEI,IAAAA,KAAK,EAALA,KAAK;AAAEJ,IAAAA,SAAS,EAATA;GAAW;AAC5B;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@device-portal/react",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Simple WebRTC data channel for React.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "dev:package": "rollup -c -w",
19
19
  "build:package": "rollup -c",
20
20
  "test:package": "npm run build:package",
21
- "dev:storybook": "storybook dev -p 6006",
21
+ "dev:storybook": "storybook dev -p 7007",
22
22
  "build:storybook": "storybook build"
23
23
  },
24
24
  "repository": {
@@ -39,14 +39,13 @@
39
39
  "react": "18 || 19"
40
40
  },
41
41
  "dependencies": {
42
- "@device-portal/client": "^0.0.16"
42
+ "@device-portal/client": "^0.0.18"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@babel/core": "^7.24.0",
46
46
  "@babel/preset-env": "^7.24.0",
47
47
  "@babel/preset-react": "^7.24.0",
48
48
  "@babel/preset-typescript": "^7.24.0",
49
- "@device-portal/constants": "*",
50
49
  "@rollup/plugin-babel": "^6.0.4",
51
50
  "@rollup/plugin-commonjs": "^25.0.7",
52
51
  "@rollup/plugin-node-resolve": "^15.2.3",