@device-portal/react 0.0.17 → 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 {};
@@ -20,6 +20,14 @@ var withResolvers = function withResolvers() {
20
20
  };
21
21
  };
22
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
+ */
23
31
  var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
24
32
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25
33
  var _useState = useState(null),
@@ -28,6 +36,7 @@ var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
28
36
  setValueState = _useState2[1];
29
37
  // Initialize the responder synchronously if it doesn't exist
30
38
  if (!responders[room]) {
39
+ var _options$sendLastValu;
31
40
  var _withResolvers = withResolvers(),
32
41
  firstValuePromise = _withResolvers.promise,
33
42
  firstValueResolve = _withResolvers.resolve;
@@ -62,9 +71,9 @@ var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
62
71
  _iterator.f();
63
72
  }
64
73
  },
65
- sendLastValueOnConnectAndReconnect: false,
66
- websocketSignalingServer: options.websocketSignalingServer,
67
- localDeviceOnly: options.localDeviceOnly
74
+ sendLastValueOnConnectAndReconnect: (_options$sendLastValu = options.sendLastValueOnConnectAndReconnect) !== null && _options$sendLastValu !== void 0 ? _options$sendLastValu : false,
75
+ webSocketSignalingServer: options.webSocketSignalingServer,
76
+ browserDirect: options.browserDirect
68
77
  });
69
78
  responders[room] = {
70
79
  responder: responder,
@@ -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 } 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\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\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: false,\n\t\t\twebsocketSignalingServer: options.websocketSignalingServer,\n\t\t\tlocalDeviceOnly: options.localDeviceOnly,\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","_withResolvers","firstValuePromise","firstValueResolve","sendMessageToProvider","value","responder","send","Responder","onMessage","consumer","_iterator","_createForOfIteratorHelper","setValueStates","_step","s","n","done","setState","err","e","f","sendLastValueOnConnectAndReconnect","websocketSignalingServer","localDeviceOnly","Set","useEffect","roomEntry","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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"}
@@ -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.17",
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.17"
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",