@adventurelabs/scout-core 1.2.6 → 1.3.1

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.
@@ -0,0 +1,377 @@
1
+ import { createApi, fakeBaseQuery } from "@reduxjs/toolkit/query/react";
2
+ import { generateSignedUrlsBatch } from "../helpers/storage";
3
+ // Create the API slice
4
+ export const scoutApi = createApi({
5
+ reducerPath: "scoutApi",
6
+ baseQuery: fakeBaseQuery(),
7
+ tagTypes: ["Session", "Event", "Artifact"],
8
+ endpoints: (builder) => ({
9
+ // =====================================================
10
+ // SESSIONS INFINITE QUERIES
11
+ // =====================================================
12
+ getSessionsInfiniteByHerd: builder.query({
13
+ async queryFn({ herdId, limit = 20, cursor, supabase }) {
14
+ try {
15
+ if (!herdId) {
16
+ return {
17
+ error: { status: "CUSTOM_ERROR", error: "Herd ID is required" },
18
+ };
19
+ }
20
+ const { data, error } = await supabase.rpc("get_sessions_infinite_by_herd", {
21
+ herd_id_caller: herdId,
22
+ limit_caller: limit + 1, // Fetch one extra to determine if there are more
23
+ cursor_timestamp: cursor?.timestamp || null,
24
+ cursor_id: cursor?.id || null,
25
+ });
26
+ if (error) {
27
+ return {
28
+ error: { status: "SUPABASE_ERROR", error: error.message },
29
+ };
30
+ }
31
+ const sessions = data || [];
32
+ const hasMore = sessions.length > limit;
33
+ const resultSessions = hasMore ? sessions.slice(0, limit) : sessions;
34
+ const nextCursor = hasMore && resultSessions.length > 0
35
+ ? {
36
+ timestamp: resultSessions[resultSessions.length - 1].timestamp_start ||
37
+ "",
38
+ id: resultSessions[resultSessions.length - 1].id || 0,
39
+ }
40
+ : null;
41
+ return {
42
+ data: {
43
+ sessions: resultSessions,
44
+ nextCursor,
45
+ hasMore,
46
+ },
47
+ };
48
+ }
49
+ catch (err) {
50
+ return { error: { status: "FETCH_ERROR", error: String(err) } };
51
+ }
52
+ },
53
+ providesTags: (result) => result
54
+ ? [
55
+ ...result.sessions.map(({ id }) => ({
56
+ type: "Session",
57
+ id: id || "unknown",
58
+ })),
59
+ { type: "Session", id: "LIST" },
60
+ ]
61
+ : [{ type: "Session", id: "LIST" }],
62
+ }),
63
+ getSessionsInfiniteByDevice: builder.query({
64
+ async queryFn({ deviceId, limit = 20, cursor, supabase }) {
65
+ try {
66
+ if (!deviceId) {
67
+ return {
68
+ error: { status: "CUSTOM_ERROR", error: "Device ID is required" },
69
+ };
70
+ }
71
+ const { data, error } = await supabase.rpc("get_sessions_infinite_by_device", {
72
+ device_id_caller: deviceId,
73
+ limit_caller: limit + 1,
74
+ cursor_timestamp: cursor?.timestamp || null,
75
+ cursor_id: cursor?.id || null,
76
+ });
77
+ if (error) {
78
+ return {
79
+ error: { status: "SUPABASE_ERROR", error: error.message },
80
+ };
81
+ }
82
+ const sessions = data || [];
83
+ const hasMore = sessions.length > limit;
84
+ const resultSessions = hasMore ? sessions.slice(0, limit) : sessions;
85
+ const nextCursor = hasMore && resultSessions.length > 0
86
+ ? {
87
+ timestamp: resultSessions[resultSessions.length - 1].timestamp_start ||
88
+ "",
89
+ id: resultSessions[resultSessions.length - 1].id || 0,
90
+ }
91
+ : null;
92
+ return {
93
+ data: {
94
+ sessions: resultSessions,
95
+ nextCursor,
96
+ hasMore,
97
+ },
98
+ };
99
+ }
100
+ catch (err) {
101
+ return { error: { status: "FETCH_ERROR", error: String(err) } };
102
+ }
103
+ },
104
+ providesTags: (result) => result
105
+ ? [
106
+ ...result.sessions.map(({ id }) => ({
107
+ type: "Session",
108
+ id: id || "unknown",
109
+ })),
110
+ { type: "Session", id: "LIST" },
111
+ ]
112
+ : [{ type: "Session", id: "LIST" }],
113
+ }),
114
+ // =====================================================
115
+ // EVENTS INFINITE QUERIES
116
+ // =====================================================
117
+ getEventsInfiniteByHerd: builder.query({
118
+ async queryFn({ herdId, limit = 20, cursor, supabase }) {
119
+ try {
120
+ if (!herdId) {
121
+ return {
122
+ error: { status: "CUSTOM_ERROR", error: "Herd ID is required" },
123
+ };
124
+ }
125
+ const { data, error } = await supabase.rpc("get_events_infinite_by_herd", {
126
+ herd_id_caller: herdId,
127
+ limit_caller: limit + 1,
128
+ cursor_timestamp: cursor?.timestamp || null,
129
+ cursor_id: cursor?.id || null,
130
+ });
131
+ if (error) {
132
+ return {
133
+ error: { status: "SUPABASE_ERROR", error: error.message },
134
+ };
135
+ }
136
+ const events = data || [];
137
+ const hasMore = events.length > limit;
138
+ const resultEvents = hasMore ? events.slice(0, limit) : events;
139
+ const nextCursor = hasMore && resultEvents.length > 0
140
+ ? {
141
+ timestamp: resultEvents[resultEvents.length - 1]
142
+ .timestamp_observation || "",
143
+ id: resultEvents[resultEvents.length - 1].id || 0,
144
+ }
145
+ : null;
146
+ return {
147
+ data: {
148
+ events: resultEvents,
149
+ nextCursor,
150
+ hasMore,
151
+ },
152
+ };
153
+ }
154
+ catch (err) {
155
+ return { error: { status: "FETCH_ERROR", error: String(err) } };
156
+ }
157
+ },
158
+ providesTags: (result) => result
159
+ ? [
160
+ ...result.events.map(({ id }) => ({
161
+ type: "Event",
162
+ id: id || "unknown",
163
+ })),
164
+ { type: "Event", id: "LIST" },
165
+ ]
166
+ : [{ type: "Event", id: "LIST" }],
167
+ }),
168
+ getEventsInfiniteByDevice: builder.query({
169
+ async queryFn({ deviceId, limit = 20, cursor, supabase }) {
170
+ try {
171
+ if (!deviceId) {
172
+ return {
173
+ error: { status: "CUSTOM_ERROR", error: "Device ID is required" },
174
+ };
175
+ }
176
+ const { data, error } = await supabase.rpc("get_events_infinite_by_device", {
177
+ device_id_caller: deviceId,
178
+ limit_caller: limit + 1,
179
+ cursor_timestamp: cursor?.timestamp || null,
180
+ cursor_id: cursor?.id || null,
181
+ });
182
+ if (error) {
183
+ return {
184
+ error: { status: "SUPABASE_ERROR", error: error.message },
185
+ };
186
+ }
187
+ const events = data || [];
188
+ const hasMore = events.length > limit;
189
+ const resultEvents = hasMore ? events.slice(0, limit) : events;
190
+ const nextCursor = hasMore && resultEvents.length > 0
191
+ ? {
192
+ timestamp: resultEvents[resultEvents.length - 1]
193
+ .timestamp_observation || "",
194
+ id: resultEvents[resultEvents.length - 1].id || 0,
195
+ }
196
+ : null;
197
+ return {
198
+ data: {
199
+ events: resultEvents,
200
+ nextCursor,
201
+ hasMore,
202
+ },
203
+ };
204
+ }
205
+ catch (err) {
206
+ return { error: { status: "FETCH_ERROR", error: String(err) } };
207
+ }
208
+ },
209
+ providesTags: (result) => result
210
+ ? [
211
+ ...result.events.map(({ id }) => ({
212
+ type: "Event",
213
+ id: id || "unknown",
214
+ })),
215
+ { type: "Event", id: "LIST" },
216
+ ]
217
+ : [{ type: "Event", id: "LIST" }],
218
+ }),
219
+ // =====================================================
220
+ // ARTIFACTS INFINITE QUERIES
221
+ // =====================================================
222
+ getArtifactsInfiniteByHerd: builder.query({
223
+ async queryFn({ herdId, limit = 20, cursor, supabase }) {
224
+ try {
225
+ if (!herdId) {
226
+ return {
227
+ error: { status: "CUSTOM_ERROR", error: "Herd ID is required" },
228
+ };
229
+ }
230
+ const { data, error } = await supabase.rpc("get_artifacts_infinite_by_herd", {
231
+ herd_id_caller: herdId,
232
+ limit_caller: limit + 1,
233
+ cursor_timestamp: cursor?.timestamp || null,
234
+ cursor_id: cursor?.id || null,
235
+ });
236
+ if (error) {
237
+ return {
238
+ error: { status: "SUPABASE_ERROR", error: error.message },
239
+ };
240
+ }
241
+ const artifacts = data || [];
242
+ const hasMore = artifacts.length > limit;
243
+ const resultArtifacts = hasMore
244
+ ? artifacts.slice(0, limit)
245
+ : artifacts;
246
+ // Generate signed URLs for artifacts
247
+ const uniqueFilePaths = Array.from(new Set(resultArtifacts
248
+ .map((artifact) => artifact.file_path)
249
+ .filter((path) => path !== null && path !== undefined)));
250
+ let urlMap = new Map();
251
+ if (uniqueFilePaths.length > 0) {
252
+ try {
253
+ const urlResults = await generateSignedUrlsBatch(uniqueFilePaths);
254
+ urlResults.forEach((url, index) => {
255
+ if (url) {
256
+ urlMap.set(uniqueFilePaths[index], url);
257
+ }
258
+ });
259
+ }
260
+ catch (urlError) {
261
+ console.warn("Failed to generate signed URLs for artifacts:", urlError);
262
+ }
263
+ }
264
+ const artifactsWithUrls = resultArtifacts.map((artifact) => ({
265
+ ...artifact,
266
+ media_url: artifact.file_path
267
+ ? urlMap.get(artifact.file_path) || null
268
+ : null,
269
+ }));
270
+ const nextCursor = hasMore && resultArtifacts.length > 0
271
+ ? {
272
+ timestamp: resultArtifacts[resultArtifacts.length - 1].created_at,
273
+ id: resultArtifacts[resultArtifacts.length - 1].id,
274
+ }
275
+ : null;
276
+ return {
277
+ data: {
278
+ artifacts: artifactsWithUrls,
279
+ nextCursor,
280
+ hasMore,
281
+ },
282
+ };
283
+ }
284
+ catch (err) {
285
+ return { error: { status: "FETCH_ERROR", error: String(err) } };
286
+ }
287
+ },
288
+ providesTags: (result) => result
289
+ ? [
290
+ ...result.artifacts.map(({ id }) => ({
291
+ type: "Artifact",
292
+ id,
293
+ })),
294
+ { type: "Artifact", id: "LIST" },
295
+ ]
296
+ : [{ type: "Artifact", id: "LIST" }],
297
+ }),
298
+ getArtifactsInfiniteByDevice: builder.query({
299
+ async queryFn({ deviceId, limit = 20, cursor, supabase }) {
300
+ try {
301
+ if (!deviceId) {
302
+ return {
303
+ error: { status: "CUSTOM_ERROR", error: "Device ID is required" },
304
+ };
305
+ }
306
+ const { data, error } = await supabase.rpc("get_artifacts_infinite_by_device", {
307
+ device_id_caller: deviceId,
308
+ limit_caller: limit + 1,
309
+ cursor_timestamp: cursor?.timestamp || null,
310
+ cursor_id: cursor?.id || null,
311
+ });
312
+ if (error) {
313
+ return {
314
+ error: { status: "SUPABASE_ERROR", error: error.message },
315
+ };
316
+ }
317
+ const artifacts = data || [];
318
+ const hasMore = artifacts.length > limit;
319
+ const resultArtifacts = hasMore
320
+ ? artifacts.slice(0, limit)
321
+ : artifacts;
322
+ // Generate signed URLs for artifacts
323
+ const uniqueFilePaths = Array.from(new Set(resultArtifacts
324
+ .map((artifact) => artifact.file_path)
325
+ .filter((path) => path !== null && path !== undefined)));
326
+ let urlMap = new Map();
327
+ if (uniqueFilePaths.length > 0) {
328
+ try {
329
+ const urlResults = await generateSignedUrlsBatch(uniqueFilePaths);
330
+ urlResults.forEach((url, index) => {
331
+ if (url) {
332
+ urlMap.set(uniqueFilePaths[index], url);
333
+ }
334
+ });
335
+ }
336
+ catch (urlError) {
337
+ console.warn("Failed to generate signed URLs for artifacts:", urlError);
338
+ }
339
+ }
340
+ const artifactsWithUrls = resultArtifacts.map((artifact) => ({
341
+ ...artifact,
342
+ media_url: artifact.file_path
343
+ ? urlMap.get(artifact.file_path) || null
344
+ : null,
345
+ }));
346
+ const nextCursor = hasMore && resultArtifacts.length > 0
347
+ ? {
348
+ timestamp: resultArtifacts[resultArtifacts.length - 1].created_at,
349
+ id: resultArtifacts[resultArtifacts.length - 1].id,
350
+ }
351
+ : null;
352
+ return {
353
+ data: {
354
+ artifacts: artifactsWithUrls,
355
+ nextCursor,
356
+ hasMore,
357
+ },
358
+ };
359
+ }
360
+ catch (err) {
361
+ return { error: { status: "FETCH_ERROR", error: String(err) } };
362
+ }
363
+ },
364
+ providesTags: (result) => result
365
+ ? [
366
+ ...result.artifacts.map(({ id }) => ({
367
+ type: "Artifact",
368
+ id,
369
+ })),
370
+ { type: "Artifact", id: "LIST" },
371
+ ]
372
+ : [{ type: "Artifact", id: "LIST" }],
373
+ }),
374
+ }),
375
+ });
376
+ // Export hooks for usage in functional components
377
+ export const { useGetSessionsInfiniteByHerdQuery, useGetSessionsInfiniteByDeviceQuery, useGetEventsInfiniteByHerdQuery, useGetEventsInfiniteByDeviceQuery, useGetArtifactsInfiniteByHerdQuery, useGetArtifactsInfiniteByDeviceQuery, } = scoutApi;
@@ -0,0 +1,96 @@
1
+ export declare const configureScoutStore: () => import("@reduxjs/toolkit").EnhancedStore<{
2
+ scout: import("./scout").ScoutState;
3
+ scoutApi: import("@reduxjs/toolkit/query").CombinedState<{
4
+ getSessionsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
5
+ supabase: import("@supabase/supabase-js").SupabaseClient;
6
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
7
+ getSessionsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
8
+ supabase: import("@supabase/supabase-js").SupabaseClient;
9
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
10
+ getEventsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
11
+ supabase: import("@supabase/supabase-js").SupabaseClient;
12
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
13
+ getEventsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
14
+ supabase: import("@supabase/supabase-js").SupabaseClient;
15
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
16
+ getArtifactsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
17
+ supabase: import("@supabase/supabase-js").SupabaseClient;
18
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
19
+ getArtifactsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
20
+ supabase: import("@supabase/supabase-js").SupabaseClient;
21
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
22
+ }, "Session" | "Event" | "Artifact", "scoutApi">;
23
+ }, import("redux").UnknownAction, import("@reduxjs/toolkit").Tuple<[import("redux").StoreEnhancer<{
24
+ dispatch: import("redux-thunk").ThunkDispatch<{
25
+ scout: import("./scout").ScoutState;
26
+ scoutApi: import("@reduxjs/toolkit/query").CombinedState<{
27
+ getSessionsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
28
+ supabase: import("@supabase/supabase-js").SupabaseClient;
29
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
30
+ getSessionsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
31
+ supabase: import("@supabase/supabase-js").SupabaseClient;
32
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
33
+ getEventsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
34
+ supabase: import("@supabase/supabase-js").SupabaseClient;
35
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
36
+ getEventsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
37
+ supabase: import("@supabase/supabase-js").SupabaseClient;
38
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
39
+ getArtifactsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
40
+ supabase: import("@supabase/supabase-js").SupabaseClient;
41
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
42
+ getArtifactsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
43
+ supabase: import("@supabase/supabase-js").SupabaseClient;
44
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
45
+ }, "Session" | "Event" | "Artifact", "scoutApi">;
46
+ }, undefined, import("redux").UnknownAction>;
47
+ }>, import("redux").StoreEnhancer]>>;
48
+ export type RootState = ReturnType<ReturnType<typeof configureScoutStore>["getState"]>;
49
+ export type AppDispatch = ReturnType<typeof configureScoutStore>["dispatch"];
50
+ export declare const store: import("@reduxjs/toolkit").EnhancedStore<{
51
+ scout: import("./scout").ScoutState;
52
+ scoutApi: import("@reduxjs/toolkit/query").CombinedState<{
53
+ getSessionsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
54
+ supabase: import("@supabase/supabase-js").SupabaseClient;
55
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
56
+ getSessionsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
57
+ supabase: import("@supabase/supabase-js").SupabaseClient;
58
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
59
+ getEventsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
60
+ supabase: import("@supabase/supabase-js").SupabaseClient;
61
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
62
+ getEventsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
63
+ supabase: import("@supabase/supabase-js").SupabaseClient;
64
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
65
+ getArtifactsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
66
+ supabase: import("@supabase/supabase-js").SupabaseClient;
67
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
68
+ getArtifactsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
69
+ supabase: import("@supabase/supabase-js").SupabaseClient;
70
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
71
+ }, "Session" | "Event" | "Artifact", "scoutApi">;
72
+ }, import("redux").UnknownAction, import("@reduxjs/toolkit").Tuple<[import("redux").StoreEnhancer<{
73
+ dispatch: import("redux-thunk").ThunkDispatch<{
74
+ scout: import("./scout").ScoutState;
75
+ scoutApi: import("@reduxjs/toolkit/query").CombinedState<{
76
+ getSessionsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
77
+ supabase: import("@supabase/supabase-js").SupabaseClient;
78
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
79
+ getSessionsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
80
+ supabase: import("@supabase/supabase-js").SupabaseClient;
81
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").SessionsInfiniteResponse, "scoutApi", unknown>;
82
+ getEventsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
83
+ supabase: import("@supabase/supabase-js").SupabaseClient;
84
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
85
+ getEventsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
86
+ supabase: import("@supabase/supabase-js").SupabaseClient;
87
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").EventsInfiniteResponse, "scoutApi", unknown>;
88
+ getArtifactsInfiniteByHerd: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
89
+ supabase: import("@supabase/supabase-js").SupabaseClient;
90
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
91
+ getArtifactsInfiniteByDevice: import("@reduxjs/toolkit/query").QueryDefinition<import("./api").InfiniteQueryArgs & {
92
+ supabase: import("@supabase/supabase-js").SupabaseClient;
93
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<any, unknown, unknown, {}, {}>, "Session" | "Event" | "Artifact", import("./api").ArtifactsInfiniteResponse, "scoutApi", unknown>;
94
+ }, "Session" | "Event" | "Artifact", "scoutApi">;
95
+ }, undefined, import("redux").UnknownAction>;
96
+ }>, import("redux").StoreEnhancer]>>;
@@ -0,0 +1,40 @@
1
+ import { configureStore } from "@reduxjs/toolkit";
2
+ import { setupListeners } from "@reduxjs/toolkit/query";
3
+ import scoutReducer from "./scout";
4
+ import { scoutApi } from "./api";
5
+ // Configure the Redux store
6
+ export const configureScoutStore = () => {
7
+ const store = configureStore({
8
+ reducer: {
9
+ scout: scoutReducer,
10
+ [scoutApi.reducerPath]: scoutApi.reducer,
11
+ },
12
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware({
13
+ serializableCheck: {
14
+ // Ignore these action types for serializable check
15
+ ignoredActions: [
16
+ "persist/PERSIST",
17
+ "persist/REHYDRATE",
18
+ "persist/REGISTER",
19
+ // RTK Query actions
20
+ "scoutApi/executeQuery/pending",
21
+ "scoutApi/executeQuery/fulfilled",
22
+ "scoutApi/executeQuery/rejected",
23
+ "scoutApi/executeMutation/pending",
24
+ "scoutApi/executeMutation/fulfilled",
25
+ "scoutApi/executeMutation/rejected",
26
+ ],
27
+ // Ignore these field paths in all actions
28
+ ignoredActionsPaths: ["meta.arg", "payload.timestamp"],
29
+ // Ignore these paths in the state
30
+ ignoredPaths: ["scoutApi.queries", "scoutApi.mutations"],
31
+ },
32
+ }).concat(scoutApi.middleware),
33
+ devTools: process.env.NODE_ENV !== "production",
34
+ });
35
+ // Enable listener behavior for the store
36
+ setupListeners(store.dispatch);
37
+ return store;
38
+ };
39
+ // Create a default store instance
40
+ export const store = configureScoutStore();
@@ -1,3 +1,5 @@
1
+ import { LoadingPerformance } from "./scout";
2
+ import { ISessionSummary } from "../types/db";
1
3
  export declare const useAppDispatch: import("react-redux").UseDispatch<import("redux").Dispatch<import("redux").UnknownAction>>;
2
4
  export declare const useHerdModulesLoadingState: () => any;
3
5
  export declare const useIsHerdModulesLoading: () => boolean;
@@ -5,4 +7,7 @@ export declare const useIsHerdModulesLoaded: () => boolean;
5
7
  export declare const useIsHerdModulesFailed: () => boolean;
6
8
  export declare const useHerdModulesLoadedAt: () => any;
7
9
  export declare const useHerdModulesLoadingDuration: () => any;
10
+ export declare const useLoadingPerformance: () => LoadingPerformance;
11
+ export declare const useSessionSummariesByHerd: (herdId: number) => ISessionSummary | null;
12
+ export declare const useHasSessionSummaries: (herdId: number) => boolean;
8
13
  export declare const useHerdModulesLoadingTimeAgo: () => string | null;
@@ -23,18 +23,36 @@ export const useIsHerdModulesFailed = () => {
23
23
  };
24
24
  // Selector hook for getting when herd modules were last loaded
25
25
  export const useHerdModulesLoadedAt = () => {
26
- return useSelector((state) => state.scout.herd_modules_loaded_in_ms);
26
+ return useSelector((state) => state.scout.loading_performance.herd_modules_loaded_in_ms);
27
27
  };
28
28
  // Selector hook for getting the loading duration in milliseconds
29
29
  export const useHerdModulesLoadingDuration = () => {
30
30
  return useSelector((state) => {
31
- return state.scout.herd_modules_loaded_in_ms;
31
+ return state.scout.loading_performance.herd_modules_loaded_in_ms;
32
+ });
33
+ };
34
+ // Selector hook for getting the complete loading performance object
35
+ export const useLoadingPerformance = () => {
36
+ return useSelector((state) => state.scout.loading_performance);
37
+ };
38
+ // Selector hook for getting session summaries for a specific herd
39
+ export const useSessionSummariesByHerd = (herdId) => {
40
+ return useSelector((state) => {
41
+ const herdModule = state.scout.herd_modules.find((hm) => hm.herd.id === herdId);
42
+ return herdModule?.session_summaries || null;
43
+ });
44
+ };
45
+ // Selector hook for checking if session summaries are available for a herd
46
+ export const useHasSessionSummaries = (herdId) => {
47
+ return useSelector((state) => {
48
+ const herdModule = state.scout.herd_modules.find((hm) => hm.herd.id === herdId);
49
+ return !!herdModule?.session_summaries;
32
50
  });
33
51
  };
34
52
  // Selector hook for getting formatted loading time (e.g., "2.5s ago")
35
53
  export const useHerdModulesLoadingTimeAgo = () => {
36
54
  return useSelector((state) => {
37
- const loadingDuration = state.scout.herd_modules_loaded_in_ms;
55
+ const loadingDuration = state.scout.loading_performance.herd_modules_loaded_in_ms;
38
56
  if (!loadingDuration)
39
57
  return null;
40
58
  // Since we store the duration, we need to calculate when it was loaded
@@ -1 +1,2 @@
1
1
  export * from "./scout";
2
+ export * from "./api";
@@ -1 +1,2 @@
1
1
  export * from "./scout";
2
+ export * from "./api";