floppy-disk 3.7.1 → 3.7.2

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.
@@ -33,6 +33,50 @@ type StreamDataState<TData, TError> = {
33
33
  error: TError;
34
34
  errorUpdatedAt: number;
35
35
  };
36
+ /**
37
+ * Represents the full state of a stream.
38
+ *
39
+ * @remarks
40
+ * A stream consists of two independent concerns:
41
+ *
42
+ * 1. **Connection state** — lifecycle of the underlying connection
43
+ * 2. **Data state** — lifecycle of emitted data
44
+ *
45
+ * These two are combined into a single state object.
46
+ *
47
+ * ---
48
+ *
49
+ * ## Connection lifecycle
50
+ *
51
+ * - `INITIAL` → no connection has been established
52
+ * - `CONNECTING` → connection is being established
53
+ * - `CONNECTED` → connection is active
54
+ * - `DISCONNECTED` → connection was previously established but is now closed
55
+ *
56
+ * Timestamps:
57
+ * - `connectingAt` → when connection attempt started
58
+ * - `connectedAt` → when connection was established
59
+ * - `disconnectedAt` → when connection was closed
60
+ *
61
+ * ---
62
+ *
63
+ * ## Data lifecycle
64
+ *
65
+ * - `INITIAL` → no data has been received
66
+ * - `SUCCESS` → data has been received successfully
67
+ * - `ERROR` → error occurred before any data
68
+ * - `SUCCESS_BUT_THEN_ERROR` → data exists, but a later error occurred
69
+ *
70
+ * ---
71
+ *
72
+ * ## Notes
73
+ *
74
+ * - Connection state and data state evolve independently.
75
+ * - A stream may be:
76
+ * - connected but have no data yet
77
+ * - disconnected but still retain previous data
78
+ * - Errors do not necessarily reset data.
79
+ */
36
80
  export type StreamState<TData, TError> = ({
37
81
  connectionState: "INITIAL";
38
82
  connectingAt: undefined;
@@ -60,25 +104,218 @@ type DisconnectTrigger = "last-unsubscribe" | "document-hidden" | "offline";
60
104
  type ReconnectTrigger = "first-subscribe" | "document-visible" | "online";
61
105
  type AdditionalStoreApi<TConnection> = {
62
106
  variableHash: string;
107
+ /**
108
+ * Connection controls for the stream.
109
+ *
110
+ * @remarks
111
+ * Provides imperative control over the underlying connection.
112
+ */
63
113
  connection: {
114
+ /**
115
+ * Returns the current connection instance.
116
+ *
117
+ * @returns The active connection or `undefined` if not connected
118
+ */
64
119
  get: () => Readonly<TConnection> | undefined;
120
+ /**
121
+ * Forces a reconnection.
122
+ *
123
+ * @remarks
124
+ * - Cancels any scheduled disconnect
125
+ * - Starts a new connection if not already connecting
126
+ */
65
127
  reconnect: () => void;
128
+ /**
129
+ * Immediately disconnects the current connection.
130
+ *
131
+ * @remarks
132
+ * - Ignores disconnect delay rules
133
+ * - Updates connection state to `DISCONNECTED`
134
+ */
66
135
  disconnect: () => void;
67
136
  };
137
+ /**
138
+ * Data controls for the stream.
139
+ */
68
140
  data: {
141
+ /**
142
+ * Resets the data state back to `INITIAL`.
143
+ *
144
+ * @remarks
145
+ * - Does not affect connection state
146
+ * - Useful for clearing stale or invalid data
147
+ */
69
148
  reset: () => void;
70
149
  };
150
+ /**
151
+ * Deletes the stream instance.
152
+ *
153
+ * @returns `true` if deleted, `false` otherwise
154
+ *
155
+ * @remarks
156
+ * - Cannot delete while there are active subscribers
157
+ * - Clears connection, state, and cached instance
158
+ */
71
159
  delete: () => boolean;
72
160
  };
161
+ /**
162
+ * Configuration options for a stream.
163
+ *
164
+ * @remarks
165
+ * Controls connection lifecycle, reconnection behavior, and data retention.
166
+ */
73
167
  export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions<StreamState<TData, TError>, AdditionalStoreApi<TConnection>> & {
168
+ /**
169
+ * Connection-related behavior.
170
+ */
74
171
  connection?: {
172
+ /**
173
+ * Determines when a connection should be disconnected.
174
+ *
175
+ * @param trigger - The reason for the disconnect attempt
176
+ * @param state - Current stream state
177
+ *
178
+ * @returns
179
+ * - `number` → delay (ms) before disconnecting
180
+ * - `false` → prevent disconnection
181
+ *
182
+ * @default Disconnect after 5 seconds for any triggers
183
+ *
184
+ * @remarks
185
+ * Triggers:
186
+ * - `"last-unsubscribe"` → no active subscribers
187
+ * - `"document-hidden"` → tab becomes hidden
188
+ * - `"offline"` → network goes offline
189
+ */
75
190
  disconnectOn?: (trigger: DisconnectTrigger, state: StreamState<TData, TError>) => false | number;
191
+ /**
192
+ * Determines whether a connection should reconnect.
193
+ *
194
+ * @param trigger - The reason for the reconnect attempt
195
+ * @param state - Current stream state
196
+ *
197
+ * @returns `true` to reconnect, otherwise `false`
198
+ *
199
+ * @default No reconnection if already connected
200
+ *
201
+ * @remarks
202
+ * Triggers:
203
+ * - `"first-subscribe"` → first subscriber appears
204
+ * - `"document-visible"` → tab becomes visible
205
+ * - `"online"` → network reconnects
206
+ */
76
207
  reconnectOn?: (trigger: ReconnectTrigger, state: StreamState<TData, TError>) => boolean;
77
208
  };
209
+ /**
210
+ * Data-related behavior.
211
+ */
78
212
  data?: {
213
+ /**
214
+ * Time (in milliseconds) before unused stream data is garbage collected.
215
+ *
216
+ * Starts counting after disconnection.
217
+ *
218
+ * @default 5 minutes
219
+ */
79
220
  gcTime?: number;
80
221
  };
81
222
  };
223
+ /**
224
+ * Creates a stream factory for managing real-time connections.
225
+ *
226
+ * @param connect - Function to establish a connection
227
+ * @param disconnect - Function to close a connection
228
+ * @param options - Optional configuration for lifecycle and behavior
229
+ *
230
+ * @returns A function to retrieve or create a stream instance by variable
231
+ *
232
+ * @remarks
233
+ * This utility is designed for **long-lived, push-based async sources**, such as:
234
+ * - WebSocket
235
+ * - Server-Sent Events (SSE)
236
+ * - Firebase / realtime databases
237
+ *
238
+ * ---
239
+ *
240
+ * ## Key concepts
241
+ *
242
+ * ### 1. Connection lifecycle (managed automatically)
243
+ *
244
+ * - Connection is established when needed (e.g. first subscriber)
245
+ * - Connection may be disconnected based on triggers:
246
+ * - no subscribers
247
+ * - tab hidden
248
+ * - offline
249
+ * - Reconnection is controlled via `reconnectOn`
250
+ *
251
+ * ---
252
+ *
253
+ * ### 2. Data flow (push-based)
254
+ *
255
+ * The `connect` function receives an `emit` API:
256
+ *
257
+ * - `emit.connected()` → mark connection as established
258
+ * - `emit.data(fn)` → update data using reducer
259
+ * - `emit.error(err)` → report error
260
+ *
261
+ * Data updates are **incremental** and controlled by the stream source.
262
+ *
263
+ * ---
264
+ *
265
+ * ### 3. Store-per-variable
266
+ *
267
+ * - Each unique `variable` creates a separate stream instance
268
+ * - Variables are deterministically hashed for stable identity
269
+ * - Each instance manages its own:
270
+ * - connection
271
+ * - state
272
+ * - subscribers
273
+ *
274
+ * ---
275
+ *
276
+ * ### 4. React integration (Proxy-based)
277
+ *
278
+ * - The returned hook exposes the full state as a Proxy
279
+ * - Components automatically subscribe to accessed properties
280
+ * - No selector or memoization is required
281
+ *
282
+ * ---
283
+ *
284
+ * ## Execution model
285
+ *
286
+ * - Streams are **lazy**:
287
+ * - No connection until there is a subscriber
288
+ * - Streams are **shared**:
289
+ * - Multiple subscribers reuse the same connection
290
+ * - Streams are **stateful**:
291
+ * - Data persists across reconnects (unless reset or GC)
292
+ *
293
+ * ---
294
+ *
295
+ * @example
296
+ * const chatStream = createStream(
297
+ * (roomId, emit) => {
298
+ * const ws = new WebSocket(`/chat/${roomId}`);
299
+ *
300
+ * ws.onopen = () => emit.connected();
301
+ * ws.onmessage = (e) => {
302
+ * const msg = JSON.parse(e.data);
303
+ * emit.data((prev) => [...(prev ?? []), msg]);
304
+ * };
305
+ * ws.onerror = (err) => emit.error(err);
306
+ *
307
+ * return ws;
308
+ * },
309
+ * (ws) => ws.close()
310
+ * );
311
+ *
312
+ * function Chat({ roomId }) {
313
+ * const useChat = chatStream(roomId);
314
+ * const state = useChat();
315
+ *
316
+ * return <div>{state.data?.length}</div>;
317
+ * }
318
+ */
82
319
  export declare const experimental_createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
83
320
  connected: () => void;
84
321
  data: (reducer: (data: TData | undefined) => TData) => void;
@@ -91,14 +328,58 @@ export declare const experimental_createStream: <TConnection, TData, TVariable e
91
328
  subscribe: (subscriber: import("../vanilla.d.mts").Subscriber<StreamState<TData, TError>>) => () => void;
92
329
  getSubscriberCount: () => number;
93
330
  variableHash: string;
331
+ /**
332
+ * Connection controls for the stream.
333
+ *
334
+ * @remarks
335
+ * Provides imperative control over the underlying connection.
336
+ */
94
337
  connection: {
338
+ /**
339
+ * Returns the current connection instance.
340
+ *
341
+ * @returns The active connection or `undefined` if not connected
342
+ */
95
343
  get: () => Readonly<TConnection> | undefined;
344
+ /**
345
+ * Forces a reconnection.
346
+ *
347
+ * @remarks
348
+ * - Cancels any scheduled disconnect
349
+ * - Starts a new connection if not already connecting
350
+ */
96
351
  reconnect: () => void;
352
+ /**
353
+ * Immediately disconnects the current connection.
354
+ *
355
+ * @remarks
356
+ * - Ignores disconnect delay rules
357
+ * - Updates connection state to `DISCONNECTED`
358
+ */
97
359
  disconnect: () => void;
98
360
  };
361
+ /**
362
+ * Data controls for the stream.
363
+ */
99
364
  data: {
365
+ /**
366
+ * Resets the data state back to `INITIAL`.
367
+ *
368
+ * @remarks
369
+ * - Does not affect connection state
370
+ * - Useful for clearing stale or invalid data
371
+ */
100
372
  reset: () => void;
101
373
  };
374
+ /**
375
+ * Deletes the stream instance.
376
+ *
377
+ * @returns `true` if deleted, `false` otherwise
378
+ *
379
+ * @remarks
380
+ * - Cannot delete while there are active subscribers
381
+ * - Clears connection, state, and cached instance
382
+ */
102
383
  delete: () => boolean;
103
384
  };
104
385
  export {};
package/esm/react.mjs CHANGED
@@ -873,11 +873,14 @@ const experimental_createStream = (connect, disconnect, options = {}) => {
873
873
  store.connection = {};
874
874
  store.connection.get = () => connections.get(store);
875
875
  store.connection.reconnect = () => {
876
- var _a;
877
876
  clearAllTimeouts(store);
878
877
  const { connectionState } = store.getState();
879
878
  if (connectionState === "CONNECTING") return;
880
- (_a = disconnectFns.get(store)) == null ? void 0 : _a();
879
+ const prevDisconnect = disconnectFns.get(store);
880
+ if (prevDisconnect) {
881
+ prevDisconnect();
882
+ disconnectFns.delete(store);
883
+ }
881
884
  store.setState({
882
885
  connectionState: "CONNECTING",
883
886
  connectingAt: Date.now(),
@@ -894,10 +897,10 @@ const experimental_createStream = (connect, disconnect, options = {}) => {
894
897
  },
895
898
  data: (reducer) => {
896
899
  store.setState((prev) => {
897
- var _a2;
900
+ var _a;
898
901
  return {
899
902
  connectionState: "CONNECTED",
900
- connectedAt: (_a2 = prev.connectedAt) != null ? _a2 : Date.now(),
903
+ connectedAt: (_a = prev.connectedAt) != null ? _a : Date.now(),
901
904
  state: "SUCCESS",
902
905
  isSuccess: true,
903
906
  isError: false,
@@ -960,7 +963,10 @@ const experimental_createStream = (connect, disconnect, options = {}) => {
960
963
  clearAllTimeouts(store);
961
964
  const { connectionState } = store.getState();
962
965
  if (connectionState === "INITIAL" || connectionState === "DISCONNECTED") {
963
- return store.connection.reconnect();
966
+ queueMicrotask(() => {
967
+ store.connection.reconnect();
968
+ });
969
+ return;
964
970
  }
965
971
  const shouldReconnect = reconnectOn(trigger, store.getState());
966
972
  if (shouldReconnect) store.connection.reconnect();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "floppy-disk",
3
3
  "description": "Lightweight unified state management for sync and async data.",
4
4
  "private": false,
5
- "version": "3.7.1",
5
+ "version": "3.7.2",
6
6
  "keywords": [
7
7
  "utilities",
8
8
  "store",
@@ -33,6 +33,50 @@ type StreamDataState<TData, TError> = {
33
33
  error: TError;
34
34
  errorUpdatedAt: number;
35
35
  };
36
+ /**
37
+ * Represents the full state of a stream.
38
+ *
39
+ * @remarks
40
+ * A stream consists of two independent concerns:
41
+ *
42
+ * 1. **Connection state** — lifecycle of the underlying connection
43
+ * 2. **Data state** — lifecycle of emitted data
44
+ *
45
+ * These two are combined into a single state object.
46
+ *
47
+ * ---
48
+ *
49
+ * ## Connection lifecycle
50
+ *
51
+ * - `INITIAL` → no connection has been established
52
+ * - `CONNECTING` → connection is being established
53
+ * - `CONNECTED` → connection is active
54
+ * - `DISCONNECTED` → connection was previously established but is now closed
55
+ *
56
+ * Timestamps:
57
+ * - `connectingAt` → when connection attempt started
58
+ * - `connectedAt` → when connection was established
59
+ * - `disconnectedAt` → when connection was closed
60
+ *
61
+ * ---
62
+ *
63
+ * ## Data lifecycle
64
+ *
65
+ * - `INITIAL` → no data has been received
66
+ * - `SUCCESS` → data has been received successfully
67
+ * - `ERROR` → error occurred before any data
68
+ * - `SUCCESS_BUT_THEN_ERROR` → data exists, but a later error occurred
69
+ *
70
+ * ---
71
+ *
72
+ * ## Notes
73
+ *
74
+ * - Connection state and data state evolve independently.
75
+ * - A stream may be:
76
+ * - connected but have no data yet
77
+ * - disconnected but still retain previous data
78
+ * - Errors do not necessarily reset data.
79
+ */
36
80
  export type StreamState<TData, TError> = ({
37
81
  connectionState: "INITIAL";
38
82
  connectingAt: undefined;
@@ -60,25 +104,218 @@ type DisconnectTrigger = "last-unsubscribe" | "document-hidden" | "offline";
60
104
  type ReconnectTrigger = "first-subscribe" | "document-visible" | "online";
61
105
  type AdditionalStoreApi<TConnection> = {
62
106
  variableHash: string;
107
+ /**
108
+ * Connection controls for the stream.
109
+ *
110
+ * @remarks
111
+ * Provides imperative control over the underlying connection.
112
+ */
63
113
  connection: {
114
+ /**
115
+ * Returns the current connection instance.
116
+ *
117
+ * @returns The active connection or `undefined` if not connected
118
+ */
64
119
  get: () => Readonly<TConnection> | undefined;
120
+ /**
121
+ * Forces a reconnection.
122
+ *
123
+ * @remarks
124
+ * - Cancels any scheduled disconnect
125
+ * - Starts a new connection if not already connecting
126
+ */
65
127
  reconnect: () => void;
128
+ /**
129
+ * Immediately disconnects the current connection.
130
+ *
131
+ * @remarks
132
+ * - Ignores disconnect delay rules
133
+ * - Updates connection state to `DISCONNECTED`
134
+ */
66
135
  disconnect: () => void;
67
136
  };
137
+ /**
138
+ * Data controls for the stream.
139
+ */
68
140
  data: {
141
+ /**
142
+ * Resets the data state back to `INITIAL`.
143
+ *
144
+ * @remarks
145
+ * - Does not affect connection state
146
+ * - Useful for clearing stale or invalid data
147
+ */
69
148
  reset: () => void;
70
149
  };
150
+ /**
151
+ * Deletes the stream instance.
152
+ *
153
+ * @returns `true` if deleted, `false` otherwise
154
+ *
155
+ * @remarks
156
+ * - Cannot delete while there are active subscribers
157
+ * - Clears connection, state, and cached instance
158
+ */
71
159
  delete: () => boolean;
72
160
  };
161
+ /**
162
+ * Configuration options for a stream.
163
+ *
164
+ * @remarks
165
+ * Controls connection lifecycle, reconnection behavior, and data retention.
166
+ */
73
167
  export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions<StreamState<TData, TError>, AdditionalStoreApi<TConnection>> & {
168
+ /**
169
+ * Connection-related behavior.
170
+ */
74
171
  connection?: {
172
+ /**
173
+ * Determines when a connection should be disconnected.
174
+ *
175
+ * @param trigger - The reason for the disconnect attempt
176
+ * @param state - Current stream state
177
+ *
178
+ * @returns
179
+ * - `number` → delay (ms) before disconnecting
180
+ * - `false` → prevent disconnection
181
+ *
182
+ * @default Disconnect after 5 seconds for any triggers
183
+ *
184
+ * @remarks
185
+ * Triggers:
186
+ * - `"last-unsubscribe"` → no active subscribers
187
+ * - `"document-hidden"` → tab becomes hidden
188
+ * - `"offline"` → network goes offline
189
+ */
75
190
  disconnectOn?: (trigger: DisconnectTrigger, state: StreamState<TData, TError>) => false | number;
191
+ /**
192
+ * Determines whether a connection should reconnect.
193
+ *
194
+ * @param trigger - The reason for the reconnect attempt
195
+ * @param state - Current stream state
196
+ *
197
+ * @returns `true` to reconnect, otherwise `false`
198
+ *
199
+ * @default No reconnection if already connected
200
+ *
201
+ * @remarks
202
+ * Triggers:
203
+ * - `"first-subscribe"` → first subscriber appears
204
+ * - `"document-visible"` → tab becomes visible
205
+ * - `"online"` → network reconnects
206
+ */
76
207
  reconnectOn?: (trigger: ReconnectTrigger, state: StreamState<TData, TError>) => boolean;
77
208
  };
209
+ /**
210
+ * Data-related behavior.
211
+ */
78
212
  data?: {
213
+ /**
214
+ * Time (in milliseconds) before unused stream data is garbage collected.
215
+ *
216
+ * Starts counting after disconnection.
217
+ *
218
+ * @default 5 minutes
219
+ */
79
220
  gcTime?: number;
80
221
  };
81
222
  };
223
+ /**
224
+ * Creates a stream factory for managing real-time connections.
225
+ *
226
+ * @param connect - Function to establish a connection
227
+ * @param disconnect - Function to close a connection
228
+ * @param options - Optional configuration for lifecycle and behavior
229
+ *
230
+ * @returns A function to retrieve or create a stream instance by variable
231
+ *
232
+ * @remarks
233
+ * This utility is designed for **long-lived, push-based async sources**, such as:
234
+ * - WebSocket
235
+ * - Server-Sent Events (SSE)
236
+ * - Firebase / realtime databases
237
+ *
238
+ * ---
239
+ *
240
+ * ## Key concepts
241
+ *
242
+ * ### 1. Connection lifecycle (managed automatically)
243
+ *
244
+ * - Connection is established when needed (e.g. first subscriber)
245
+ * - Connection may be disconnected based on triggers:
246
+ * - no subscribers
247
+ * - tab hidden
248
+ * - offline
249
+ * - Reconnection is controlled via `reconnectOn`
250
+ *
251
+ * ---
252
+ *
253
+ * ### 2. Data flow (push-based)
254
+ *
255
+ * The `connect` function receives an `emit` API:
256
+ *
257
+ * - `emit.connected()` → mark connection as established
258
+ * - `emit.data(fn)` → update data using reducer
259
+ * - `emit.error(err)` → report error
260
+ *
261
+ * Data updates are **incremental** and controlled by the stream source.
262
+ *
263
+ * ---
264
+ *
265
+ * ### 3. Store-per-variable
266
+ *
267
+ * - Each unique `variable` creates a separate stream instance
268
+ * - Variables are deterministically hashed for stable identity
269
+ * - Each instance manages its own:
270
+ * - connection
271
+ * - state
272
+ * - subscribers
273
+ *
274
+ * ---
275
+ *
276
+ * ### 4. React integration (Proxy-based)
277
+ *
278
+ * - The returned hook exposes the full state as a Proxy
279
+ * - Components automatically subscribe to accessed properties
280
+ * - No selector or memoization is required
281
+ *
282
+ * ---
283
+ *
284
+ * ## Execution model
285
+ *
286
+ * - Streams are **lazy**:
287
+ * - No connection until there is a subscriber
288
+ * - Streams are **shared**:
289
+ * - Multiple subscribers reuse the same connection
290
+ * - Streams are **stateful**:
291
+ * - Data persists across reconnects (unless reset or GC)
292
+ *
293
+ * ---
294
+ *
295
+ * @example
296
+ * const chatStream = createStream(
297
+ * (roomId, emit) => {
298
+ * const ws = new WebSocket(`/chat/${roomId}`);
299
+ *
300
+ * ws.onopen = () => emit.connected();
301
+ * ws.onmessage = (e) => {
302
+ * const msg = JSON.parse(e.data);
303
+ * emit.data((prev) => [...(prev ?? []), msg]);
304
+ * };
305
+ * ws.onerror = (err) => emit.error(err);
306
+ *
307
+ * return ws;
308
+ * },
309
+ * (ws) => ws.close()
310
+ * );
311
+ *
312
+ * function Chat({ roomId }) {
313
+ * const useChat = chatStream(roomId);
314
+ * const state = useChat();
315
+ *
316
+ * return <div>{state.data?.length}</div>;
317
+ * }
318
+ */
82
319
  export declare const experimental_createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
83
320
  connected: () => void;
84
321
  data: (reducer: (data: TData | undefined) => TData) => void;
@@ -91,14 +328,58 @@ export declare const experimental_createStream: <TConnection, TData, TVariable e
91
328
  subscribe: (subscriber: import("../vanilla.ts").Subscriber<StreamState<TData, TError>>) => () => void;
92
329
  getSubscriberCount: () => number;
93
330
  variableHash: string;
331
+ /**
332
+ * Connection controls for the stream.
333
+ *
334
+ * @remarks
335
+ * Provides imperative control over the underlying connection.
336
+ */
94
337
  connection: {
338
+ /**
339
+ * Returns the current connection instance.
340
+ *
341
+ * @returns The active connection or `undefined` if not connected
342
+ */
95
343
  get: () => Readonly<TConnection> | undefined;
344
+ /**
345
+ * Forces a reconnection.
346
+ *
347
+ * @remarks
348
+ * - Cancels any scheduled disconnect
349
+ * - Starts a new connection if not already connecting
350
+ */
96
351
  reconnect: () => void;
352
+ /**
353
+ * Immediately disconnects the current connection.
354
+ *
355
+ * @remarks
356
+ * - Ignores disconnect delay rules
357
+ * - Updates connection state to `DISCONNECTED`
358
+ */
97
359
  disconnect: () => void;
98
360
  };
361
+ /**
362
+ * Data controls for the stream.
363
+ */
99
364
  data: {
365
+ /**
366
+ * Resets the data state back to `INITIAL`.
367
+ *
368
+ * @remarks
369
+ * - Does not affect connection state
370
+ * - Useful for clearing stale or invalid data
371
+ */
100
372
  reset: () => void;
101
373
  };
374
+ /**
375
+ * Deletes the stream instance.
376
+ *
377
+ * @returns `true` if deleted, `false` otherwise
378
+ *
379
+ * @remarks
380
+ * - Cannot delete while there are active subscribers
381
+ * - Clears connection, state, and cached instance
382
+ */
102
383
  delete: () => boolean;
103
384
  };
104
385
  export {};
package/react.js CHANGED
@@ -875,11 +875,14 @@ const experimental_createStream = (connect, disconnect, options = {}) => {
875
875
  store.connection = {};
876
876
  store.connection.get = () => connections.get(store);
877
877
  store.connection.reconnect = () => {
878
- var _a;
879
878
  clearAllTimeouts(store);
880
879
  const { connectionState } = store.getState();
881
880
  if (connectionState === "CONNECTING") return;
882
- (_a = disconnectFns.get(store)) == null ? void 0 : _a();
881
+ const prevDisconnect = disconnectFns.get(store);
882
+ if (prevDisconnect) {
883
+ prevDisconnect();
884
+ disconnectFns.delete(store);
885
+ }
883
886
  store.setState({
884
887
  connectionState: "CONNECTING",
885
888
  connectingAt: Date.now(),
@@ -896,10 +899,10 @@ const experimental_createStream = (connect, disconnect, options = {}) => {
896
899
  },
897
900
  data: (reducer) => {
898
901
  store.setState((prev) => {
899
- var _a2;
902
+ var _a;
900
903
  return {
901
904
  connectionState: "CONNECTED",
902
- connectedAt: (_a2 = prev.connectedAt) != null ? _a2 : Date.now(),
905
+ connectedAt: (_a = prev.connectedAt) != null ? _a : Date.now(),
903
906
  state: "SUCCESS",
904
907
  isSuccess: true,
905
908
  isError: false,
@@ -962,7 +965,10 @@ const experimental_createStream = (connect, disconnect, options = {}) => {
962
965
  clearAllTimeouts(store);
963
966
  const { connectionState } = store.getState();
964
967
  if (connectionState === "INITIAL" || connectionState === "DISCONNECTED") {
965
- return store.connection.reconnect();
968
+ queueMicrotask(() => {
969
+ store.connection.reconnect();
970
+ });
971
+ return;
966
972
  }
967
973
  const shouldReconnect = reconnectOn(trigger, store.getState());
968
974
  if (shouldReconnect) store.connection.reconnect();