@device-portal/react 0.0.22 → 0.1.0

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
@@ -81,6 +81,17 @@ const ConsumerComponent = () => {
81
81
  }
82
82
  ```
83
83
 
84
+ ### Per-peer Values
85
+
86
+ Instead of broadcasting the same value to all consumers, you can pass a function that returns a different value for each peer:
87
+
88
+ ```jsx
89
+ useDevicePortalProvider('my-test-room', {
90
+ value: (peerId) => JSON.stringify({ peerId, timestamp: Date.now() }),
91
+ webSocketSignalingServer: 'wss://device-portal.filipchalupa.cz',
92
+ })
93
+ ```
94
+
84
95
  ### Two-way Communication
85
96
 
86
97
  You can also send messages from the `Consumer` back to the `Provider`.
@@ -157,6 +168,30 @@ const ConsumerComponent = () => {
157
168
 
158
169
  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
170
 
171
+ ### Connection Status
172
+
173
+ `useDevicePortalConsumer` exposes a `connectionStatus` field so the UI can react when the peer link drops:
174
+
175
+ ```jsx
176
+ const ConsumerComponent = () => {
177
+ const { value, connectionStatus } = useDevicePortalConsumer('my-test-room')
178
+
179
+ return (
180
+ <>
181
+ <p>Value from provider: {value}</p>
182
+ {connectionStatus === 'reconnecting' && <p>Reconnecting to provider…</p>}
183
+ </>
184
+ )
185
+ }
186
+ ```
187
+
188
+ The status is one of:
189
+
190
+ - `'connected'` — the peer link is up.
191
+ - `'reconnecting'` — the peer link dropped (peer left, ICE failed); the consumer is retrying in the background. The last received `value` is kept.
192
+
193
+ `'connecting'` is intentionally not exposed: the hook suspends until the first value arrives, so by the time your component renders the consumer was already connected at least once.
194
+
160
195
  ### Browser Direct Communication
161
196
 
162
197
  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.
@@ -6,6 +6,54 @@ 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
+ }
9
57
  function _iterableToArrayLimit(r, l) {
10
58
  var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
11
59
  if (null != t) {
@@ -64,5 +112,5 @@ function _unsupportedIterableToArray(r, a) {
64
112
  }
65
113
  }
66
114
 
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 };
115
+ export { _arrayLikeToArray as arrayLikeToArray, _arrayWithHoles as arrayWithHoles, _createForOfIteratorHelper as createForOfIteratorHelper, _iterableToArrayLimit as iterableToArrayLimit, _nonIterableRest as nonIterableRest, _objectWithoutProperties as objectWithoutProperties, _objectWithoutPropertiesLoose as objectWithoutPropertiesLoose, _slicedToArray as slicedToArray, _unsupportedIterableToArray as unsupportedIterableToArray };
68
116
  //# 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -10,6 +10,16 @@ export type DevicePortalConsumerOptions = {
10
10
  /** Whether to automatically send the last 'send()' value back to the provider on reconnect. Default: false. */
11
11
  sendLastMessageOnReconnect?: boolean;
12
12
  };
13
+ /**
14
+ * Connection state reported by {@link useDevicePortalConsumer}.
15
+ *
16
+ * The hook suspends until the first value arrives, so `'connecting'` is never
17
+ * observed — by the time the component renders, the consumer was at least once
18
+ * connected. After that, the status flips to `'reconnecting'` whenever the
19
+ * peer link drops (peer left, ICE failure) and back to `'connected'` once the
20
+ * link is re-established.
21
+ */
22
+ type ConnectionStatus = 'connected' | 'reconnecting';
13
23
  /**
14
24
  * A React hook that joins a Device Portal room and receives values from the
15
25
  * provider. **Suspends** the component until the first value is received —
@@ -23,8 +33,18 @@ export type DevicePortalConsumerOptions = {
23
33
  *
24
34
  * @param room - The unique room ID.
25
35
  * @param options - Consumer configuration options.
36
+ * @returns An object with:
37
+ * - `value`: the latest value pushed by the provider. After Suspense
38
+ * resolves, this never goes "stale" to `undefined` — if the peer link
39
+ * drops, the last received value is retained.
40
+ * - `connectionStatus`: `'connected'` or `'reconnecting'`. Use this to
41
+ * render a banner / indicator when the peer link is temporarily down.
42
+ * The hook keeps trying to reconnect in the background.
43
+ * - `sendMessageToProvider`: send a message back to the provider.
26
44
  */
27
45
  export declare const useDevicePortalConsumer: (room: string, options?: DevicePortalConsumerOptions) => {
28
46
  value: string;
47
+ connectionStatus: ConnectionStatus;
29
48
  sendMessageToProvider: (message: string) => void;
30
49
  };
50
+ export {};
@@ -37,7 +37,9 @@ function getOrCreateEntry(key, room, peerId, options) {
37
37
  firstValuePromise: firstValuePromise,
38
38
  latestValue: undefined,
39
39
  lastSentValue: undefined,
40
+ connectionStatus: 'reconnecting',
40
41
  setValue: null,
42
+ setConnectionStatus: null,
41
43
  destroyTimer: null,
42
44
  room: room,
43
45
  optionsSnapshot: newOptionsKey
@@ -50,10 +52,18 @@ function getOrCreateEntry(key, room, peerId, options) {
50
52
  (_entry$setValue = entry.setValue) === null || _entry$setValue === void 0 || _entry$setValue.call(entry, value);
51
53
  },
52
54
  onConnected: function onConnected() {
55
+ var _entry$setConnectionS;
56
+ entry.connectionStatus = 'connected';
57
+ (_entry$setConnectionS = entry.setConnectionStatus) === null || _entry$setConnectionS === void 0 || _entry$setConnectionS.call(entry, 'connected');
53
58
  if (sendLastMessageOnReconnect && entry.lastSentValue !== undefined) {
54
59
  entry.consumer.send(entry.lastSentValue);
55
60
  }
56
61
  },
62
+ onDisconnected: function onDisconnected() {
63
+ var _entry$setConnectionS2;
64
+ entry.connectionStatus = 'reconnecting';
65
+ (_entry$setConnectionS2 = entry.setConnectionStatus) === null || _entry$setConnectionS2 === void 0 || _entry$setConnectionS2.call(entry, 'reconnecting');
66
+ },
57
67
  webSocketSignalingServer: options.webSocketSignalingServer,
58
68
  browserDirect: options.browserDirect,
59
69
  peerId: peerId
@@ -85,6 +95,14 @@ function scheduleDestroy(key) {
85
95
  *
86
96
  * @param room - The unique room ID.
87
97
  * @param options - Consumer configuration options.
98
+ * @returns An object with:
99
+ * - `value`: the latest value pushed by the provider. After Suspense
100
+ * resolves, this never goes "stale" to `undefined` — if the peer link
101
+ * drops, the last received value is retained.
102
+ * - `connectionStatus`: `'connected'` or `'reconnecting'`. Use this to
103
+ * render a banner / indicator when the peer link is temporarily down.
104
+ * The hook keeps trying to reconnect in the background.
105
+ * - `sendMessageToProvider`: send a message back to the provider.
88
106
  */
89
107
  var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
90
108
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -104,17 +122,27 @@ var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
104
122
  _useState2 = _slicedToArray(_useState, 2),
105
123
  value = _useState2[0],
106
124
  setValue = _useState2[1];
107
- // Wire up the entry's setValue so the Consumer can push updates.
125
+ var _useState3 = useState(function () {
126
+ return entry.connectionStatus;
127
+ }),
128
+ _useState4 = _slicedToArray(_useState3, 2),
129
+ connectionStatus = _useState4[0],
130
+ setConnectionStatus = _useState4[1];
131
+ // Wire up the entry's setters so the Consumer can push updates.
108
132
  entry.setValue = setValue;
133
+ entry.setConnectionStatus = setConnectionStatus;
109
134
  useEffect(function () {
110
135
  var e = getOrCreateEntry(cacheKey, room, peerIdRef.current, options);
111
136
  e.setValue = setValue;
112
- // Sync in case a value arrived between render and effect commit.
137
+ e.setConnectionStatus = setConnectionStatus;
138
+ // Sync in case state changed between render and effect commit.
113
139
  if (e.latestValue !== undefined) {
114
140
  setValue(e.latestValue);
115
141
  }
142
+ setConnectionStatus(e.connectionStatus);
116
143
  return function () {
117
144
  e.setValue = null;
145
+ e.setConnectionStatus = null;
118
146
  scheduleDestroy(cacheKey);
119
147
  };
120
148
  }, [cacheKey, room, options.webSocketSignalingServer, options.browserDirect, options.sendLastMessageOnReconnect]);
@@ -124,6 +152,7 @@ var useDevicePortalConsumer = function useDevicePortalConsumer(room) {
124
152
  }, [entry]);
125
153
  return {
126
154
  value: value,
155
+ connectionStatus: connectionStatus,
127
156
  sendMessageToProvider: sendMessageToProvider
128
157
  };
129
158
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useDevicePortalConsumer.js","sources":["../../src/consumer/useDevicePortalConsumer.ts"],"sourcesContent":["import {\n\tClient,\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: Client\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 Client(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\t// setTimeout(0) so React Strict Mode's synchronous unmount/remount\n\t// reclaims the entry before it's destroyed, while real unmounts\n\t// (navigation, conditional render) clean up on the next microtask.\n\tentry.destroyTimer = setTimeout(() => {\n\t\tentry.consumer.destroy()\n\t\tentries.delete(key)\n\t}, 0)\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","Client","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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"}
1
+ {"version":3,"file":"useDevicePortalConsumer.js","sources":["../../src/consumer/useDevicePortalConsumer.ts"],"sourcesContent":["import {\n\tClient,\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\n/**\n * Connection state reported by {@link useDevicePortalConsumer}.\n *\n * The hook suspends until the first value arrives, so `'connecting'` is never\n * observed — by the time the component renders, the consumer was at least once\n * connected. After that, the status flips to `'reconnecting'` whenever the\n * peer link drops (peer left, ICE failure) and back to `'connected'` once the\n * link is re-established.\n */\ntype ConnectionStatus = 'connected' | 'reconnecting'\n\ntype ConsumerEntry = {\n\tconsumer: Client\n\tfirstValuePromise: Promise<string>\n\tlatestValue: string | undefined\n\tlastSentValue: string | undefined\n\tconnectionStatus: ConnectionStatus\n\tsetValue: ((value: string) => void) | null\n\tsetConnectionStatus: ((status: ConnectionStatus) => 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\tconnectionStatus: 'reconnecting',\n\t\tsetValue: null,\n\t\tsetConnectionStatus: null,\n\t\tdestroyTimer: null,\n\t\troom,\n\t\toptionsSnapshot: newOptionsKey,\n\t}\n\n\tentry.consumer = new Client(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\tentry.connectionStatus = 'connected'\n\t\t\tentry.setConnectionStatus?.('connected')\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\tonDisconnected: () => {\n\t\t\tentry.connectionStatus = 'reconnecting'\n\t\t\tentry.setConnectionStatus?.('reconnecting')\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\t// setTimeout(0) so React Strict Mode's synchronous unmount/remount\n\t// reclaims the entry before it's destroyed, while real unmounts\n\t// (navigation, conditional render) clean up on the next microtask.\n\tentry.destroyTimer = setTimeout(() => {\n\t\tentry.consumer.destroy()\n\t\tentries.delete(key)\n\t}, 0)\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 * @returns An object with:\n * - `value`: the latest value pushed by the provider. After Suspense\n * resolves, this never goes \"stale\" to `undefined` — if the peer link\n * drops, the last received value is retained.\n * - `connectionStatus`: `'connected'` or `'reconnecting'`. Use this to\n * render a banner / indicator when the peer link is temporarily down.\n * The hook keeps trying to reconnect in the background.\n * - `sendMessageToProvider`: send a message back to the provider.\n */\nexport const useDevicePortalConsumer = (\n\troom: string,\n\toptions: DevicePortalConsumerOptions = {},\n): {\n\tvalue: string\n\tconnectionStatus: ConnectionStatus\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\tconst [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>(\n\t\t() => entry.connectionStatus,\n\t)\n\n\t// Wire up the entry's setters so the Consumer can push updates.\n\tentry.setValue = setValue\n\tentry.setConnectionStatus = setConnectionStatus\n\n\tuseEffect(() => {\n\t\tconst e = getOrCreateEntry(cacheKey, room, peerIdRef.current, options)\n\t\te.setValue = setValue\n\t\te.setConnectionStatus = setConnectionStatus\n\n\t\t// Sync in case state changed between render and effect commit.\n\t\tif (e.latestValue !== undefined) {\n\t\t\tsetValue(e.latestValue)\n\t\t}\n\t\tsetConnectionStatus(e.connectionStatus)\n\n\t\treturn () => {\n\t\t\te.setValue = null\n\t\t\te.setConnectionStatus = 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, connectionStatus, 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","connectionStatus","setValue","setConnectionStatus","Client","onMessage","value","_entry$setValue","call","onConnected","_entry$setConnectionS","send","onDisconnected","_entry$setConnectionS2","set","scheduleDestroy","setTimeout","useDevicePortalConsumer","arguments","length","cacheKey","peerIdRef","useRef","generatePeerId","current","firstValue","use","_useState","useState","_entry$latestValue","_useState2","_slicedToArray","_useState3","_useState4","useEffect","e","sendMessageToProvider","useCallback","message"],"mappings":"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"}
@@ -3,8 +3,8 @@ import { Host, type BrowserDirectOption, type PeerId } from '@device-portal/clie
3
3
  * Configuration options for the Device Portal Provider.
4
4
  */
5
5
  export type DevicePortalProviderOptions = {
6
- /** The value to share with all connected consumers. */
7
- value?: string;
6
+ /** The value to share with connected consumers. A function receives the peerId and can return a per-peer value. */
7
+ value?: string | ((peerId: PeerId) => string);
8
8
  /** URL of the signaling server, or null to disable. */
9
9
  webSocketSignalingServer?: string | null;
10
10
  /** Callback when a consumer sends a message back to the provider. */
@@ -1,4 +1,4 @@
1
- import { slicedToArray as _slicedToArray } from '../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { slicedToArray as _slicedToArray, createForOfIteratorHelper as _createForOfIteratorHelper } from '../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import { generatePeerId, Host } from '@device-portal/client';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
 
@@ -24,8 +24,9 @@ var useDevicePortalProvider = function useDevicePortalProvider(room) {
24
24
  onMessageFromConsumerRef.current = options.onMessageFromConsumer;
25
25
  // Stable peer ID that survives React Strict Mode unmount/remount cycles
26
26
  var peerIdRef = useRef(generatePeerId());
27
- var lastSentValueRef = useRef(undefined);
28
27
  var sendLastValueOnConnectAndReconnect = (_options$sendLastValu = options.sendLastValueOnConnectAndReconnect) !== null && _options$sendLastValu !== void 0 ? _options$sendLastValu : true;
28
+ var valueRef = useRef(options.value);
29
+ valueRef.current = options.value;
29
30
  useEffect(function () {
30
31
  var newProvider = new Host(room, {
31
32
  onMessage: function onMessage(value, peerId) {
@@ -35,10 +36,11 @@ var useDevicePortalProvider = function useDevicePortalProvider(room) {
35
36
  onPeersChange: function onPeersChange(peers_0) {
36
37
  setPeers(peers_0);
37
38
  },
38
- onPeerConnected: function onPeerConnected() {
39
- if (sendLastValueOnConnectAndReconnect && lastSentValueRef.current !== undefined) {
40
- newProvider.send(lastSentValueRef.current);
41
- }
39
+ onPeerConnected: function onPeerConnected(peerId_0) {
40
+ if (!sendLastValueOnConnectAndReconnect) return;
41
+ var v = valueRef.current;
42
+ if (v === undefined) return;
43
+ newProvider.sendToPeer(peerId_0, typeof v === 'function' ? v(peerId_0) : v);
42
44
  },
43
45
  webSocketSignalingServer: options.webSocketSignalingServer,
44
46
  maxClients: options.maxClients,
@@ -54,11 +56,24 @@ var useDevicePortalProvider = function useDevicePortalProvider(room) {
54
56
  };
55
57
  }, [room, sendLastValueOnConnectAndReconnect, options.webSocketSignalingServer, options.maxClients, options.browserDirect]);
56
58
  useEffect(function () {
57
- if (options.value === undefined) {
58
- return;
59
+ if (options.value === undefined || !provider) return;
60
+ if (typeof options.value === 'function') {
61
+ var fn = options.value;
62
+ var _iterator = _createForOfIteratorHelper(provider.peers),
63
+ _step;
64
+ try {
65
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
66
+ var peerId_1 = _step.value;
67
+ provider.sendToPeer(peerId_1, fn(peerId_1));
68
+ }
69
+ } catch (err) {
70
+ _iterator.e(err);
71
+ } finally {
72
+ _iterator.f();
73
+ }
74
+ } else {
75
+ provider.send(options.value);
59
76
  }
60
- lastSentValueRef.current = options.value;
61
- provider === null || provider === void 0 || provider.send(options.value);
62
77
  }, [options.value, provider]);
63
78
  return {
64
79
  peers: peers,
@@ -1 +1 @@
1
- {"version":3,"file":"useDevicePortalProvider.js","sources":["../../src/provider/useDevicePortalProvider.ts"],"sourcesContent":["import {\n\tHost,\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<Host | 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 Host(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","Host","onMessage","value","peerId","_onMessageFromConsume","call","onPeersChange","onPeerConnected","send","webSocketSignalingServer","maxClients","browserDirect","destroy"],"mappings":";;;;AA4BA;;;;;;AAMG;IACUA,uBAAuB,GAAG,SAA1BA,uBAAuBA,CACnCC,IAAY,EAET;AAAA,EAAA,IAAAC,qBAAA;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,GAAgCC,QAAQ,CAAc,IAAI,CAAC;IAAAC,UAAA,GAAAC,cAAA,CAAAH,SAAA,EAAA,CAAA,CAAA;AAApDI,IAAAA,QAAQ,GAAAF,UAAA,CAAA,CAAA,CAAA;AAAEG,IAAAA,WAAW,GAAAH,UAAA,CAAA,CAAA,CAAA;AAC5B,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;AAChE;AACA,EAAA,IAAME,SAAS,GAAGH,MAAM,CAASI,cAAc,EAAE,CAAC;AAClD,EAAA,IAAMC,gBAAgB,GAAGL,MAAM,CAAqBZ,SAAS,CAAC;AAC9D,EAAA,IAAMkB,kCAAkC,GAAA,CAAAtB,qBAAA,GACvCC,OAAO,CAACqB,kCAAkC,MAAA,IAAA,IAAAtB,qBAAA,KAAA,MAAA,GAAAA,qBAAA,GAAI,IAAI;AAEnDuB,EAAAA,SAAS,CAAC,YAAK;AACd,IAAA,IAAMC,WAAW,GAAG,IAAIC,IAAI,CAAC1B,IAAI,EAAE;AAClC2B,MAAAA,SAAS,EAAE,SAAXA,SAASA,CAAGC,KAAK,EAAEC,MAAM,EAAI;AAAA,QAAA,IAAAC,qBAAA;AAC5B,QAAA,CAAAA,qBAAA,GAAAd,wBAAwB,CAACG,OAAO,cAAAW,qBAAA,KAAA,MAAA,IAAhCA,qBAAA,CAAAC,IAAA,CAAAf,wBAAwB,EAAWY,KAAK,EAAEC,MAAM,CAAC;OACjD;AACDG,MAAAA,aAAa,EAAE,SAAfA,aAAaA,CAAGlB,OAAK,EAAI;QACxBC,QAAQ,CAACD,OAAK,CAAC;OACf;AACDmB,MAAAA,eAAe,EAAE,SAAjBA,eAAeA,GAAO;AACrB,QAAA,IACCV,kCAAkC,IAClCD,gBAAgB,CAACH,OAAO,KAAKd,SAAS,EACrC;AACDoB,UAAAA,WAAW,CAACS,IAAI,CAACZ,gBAAgB,CAACH,OAAO,CAAC;AAC3C;OACA;MACDgB,wBAAwB,EAAEjC,OAAO,CAACiC,wBAAwB;MAC1DC,UAAU,EAAElC,OAAO,CAACkC,UAAU;MAC9BC,aAAa,EAAEnC,OAAO,CAACmC,aAAa;MACpCR,MAAM,EAAET,SAAS,CAACD;AAClB,KAAA,CAAC;IACFR,WAAW,CAACc,WAAW,CAAC;AACxBV,IAAAA,QAAQ,CAACU,WAAW,CAACX,KAAK,CAAC;AAE3B,IAAA,OAAO,YAAK;MACXW,WAAW,CAACa,OAAO,EAAE;MACrB3B,WAAW,CAAC,IAAI,CAAC;MACjBI,QAAQ,CAAC,EAAE,CAAC;KACZ;AACF,GAAC,EAAE,CACFf,IAAI,EACJuB,kCAAkC,EAClCrB,OAAO,CAACiC,wBAAwB,EAChCjC,OAAO,CAACkC,UAAU,EAClBlC,OAAO,CAACmC,aAAa,CACrB,CAAC;AAEFb,EAAAA,SAAS,CAAC,YAAK;AACd,IAAA,IAAItB,OAAO,CAAC0B,KAAK,KAAKvB,SAAS,EAAE;AAChC,MAAA;AACD;AACAiB,IAAAA,gBAAgB,CAACH,OAAO,GAAGjB,OAAO,CAAC0B,KAAK;IACxClB,QAAQ,KAAA,IAAA,IAARA,QAAQ,KAAA,MAAA,IAARA,QAAQ,CAAEwB,IAAI,CAAChC,OAAO,CAAC0B,KAAK,CAAC;GAC7B,EAAE,CAAC1B,OAAO,CAAC0B,KAAK,EAAElB,QAAQ,CAAC,CAAC;EAE7B,OAAO;AAAEI,IAAAA,KAAK,EAALA,KAAK;AAAEJ,IAAAA,QAAQ,EAARA;GAAU;AAC3B;;;;"}
1
+ {"version":3,"file":"useDevicePortalProvider.js","sources":["../../src/provider/useDevicePortalProvider.ts"],"sourcesContent":["import {\n\tHost,\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 connected consumers. A function receives the peerId and can return a per-peer value. */\n\tvalue?: string | ((peerId: PeerId) => 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<Host | 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 sendLastValueOnConnectAndReconnect =\n\t\toptions.sendLastValueOnConnectAndReconnect ?? true\n\n\tconst valueRef = useRef(options.value)\n\tvalueRef.current = options.value\n\n\tuseEffect(() => {\n\t\tconst newProvider = new Host(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: (peerId) => {\n\t\t\t\tif (!sendLastValueOnConnectAndReconnect) return\n\t\t\t\tconst v = valueRef.current\n\t\t\t\tif (v === undefined) return\n\t\t\t\tnewProvider.sendToPeer(peerId, typeof v === 'function' ? v(peerId) : v)\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 || !provider) return\n\t\tif (typeof options.value === 'function') {\n\t\t\tconst fn = options.value\n\t\t\tfor (const peerId of provider.peers) {\n\t\t\t\tprovider.sendToPeer(peerId, fn(peerId))\n\t\t\t}\n\t\t} else {\n\t\t\tprovider.send(options.value)\n\t\t}\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","sendLastValueOnConnectAndReconnect","valueRef","value","useEffect","newProvider","Host","onMessage","peerId","_onMessageFromConsume","call","onPeersChange","onPeerConnected","v","sendToPeer","webSocketSignalingServer","maxClients","browserDirect","destroy","fn","_iterator","_createForOfIteratorHelper","_step","s","n","done","err","e","f","send"],"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.22",
3
+ "version": "0.1.0",
4
4
  "description": "Simple WebRTC data channel for React.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,7 +39,7 @@
39
39
  "react": ">=19"
40
40
  },
41
41
  "dependencies": {
42
- "@device-portal/client": "^0.0.22"
42
+ "@device-portal/client": "^0.1.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@babel/core": "^7.24.0",