@nimbleflux/fluxbase-sdk-react 2026.5.4 → 2026.5.5-rc.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.
- package/package.json +2 -2
- package/src/index.ts +10 -0
- package/src/use-branches.ts +21 -0
- package/src/use-functions.ts +13 -0
- package/src/use-jobs.ts +33 -0
- package/src/use-query.ts +25 -10
- package/src/use-tenant.ts +55 -75
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimbleflux/fluxbase-sdk-react",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.5-rc.2",
|
|
4
4
|
"description": "React hooks for Fluxbase SDK",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@nimbleflux/fluxbase-sdk": "^2026.5.
|
|
42
|
+
"@nimbleflux/fluxbase-sdk": "^2026.5.5-rc.2",
|
|
43
43
|
"@tanstack/react-query": "^5.96.2",
|
|
44
44
|
"react": "^18.0.0 || ^19.0.0"
|
|
45
45
|
},
|
package/src/index.ts
CHANGED
|
@@ -135,6 +135,15 @@ export {
|
|
|
135
135
|
type UseTableDetailsReturn,
|
|
136
136
|
} from "./use-table-export";
|
|
137
137
|
|
|
138
|
+
// Function hooks
|
|
139
|
+
export { useInvokeFunction } from "./use-functions";
|
|
140
|
+
|
|
141
|
+
// Job hooks
|
|
142
|
+
export { useSubmitJob, useJobStatus } from "./use-jobs";
|
|
143
|
+
|
|
144
|
+
// Branching hooks
|
|
145
|
+
export { useBranches } from "./use-branches";
|
|
146
|
+
|
|
138
147
|
// Re-export types from SDK
|
|
139
148
|
export type {
|
|
140
149
|
FluxbaseClient,
|
|
@@ -144,6 +153,7 @@ export type {
|
|
|
144
153
|
SignUpCredentials,
|
|
145
154
|
PostgrestResponse,
|
|
146
155
|
RealtimeChangePayload,
|
|
156
|
+
// @deprecated backward compat aliases
|
|
147
157
|
StorageObject,
|
|
148
158
|
AdminUser,
|
|
149
159
|
EnrichedUser,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query"
|
|
2
|
+
import { useFluxbaseClient } from "./context"
|
|
3
|
+
|
|
4
|
+
export interface UseBranchesOptions {
|
|
5
|
+
status?: "creating" | "ready" | "migrating" | "error" | "deleting" | "deleted";
|
|
6
|
+
type?: "main" | "preview" | "persistent";
|
|
7
|
+
limit?: number;
|
|
8
|
+
offset?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useBranches(options?: UseBranchesOptions) {
|
|
12
|
+
const client = useFluxbaseClient()
|
|
13
|
+
return useQuery({
|
|
14
|
+
queryKey: ["fluxbase", "branches", options],
|
|
15
|
+
queryFn: async (): Promise<unknown> => {
|
|
16
|
+
const { data, error } = await client.branching.list(options)
|
|
17
|
+
if (error) throw error
|
|
18
|
+
return data
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useMutation } from "@tanstack/react-query"
|
|
2
|
+
import { useFluxbaseClient } from "./context"
|
|
3
|
+
|
|
4
|
+
export function useInvokeFunction() {
|
|
5
|
+
const client = useFluxbaseClient()
|
|
6
|
+
return useMutation({
|
|
7
|
+
mutationFn: async ({ name, payload, method }: { name: string; payload?: unknown; method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" }) => {
|
|
8
|
+
const { data, error } = await client.functions.invoke(name, { body: payload, method })
|
|
9
|
+
if (error) throw error
|
|
10
|
+
return data
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
}
|
package/src/use-jobs.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
|
2
|
+
import { useFluxbaseClient } from "./context"
|
|
3
|
+
|
|
4
|
+
export function useSubmitJob() {
|
|
5
|
+
const client = useFluxbaseClient()
|
|
6
|
+
const queryClient = useQueryClient()
|
|
7
|
+
return useMutation({
|
|
8
|
+
mutationFn: async (params: { name: string; payload?: unknown }): Promise<unknown> => {
|
|
9
|
+
const { data, error } = await client.jobs.submit(params.name, params.payload)
|
|
10
|
+
if (error) throw error
|
|
11
|
+
return data
|
|
12
|
+
},
|
|
13
|
+
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["fluxbase", "jobs"] }) },
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useJobStatus(jobId: string | null) {
|
|
18
|
+
const client = useFluxbaseClient()
|
|
19
|
+
return useQuery({
|
|
20
|
+
queryKey: ["fluxbase", "jobs", jobId],
|
|
21
|
+
queryFn: async (): Promise<unknown> => {
|
|
22
|
+
if (!jobId) return null
|
|
23
|
+
const { data, error } = await client.jobs.get(jobId)
|
|
24
|
+
if (error) throw error
|
|
25
|
+
return data
|
|
26
|
+
},
|
|
27
|
+
enabled: !!jobId,
|
|
28
|
+
refetchInterval: (query) => {
|
|
29
|
+
const status = (query.state.data as Record<string, unknown> | null)?.status as string | undefined
|
|
30
|
+
return status === "pending" || status === "running" ? 2000 : false
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
}
|
package/src/use-query.ts
CHANGED
|
@@ -43,17 +43,22 @@ export function useFluxbaseQuery<T = any>(
|
|
|
43
43
|
options?: UseFluxbaseQueryOptions<T>,
|
|
44
44
|
) {
|
|
45
45
|
const client = useFluxbaseClient();
|
|
46
|
+
const tenantId = client.getTenantId();
|
|
47
|
+
|
|
48
|
+
const { queryKey: customKey, ...queryOptions } = options || {};
|
|
46
49
|
|
|
47
50
|
// Require queryKey for stable caching - function.toString() is not reliable
|
|
48
51
|
// as it can vary between renders for inline functions
|
|
49
|
-
if (!
|
|
52
|
+
if (!customKey) {
|
|
50
53
|
console.warn(
|
|
51
54
|
"[useFluxbaseQuery] No queryKey provided. This may cause cache misses. " +
|
|
52
55
|
"Please provide a stable queryKey in options.",
|
|
53
56
|
);
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
const queryKey =
|
|
59
|
+
const queryKey = customKey
|
|
60
|
+
? ["fluxbase", tenantId ?? null, ...customKey]
|
|
61
|
+
: ["fluxbase", tenantId ?? null, "query", "unstable"];
|
|
57
62
|
|
|
58
63
|
return useQuery({
|
|
59
64
|
queryKey,
|
|
@@ -67,7 +72,7 @@ export function useFluxbaseQuery<T = any>(
|
|
|
67
72
|
|
|
68
73
|
return (Array.isArray(data) ? data : data ? [data] : []) as T[];
|
|
69
74
|
},
|
|
70
|
-
...
|
|
75
|
+
...queryOptions,
|
|
71
76
|
});
|
|
72
77
|
}
|
|
73
78
|
|
|
@@ -113,8 +118,7 @@ export function useTable<T = any>(
|
|
|
113
118
|
},
|
|
114
119
|
{
|
|
115
120
|
...options,
|
|
116
|
-
|
|
117
|
-
queryKey: options?.queryKey || ["fluxbase", "table", table],
|
|
121
|
+
queryKey: options?.queryKey || ["table", table],
|
|
118
122
|
},
|
|
119
123
|
);
|
|
120
124
|
}
|
|
@@ -138,8 +142,10 @@ export function useInsert<T = any>(table: string) {
|
|
|
138
142
|
return result;
|
|
139
143
|
},
|
|
140
144
|
onSuccess: () => {
|
|
141
|
-
|
|
142
|
-
queryClient.invalidateQueries({
|
|
145
|
+
const tenantId = client.getTenantId();
|
|
146
|
+
queryClient.invalidateQueries({
|
|
147
|
+
queryKey: ["fluxbase", tenantId ?? null, "table", table],
|
|
148
|
+
});
|
|
143
149
|
},
|
|
144
150
|
});
|
|
145
151
|
}
|
|
@@ -167,7 +173,10 @@ export function useUpdate<T = any>(table: string) {
|
|
|
167
173
|
return result;
|
|
168
174
|
},
|
|
169
175
|
onSuccess: () => {
|
|
170
|
-
|
|
176
|
+
const tenantId = client.getTenantId();
|
|
177
|
+
queryClient.invalidateQueries({
|
|
178
|
+
queryKey: ["fluxbase", tenantId ?? null, "table", table],
|
|
179
|
+
});
|
|
171
180
|
},
|
|
172
181
|
});
|
|
173
182
|
}
|
|
@@ -191,7 +200,10 @@ export function useUpsert<T = any>(table: string) {
|
|
|
191
200
|
return result;
|
|
192
201
|
},
|
|
193
202
|
onSuccess: () => {
|
|
194
|
-
|
|
203
|
+
const tenantId = client.getTenantId();
|
|
204
|
+
queryClient.invalidateQueries({
|
|
205
|
+
queryKey: ["fluxbase", tenantId ?? null, "table", table],
|
|
206
|
+
});
|
|
195
207
|
},
|
|
196
208
|
});
|
|
197
209
|
}
|
|
@@ -216,7 +228,10 @@ export function useDelete<T = any>(table: string) {
|
|
|
216
228
|
}
|
|
217
229
|
},
|
|
218
230
|
onSuccess: () => {
|
|
219
|
-
|
|
231
|
+
const tenantId = client.getTenantId();
|
|
232
|
+
queryClient.invalidateQueries({
|
|
233
|
+
queryKey: ["fluxbase", tenantId ?? null, "table", table],
|
|
234
|
+
});
|
|
220
235
|
},
|
|
221
236
|
});
|
|
222
237
|
}
|
package/src/use-tenant.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* @module use-tenant
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { useState,
|
|
7
|
+
import { useState, useCallback } from "react";
|
|
8
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
8
9
|
import { useFluxbaseClient } from "./context";
|
|
9
10
|
import type {
|
|
10
11
|
Tenant,
|
|
@@ -98,58 +99,55 @@ export interface UseTenantsReturn {
|
|
|
98
99
|
export function useTenants(options: UseTenantsOptions = {}): UseTenantsReturn {
|
|
99
100
|
const { autoFetch = true } = options;
|
|
100
101
|
const client = useFluxbaseClient();
|
|
102
|
+
const queryClient = useQueryClient();
|
|
101
103
|
|
|
102
|
-
const [tenants, setTenants] = useState<TenantWithRole[]>([]);
|
|
103
|
-
const [isLoading, setIsLoading] = useState(autoFetch);
|
|
104
|
-
const [error, setError] = useState<Error | null>(null);
|
|
105
104
|
const [currentTenantId, setCurrentTenantId] = useState<string | undefined>(
|
|
106
105
|
client.getTenantId(),
|
|
107
106
|
);
|
|
108
107
|
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
} catch (err) {
|
|
120
|
-
setError(err as Error);
|
|
121
|
-
} finally {
|
|
122
|
-
setIsLoading(false);
|
|
123
|
-
}
|
|
124
|
-
}, [client]);
|
|
108
|
+
const query = useQuery({
|
|
109
|
+
queryKey: ["fluxbase", "tenants", "mine"],
|
|
110
|
+
queryFn: async () => {
|
|
111
|
+
const { data, error } = await client.tenant.listMine();
|
|
112
|
+
if (error) throw error;
|
|
113
|
+
return data || ([] as TenantWithRole[]);
|
|
114
|
+
},
|
|
115
|
+
enabled: autoFetch,
|
|
116
|
+
});
|
|
125
117
|
|
|
126
118
|
const createTenant = useCallback(
|
|
127
119
|
async (opts: CreateTenantOptions): Promise<Tenant> => {
|
|
128
120
|
const { data, error: createError } = await client.tenant.create(opts);
|
|
129
121
|
if (createError) throw createError;
|
|
130
|
-
await
|
|
122
|
+
await queryClient.invalidateQueries({
|
|
123
|
+
queryKey: ["fluxbase", "tenants"],
|
|
124
|
+
});
|
|
131
125
|
return data!;
|
|
132
126
|
},
|
|
133
|
-
[client,
|
|
127
|
+
[client, queryClient],
|
|
134
128
|
);
|
|
135
129
|
|
|
136
130
|
const updateTenant = useCallback(
|
|
137
131
|
async (id: string, opts: UpdateTenantOptions): Promise<Tenant> => {
|
|
138
132
|
const { data, error: updateError } = await client.tenant.update(id, opts);
|
|
139
133
|
if (updateError) throw updateError;
|
|
140
|
-
await
|
|
134
|
+
await queryClient.invalidateQueries({
|
|
135
|
+
queryKey: ["fluxbase", "tenants"],
|
|
136
|
+
});
|
|
141
137
|
return data!;
|
|
142
138
|
},
|
|
143
|
-
[client,
|
|
139
|
+
[client, queryClient],
|
|
144
140
|
);
|
|
145
141
|
|
|
146
142
|
const deleteTenant = useCallback(
|
|
147
143
|
async (id: string): Promise<void> => {
|
|
148
144
|
const { error: deleteError } = await client.tenant.delete(id);
|
|
149
145
|
if (deleteError) throw deleteError;
|
|
150
|
-
await
|
|
146
|
+
await queryClient.invalidateQueries({
|
|
147
|
+
queryKey: ["fluxbase", "tenants"],
|
|
148
|
+
});
|
|
151
149
|
},
|
|
152
|
-
[client,
|
|
150
|
+
[client, queryClient],
|
|
153
151
|
);
|
|
154
152
|
|
|
155
153
|
const setCurrentTenant = useCallback(
|
|
@@ -160,17 +158,13 @@ export function useTenants(options: UseTenantsOptions = {}): UseTenantsReturn {
|
|
|
160
158
|
[client],
|
|
161
159
|
);
|
|
162
160
|
|
|
163
|
-
useEffect(() => {
|
|
164
|
-
if (autoFetch) {
|
|
165
|
-
fetchTenants();
|
|
166
|
-
}
|
|
167
|
-
}, [autoFetch, fetchTenants]);
|
|
168
|
-
|
|
169
161
|
return {
|
|
170
|
-
tenants,
|
|
171
|
-
isLoading,
|
|
172
|
-
error,
|
|
173
|
-
refetch:
|
|
162
|
+
tenants: query.data ?? [],
|
|
163
|
+
isLoading: autoFetch ? query.isLoading : false,
|
|
164
|
+
error: query.error,
|
|
165
|
+
refetch: async () => {
|
|
166
|
+
await query.refetch();
|
|
167
|
+
},
|
|
174
168
|
createTenant,
|
|
175
169
|
updateTenant,
|
|
176
170
|
deleteTenant,
|
|
@@ -249,31 +243,17 @@ export interface UseTenantReturn {
|
|
|
249
243
|
export function useTenant(options: UseTenantOptions): UseTenantReturn {
|
|
250
244
|
const { tenantId, autoFetch = true } = options;
|
|
251
245
|
const client = useFluxbaseClient();
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const { data, error: fetchError } = await client.tenant.get(tenantId);
|
|
264
|
-
if (fetchError) {
|
|
265
|
-
setError(fetchError);
|
|
266
|
-
setTenant(null);
|
|
267
|
-
} else {
|
|
268
|
-
setTenant(data);
|
|
269
|
-
}
|
|
270
|
-
} catch (err) {
|
|
271
|
-
setError(err as Error);
|
|
272
|
-
setTenant(null);
|
|
273
|
-
} finally {
|
|
274
|
-
setIsLoading(false);
|
|
275
|
-
}
|
|
276
|
-
}, [client, tenantId]);
|
|
246
|
+
const queryClient = useQueryClient();
|
|
247
|
+
|
|
248
|
+
const query = useQuery({
|
|
249
|
+
queryKey: ["fluxbase", "tenant", tenantId],
|
|
250
|
+
queryFn: async () => {
|
|
251
|
+
const { data, error } = await client.tenant.get(tenantId);
|
|
252
|
+
if (error) throw error;
|
|
253
|
+
return data;
|
|
254
|
+
},
|
|
255
|
+
enabled: autoFetch && !!tenantId,
|
|
256
|
+
});
|
|
277
257
|
|
|
278
258
|
const update = useCallback(
|
|
279
259
|
async (opts: UpdateTenantOptions): Promise<Tenant> => {
|
|
@@ -282,29 +262,29 @@ export function useTenant(options: UseTenantOptions): UseTenantReturn {
|
|
|
282
262
|
opts,
|
|
283
263
|
);
|
|
284
264
|
if (updateError) throw updateError;
|
|
285
|
-
|
|
265
|
+
await queryClient.invalidateQueries({
|
|
266
|
+
queryKey: ["fluxbase", "tenant", tenantId],
|
|
267
|
+
});
|
|
286
268
|
return data!;
|
|
287
269
|
},
|
|
288
|
-
[client, tenantId],
|
|
270
|
+
[client, tenantId, queryClient],
|
|
289
271
|
);
|
|
290
272
|
|
|
291
273
|
const remove = useCallback(async (): Promise<void> => {
|
|
292
274
|
const { error: deleteError } = await client.tenant.delete(tenantId);
|
|
293
275
|
if (deleteError) throw deleteError;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (autoFetch && tenantId) {
|
|
299
|
-
fetchTenant();
|
|
300
|
-
}
|
|
301
|
-
}, [autoFetch, fetchTenant, tenantId]);
|
|
276
|
+
await queryClient.invalidateQueries({
|
|
277
|
+
queryKey: ["fluxbase", "tenant", tenantId],
|
|
278
|
+
});
|
|
279
|
+
}, [client, tenantId, queryClient]);
|
|
302
280
|
|
|
303
281
|
return {
|
|
304
|
-
tenant,
|
|
305
|
-
isLoading,
|
|
306
|
-
error,
|
|
307
|
-
refetch:
|
|
282
|
+
tenant: query.data ?? null,
|
|
283
|
+
isLoading: autoFetch ? query.isLoading : false,
|
|
284
|
+
error: query.error,
|
|
285
|
+
refetch: async () => {
|
|
286
|
+
await query.refetch();
|
|
287
|
+
},
|
|
308
288
|
update,
|
|
309
289
|
remove,
|
|
310
290
|
};
|