@adventurelabs/scout-core 1.0.55 → 1.0.57

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.
@@ -6,6 +6,12 @@ export function useScoutDbListener(scoutSupabase) {
6
6
  const supabase = useRef(null);
7
7
  const channels = useRef([]);
8
8
  const dispatch = useAppDispatch();
9
+ const reconnectTimeoutRef = useRef(null);
10
+ const reconnectAttemptsRef = useRef(0);
11
+ const maxReconnectAttempts = 10;
12
+ const baseDelay = 1000; // 1 second
13
+ const maxDelay = 5000; // 5 seconds
14
+ const lastChannelIdRef = useRef(null);
9
15
  function handleTagInserts(payload) {
10
16
  console.log("[DB Listener] Tag INSERT received:", payload.new);
11
17
  dispatch(addTag(payload.new));
@@ -71,15 +77,25 @@ export function useScoutDbListener(scoutSupabase) {
71
77
  function handleConnectivityUpdates(payload) {
72
78
  console.log("[DB Listener] Connectivity UPDATE received:", payload.new);
73
79
  }
74
- useEffect(() => {
75
- if (!scoutSupabase) {
76
- console.error("[DB Listener] No Supabase client available");
77
- return;
78
- }
79
- supabase.current = scoutSupabase;
80
- // Create a single channel for all operations
81
- const channelName = `scout_realtime_${Date.now()}`;
82
- const mainChannel = scoutSupabase.channel(channelName);
80
+ // Clean up all channels
81
+ const cleanupChannels = () => {
82
+ channels.current.forEach((channel) => {
83
+ if (channel) {
84
+ scoutSupabase.removeChannel(channel);
85
+ }
86
+ });
87
+ channels.current = [];
88
+ };
89
+ // Setup channel with event handlers
90
+ const setupChannel = () => {
91
+ if (!scoutSupabase)
92
+ return null;
93
+ const channelId = `scout_realtime_${Date.now()}_${Math.random()
94
+ .toString(36)
95
+ .substr(2, 9)}`;
96
+ const mainChannel = scoutSupabase.channel(channelId);
97
+ lastChannelIdRef.current = channelId;
98
+ console.log(`[DB Listener] Creating channel: ${channelId}`);
83
99
  // Subscribe to all events
84
100
  mainChannel
85
101
  .on("postgres_changes", { event: "INSERT", schema: "public", table: "plans" }, handlePlanInserts)
@@ -101,27 +117,75 @@ export function useScoutDbListener(scoutSupabase) {
101
117
  console.log("[DB Listener] Subscription status:", status);
102
118
  if (status === "SUBSCRIBED") {
103
119
  console.log("[DB Listener] ✅ Successfully subscribed to real-time updates");
120
+ reconnectAttemptsRef.current = 0; // Reset reconnect attempts on successful connection
104
121
  }
105
122
  else if (status === "CHANNEL_ERROR") {
106
- console.error("[DB Listener] ❌ Channel error occurred");
123
+ console.warn("[DB Listener] ❌ Channel error occurred. Reconnecting...");
124
+ handleReconnect();
107
125
  }
108
126
  else if (status === "TIMED_OUT") {
109
- console.error("[DB Listener] ⏰ Subscription timed out");
127
+ console.warn("[DB Listener] ⏰ Subscription timed out. Reconnecting...");
128
+ handleReconnect();
110
129
  }
111
130
  else if (status === "CLOSED") {
112
- console.log("[DB Listener] 🔒 Channel closed");
131
+ console.log(`[DB Listener] 🔒 Channel closed: ${lastChannelIdRef.current}`);
132
+ // Only reconnect if this isn't an immediate closure after subscription
133
+ if (reconnectAttemptsRef.current > 0) {
134
+ console.log("[DB Listener] Reconnecting...");
135
+ handleReconnect();
136
+ }
137
+ else {
138
+ console.log("[DB Listener] Channel closed immediately after subscription, not reconnecting");
139
+ }
113
140
  }
114
141
  });
115
- channels.current.push(mainChannel);
142
+ return mainChannel;
143
+ };
144
+ // Handle reconnection with exponential backoff
145
+ const handleReconnect = () => {
146
+ if (reconnectAttemptsRef.current >= maxReconnectAttempts) {
147
+ console.error("[DB Listener] 🚫 Max reconnection attempts reached");
148
+ return;
149
+ }
150
+ // Clear any existing timeout
151
+ if (reconnectTimeoutRef.current) {
152
+ clearTimeout(reconnectTimeoutRef.current);
153
+ }
154
+ const delay = Math.min(baseDelay * (reconnectAttemptsRef.current + 1), maxDelay);
155
+ console.log(`[DB Listener] 🔄 Attempting reconnection in ${delay}ms (attempt ${reconnectAttemptsRef.current + 1}/${maxReconnectAttempts})`);
156
+ reconnectTimeoutRef.current = setTimeout(() => {
157
+ reconnectAttemptsRef.current++;
158
+ cleanupChannels();
159
+ const newChannel = setupChannel();
160
+ if (newChannel) {
161
+ channels.current.push(newChannel);
162
+ }
163
+ }, delay);
164
+ };
165
+ useEffect(() => {
166
+ if (!scoutSupabase) {
167
+ console.error("[DB Listener] No Supabase client available");
168
+ return;
169
+ }
170
+ supabase.current = scoutSupabase;
171
+ // Initial channel setup
172
+ const mainChannel = setupChannel();
173
+ if (mainChannel) {
174
+ channels.current.push(mainChannel);
175
+ }
116
176
  // Cleanup function
117
177
  return () => {
118
178
  console.log("[DB Listener] 🧹 Cleaning up channels");
119
- channels.current.forEach((channel) => {
120
- if (channel) {
121
- scoutSupabase.removeChannel(channel);
122
- }
123
- });
124
- channels.current = [];
179
+ // Clear any pending reconnection attempts
180
+ if (reconnectTimeoutRef.current) {
181
+ clearTimeout(reconnectTimeoutRef.current);
182
+ reconnectTimeoutRef.current = null;
183
+ }
184
+ // Reset reconnect attempts and channel tracking
185
+ reconnectAttemptsRef.current = 0;
186
+ lastChannelIdRef.current = null;
187
+ // Clean up channels
188
+ cleanupChannels();
125
189
  };
126
190
  }, [scoutSupabase, dispatch]);
127
191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "description": "Core utilities and helpers for Adventure Labs Scout applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -44,8 +44,8 @@
44
44
  "typescript": "^5.0.0"
45
45
  },
46
46
  "dependencies": {
47
- "@supabase/supabase-js": "^2.45.6",
48
- "@supabase/ssr": "^0.5.1",
47
+ "@supabase/supabase-js": "^2.53.6",
48
+ "@supabase/ssr": "^0.6.1",
49
49
  "@reduxjs/toolkit": "^2.0.0",
50
50
  "zustand": "^4.0.0"
51
51
  }