@adventurelabs/scout-core 1.0.34 → 1.0.36

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.
@@ -7,11 +7,13 @@ export function useScoutDbListener(scoutSupabase) {
7
7
  const channels = useRef([]);
8
8
  const dispatch = useAppDispatch();
9
9
  function handleTagInserts(payload) {
10
- console.log("[DB Listener] Tag INSERT received:", payload.new);
11
- dispatch(addTag(payload.new));
10
+ console.log("[DB Listener] Tag INSERT received:", payload);
11
+ // Broadcast payload contains the record directly
12
+ const tagData = payload.new || payload;
13
+ dispatch(addTag(tagData));
12
14
  }
13
15
  function handleTagDeletes(payload) {
14
- console.log("[DB Listener] Tag DELETE received:", payload.old);
16
+ console.log("[DB Listener] Tag DELETE received:", payload);
15
17
  console.log("[DB Listener] Tag DELETE - payload structure:", {
16
18
  hasOld: !!payload.old,
17
19
  oldId: payload.old?.id,
@@ -19,67 +21,87 @@ export function useScoutDbListener(scoutSupabase) {
19
21
  oldClassName: payload.old?.class_name,
20
22
  fullPayload: payload,
21
23
  });
22
- if (!payload.old || !payload.old.id) {
24
+ // Broadcast payload contains the old record
25
+ const tagData = payload.old || payload;
26
+ if (!tagData || !tagData.id) {
23
27
  console.error("[DB Listener] Tag DELETE - Invalid payload, missing tag data");
24
28
  return;
25
29
  }
26
- console.log("[DB Listener] Tag DELETE - Dispatching deleteTag action with ID:", payload.old.id);
27
- dispatch(deleteTag(payload.old));
30
+ console.log("[DB Listener] Tag DELETE - Dispatching deleteTag action with ID:", tagData.id);
31
+ dispatch(deleteTag(tagData));
28
32
  }
29
33
  function handleTagUpdates(payload) {
30
- console.log("[DB Listener] Tag UPDATE received:", payload.new);
31
- dispatch(updateTag(payload.new));
34
+ console.log("[DB Listener] Tag UPDATE received:", payload);
35
+ // Broadcast payload contains the new record
36
+ const tagData = payload.new || payload;
37
+ dispatch(updateTag(tagData));
32
38
  }
33
39
  async function handleDeviceInserts(payload) {
34
- console.log("[DB Listener] Device INSERT received:", payload.new);
35
- // For now, just dispatch the raw payload since we don't have the device helper
36
- dispatch(addDevice(payload.new));
40
+ console.log("[DB Listener] Device INSERT received:", payload);
41
+ // Broadcast payload contains the record directly
42
+ const deviceData = payload.new || payload;
43
+ dispatch(addDevice(deviceData));
37
44
  }
38
45
  function handleDeviceDeletes(payload) {
39
- console.log("[DB Listener] Device DELETE received:", payload.old);
40
- dispatch(deleteDevice(payload.old));
46
+ console.log("[DB Listener] Device DELETE received:", payload);
47
+ // Broadcast payload contains the old record
48
+ const deviceData = payload.old || payload;
49
+ dispatch(deleteDevice(deviceData));
41
50
  }
42
51
  async function handleDeviceUpdates(payload) {
43
- console.log("[DB Listener] Device UPDATE received:", payload.new);
44
- // For now, just dispatch the raw payload since we don't have the device helper
45
- dispatch(updateDevice(payload.new));
52
+ console.log("[DB Listener] Device UPDATE received:", payload);
53
+ // Broadcast payload contains the new record
54
+ const deviceData = payload.new || payload;
55
+ dispatch(updateDevice(deviceData));
46
56
  }
47
57
  function handlePlanInserts(payload) {
48
- console.log("[DB Listener] Plan INSERT received:", payload.new);
49
- dispatch(addPlan(payload.new));
58
+ console.log("[DB Listener] Plan INSERT received:", payload);
59
+ // Broadcast payload contains the record directly
60
+ const planData = payload.new || payload;
61
+ dispatch(addPlan(planData));
50
62
  }
51
63
  function handlePlanDeletes(payload) {
52
- console.log("[DB Listener] Plan DELETE received:", payload.old);
53
- dispatch(deletePlan(payload.old));
64
+ console.log("[DB Listener] Plan DELETE received:", payload);
65
+ // Broadcast payload contains the old record
66
+ const planData = payload.old || payload;
67
+ dispatch(deletePlan(planData));
54
68
  }
55
69
  function handlePlanUpdates(payload) {
56
- console.log("[DB Listener] Plan UPDATE received:", payload.new);
57
- dispatch(updatePlan(payload.new));
70
+ console.log("[DB Listener] Plan UPDATE received:", payload);
71
+ // Broadcast payload contains the new record
72
+ const planData = payload.new || payload;
73
+ dispatch(updatePlan(planData));
58
74
  }
59
75
  function handleSessionInserts(payload) {
60
- console.log("[DB Listener] Session INSERT received:", payload.new);
61
- dispatch(addSessionToStore(payload.new));
76
+ console.log("[DB Listener] Session INSERT received:", payload);
77
+ // Broadcast payload contains the record directly
78
+ const sessionData = payload.new || payload;
79
+ dispatch(addSessionToStore(sessionData));
62
80
  }
63
81
  function handleSessionDeletes(payload) {
64
- console.log("[DB Listener] Session DELETE received:", payload.old);
65
- dispatch(deleteSessionFromStore(payload.old));
82
+ console.log("[DB Listener] Session DELETE received:", payload);
83
+ // Broadcast payload contains the old record
84
+ const sessionData = payload.old || payload;
85
+ dispatch(deleteSessionFromStore(sessionData));
66
86
  }
67
87
  function handleSessionUpdates(payload) {
68
- console.log("[DB Listener] Session UPDATE received:", payload.new);
69
- dispatch(updateSessionInStore(payload.new));
88
+ console.log("[DB Listener] Session UPDATE received:", payload);
89
+ // Broadcast payload contains the new record
90
+ const sessionData = payload.new || payload;
91
+ dispatch(updateSessionInStore(sessionData));
70
92
  }
71
93
  function handleConnectivityInserts(payload) {
72
- console.log("[DB Listener] Connectivity INSERT received:", payload.new);
94
+ console.log("[DB Listener] Connectivity INSERT received:", payload);
73
95
  // For now, we'll just log connectivity changes since they're related to sessions
74
96
  // In the future, we might want to update session connectivity data
75
97
  }
76
98
  function handleConnectivityDeletes(payload) {
77
- console.log("[DB Listener] Connectivity DELETE received:", payload.old);
99
+ console.log("[DB Listener] Connectivity DELETE received:", payload);
78
100
  // For now, we'll just log connectivity changes since they're related to sessions
79
101
  // In the future, we might want to update session connectivity data
80
102
  }
81
103
  function handleConnectivityUpdates(payload) {
82
- console.log("[DB Listener] Connectivity UPDATE received:", payload.new);
104
+ console.log("[DB Listener] Connectivity UPDATE received:", payload);
83
105
  // For now, we'll just log connectivity changes since they're related to sessions
84
106
  // In the future, we might want to update session connectivity data
85
107
  }
@@ -99,47 +121,121 @@ export function useScoutDbListener(scoutSupabase) {
99
121
  console.log("[DB Listener] Auth test - Error:", error);
100
122
  }
101
123
  catch (err) {
102
- console.error("[DB Listener] Auth test failed:", err);
124
+ console.warn("[DB Listener] Auth test failed:", err);
103
125
  }
104
126
  };
105
127
  testAuth();
106
- // Create a single channel for all operations with unique name
107
- const channelName = `scout_realtime_${Date.now()}`;
108
- console.log("[DB Listener] Creating channel:", channelName);
109
- const mainChannel = scoutSupabase.channel(channelName);
110
- // Subscribe to all events
111
- mainChannel
112
- .on("postgres_changes", { event: "INSERT", schema: "public", table: "plans" }, handlePlanInserts)
113
- .on("postgres_changes", { event: "DELETE", schema: "public", table: "plans" }, handlePlanDeletes)
114
- .on("postgres_changes", { event: "UPDATE", schema: "public", table: "plans" }, handlePlanUpdates)
115
- .on("postgres_changes", { event: "INSERT", schema: "public", table: "devices" }, handleDeviceInserts)
116
- .on("postgres_changes", { event: "DELETE", schema: "public", table: "devices" }, handleDeviceDeletes)
117
- .on("postgres_changes", { event: "UPDATE", schema: "public", table: "devices" }, handleDeviceUpdates)
118
- .on("postgres_changes", { event: "INSERT", schema: "public", table: "tags" }, handleTagInserts)
119
- .on("postgres_changes", { event: "DELETE", schema: "public", table: "tags" }, handleTagDeletes)
120
- .on("postgres_changes", { event: "UPDATE", schema: "public", table: "tags" }, handleTagUpdates)
121
- .on("postgres_changes", { event: "INSERT", schema: "public", table: "connectivity" }, handleConnectivityInserts)
122
- .on("postgres_changes", { event: "DELETE", schema: "public", table: "connectivity" }, handleConnectivityDeletes)
123
- .on("postgres_changes", { event: "UPDATE", schema: "public", table: "connectivity" }, handleConnectivityUpdates)
124
- .on("postgres_changes", { event: "INSERT", schema: "public", table: "sessions" }, handleSessionInserts)
125
- .on("postgres_changes", { event: "DELETE", schema: "public", table: "sessions" }, handleSessionDeletes)
126
- .on("postgres_changes", { event: "UPDATE", schema: "public", table: "sessions" }, handleSessionUpdates)
127
- .subscribe((status) => {
128
- console.log("[DB Listener] Subscription status:", status);
129
- if (status === "SUBSCRIBED") {
130
- console.log("[DB Listener] ✅ Successfully subscribed to real-time updates");
131
- }
132
- else if (status === "CHANNEL_ERROR") {
133
- console.error("[DB Listener] ❌ Channel error occurred");
134
- }
135
- else if (status === "TIMED_OUT") {
136
- console.error("[DB Listener] ⏰ Subscription timed out");
128
+ // Set up authentication for Realtime Authorization (required for broadcast)
129
+ const setupRealtimeAuth = async () => {
130
+ try {
131
+ await scoutSupabase.realtime.setAuth();
132
+ console.log("[DB Listener] Realtime authentication set up successfully");
137
133
  }
138
- else if (status === "CLOSED") {
139
- console.log("[DB Listener] 🔒 Channel closed");
134
+ catch (err) {
135
+ console.warn("[DB Listener] Failed to set up realtime authentication:", err);
140
136
  }
137
+ };
138
+ setupRealtimeAuth();
139
+ // Create channels for each table using broadcast
140
+ const createBroadcastChannel = (tableName) => {
141
+ const channelName = `scout_broadcast_${tableName}_${Date.now()}`;
142
+ console.log(`[DB Listener] Creating broadcast channel for ${tableName}:`, channelName);
143
+ return scoutSupabase.channel(channelName, {
144
+ config: { private: true }, // Required for broadcast with Realtime Authorization
145
+ });
146
+ };
147
+ // Plans channel
148
+ const plansChannel = createBroadcastChannel("plans");
149
+ plansChannel
150
+ .on("broadcast", { event: "INSERT" }, (payload) => {
151
+ console.log("[DB Listener] Plans INSERT received:", payload);
152
+ handlePlanInserts(payload);
153
+ })
154
+ .on("broadcast", { event: "UPDATE" }, (payload) => {
155
+ console.log("[DB Listener] Plans UPDATE received:", payload);
156
+ handlePlanUpdates(payload);
157
+ })
158
+ .on("broadcast", { event: "DELETE" }, (payload) => {
159
+ console.log("[DB Listener] Plans DELETE received:", payload);
160
+ handlePlanDeletes(payload);
161
+ })
162
+ .subscribe((status) => {
163
+ console.log(`[DB Listener] Plans channel status:`, status);
164
+ });
165
+ // Devices channel
166
+ const devicesChannel = createBroadcastChannel("devices");
167
+ devicesChannel
168
+ .on("broadcast", { event: "INSERT" }, (payload) => {
169
+ console.log("[DB Listener] Devices INSERT received:", payload);
170
+ handleDeviceInserts(payload);
171
+ })
172
+ .on("broadcast", { event: "UPDATE" }, (payload) => {
173
+ console.log("[DB Listener] Devices UPDATE received:", payload);
174
+ handleDeviceUpdates(payload);
175
+ })
176
+ .on("broadcast", { event: "DELETE" }, (payload) => {
177
+ console.log("[DB Listener] Devices DELETE received:", payload);
178
+ handleDeviceDeletes(payload);
179
+ })
180
+ .subscribe((status) => {
181
+ console.log(`[DB Listener] Devices channel status:`, status);
182
+ });
183
+ // Tags channel
184
+ const tagsChannel = createBroadcastChannel("tags");
185
+ tagsChannel
186
+ .on("broadcast", { event: "INSERT" }, (payload) => {
187
+ console.log("[DB Listener] Tags INSERT received:", payload);
188
+ handleTagInserts(payload);
189
+ })
190
+ .on("broadcast", { event: "UPDATE" }, (payload) => {
191
+ console.log("[DB Listener] Tags UPDATE received:", payload);
192
+ handleTagUpdates(payload);
193
+ })
194
+ .on("broadcast", { event: "DELETE" }, (payload) => {
195
+ console.log("[DB Listener] Tags DELETE received:", payload);
196
+ handleTagDeletes(payload);
197
+ })
198
+ .subscribe((status) => {
199
+ console.log(`[DB Listener] Tags channel status:`, status);
200
+ });
201
+ // Sessions channel
202
+ const sessionsChannel = createBroadcastChannel("sessions");
203
+ sessionsChannel
204
+ .on("broadcast", { event: "INSERT" }, (payload) => {
205
+ console.log("[DB Listener] Sessions INSERT received:", payload);
206
+ handleSessionInserts(payload);
207
+ })
208
+ .on("broadcast", { event: "UPDATE" }, (payload) => {
209
+ console.log("[DB Listener] Sessions UPDATE received:", payload);
210
+ handleSessionUpdates(payload);
211
+ })
212
+ .on("broadcast", { event: "DELETE" }, (payload) => {
213
+ console.log("[DB Listener] Sessions DELETE received:", payload);
214
+ handleSessionDeletes(payload);
215
+ })
216
+ .subscribe((status) => {
217
+ console.log(`[DB Listener] Sessions channel status:`, status);
218
+ });
219
+ // Connectivity channel
220
+ const connectivityChannel = createBroadcastChannel("connectivity");
221
+ connectivityChannel
222
+ .on("broadcast", { event: "INSERT" }, (payload) => {
223
+ console.log("[DB Listener] Connectivity INSERT received:", payload);
224
+ handleConnectivityInserts(payload);
225
+ })
226
+ .on("broadcast", { event: "UPDATE" }, (payload) => {
227
+ console.log("[DB Listener] Connectivity UPDATE received:", payload);
228
+ handleConnectivityUpdates(payload);
229
+ })
230
+ .on("broadcast", { event: "DELETE" }, (payload) => {
231
+ console.log("[DB Listener] Connectivity DELETE received:", payload);
232
+ handleConnectivityDeletes(payload);
233
+ })
234
+ .subscribe((status) => {
235
+ console.log(`[DB Listener] Connectivity channel status:`, status);
141
236
  });
142
- channels.current.push(mainChannel);
237
+ // Add all channels to the channels array
238
+ channels.current.push(plansChannel, devicesChannel, tagsChannel, sessionsChannel, connectivityChannel);
143
239
  // Test the connection with system events
144
240
  const testChannelName = `test_connection_${Date.now()}`;
145
241
  console.log("[DB Listener] Creating test channel:", testChannelName);
@@ -152,7 +248,7 @@ export function useScoutDbListener(scoutSupabase) {
152
248
  console.log("[DB Listener] 🔗 Reconnected to Supabase");
153
249
  })
154
250
  .on("system", { event: "error" }, (error) => {
155
- console.error("[DB Listener] ❌ System error:", error);
251
+ console.warn("[DB Listener] ❌ System error:", error);
156
252
  })
157
253
  .subscribe((status) => {
158
254
  console.log("[DB Listener] Test channel status:", status);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
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",
@@ -26,7 +26,7 @@
26
26
  "license": "GPL-3.0",
27
27
  "repository": {
28
28
  "type": "git",
29
- "url": "https://github.com/Adventurous-Bytes/scout.git"
29
+ "url": "git+https://github.com/Adventurous-Bytes/scout.git"
30
30
  },
31
31
  "bugs": {
32
32
  "url": "https://github.com/Adventurous-Bytes/scout/issues"