@nimbleflux/fluxbase-sdk-react 2026.5.5-rc.2 → 2026.6.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/README.md CHANGED
@@ -135,18 +135,18 @@ function YourApp() {
135
135
 
136
136
  ## Documentation
137
137
 
138
- 📚 **[Complete React Hooks Guide](../../docs/docs/sdks/react-hooks.md)**
138
+ 📚 **[React SDK Reference](../../docs/src/content/docs/api/sdk-react/)**
139
139
 
140
140
  ### Core Guides
141
141
 
142
- - **[Getting Started](../../docs/docs/sdks/getting-started.md)** - Installation and setup
143
- - **[React Hooks](../../docs/docs/sdks/react-hooks.md)** - Comprehensive hooks documentation with examples
144
- - **[Database Operations](../../docs/docs/sdks/database.md)** - Query building and data operations
142
+ - **[SDK Reference](../../docs/src/content/docs/sdk/)** - Core SDK usage and configuration
143
+ - **[Admin Hooks Guide](./README-ADMIN.md)** - Comprehensive admin dashboard documentation
144
+ - **[Vector Search](../../docs/src/content/docs/guides/vector-search.md)** - Semantic search
145
145
 
146
146
  ### API Reference
147
147
 
148
- - **[React Hooks API](../../docs/static/api/sdk-react/)** - Auto-generated from source code
149
- - **[Core SDK API](../../docs/static/api/sdk/)** - Core TypeScript SDK reference
148
+ - **[React Hooks API](../../docs/src/content/docs/api/sdk-react/)** - Auto-generated from source code
149
+ - **[Core SDK API](../../docs/src/content/docs/api/sdk/)** - Core TypeScript SDK reference
150
150
 
151
151
  ## TypeScript Support
152
152
 
@@ -169,18 +169,11 @@ function ProductList() {
169
169
 
170
170
  ## Examples
171
171
 
172
- Check out working examples in the [`/example`](../example/) directory:
173
-
174
- - React with Vite
175
- - Next.js App Router
176
- - Next.js Pages Router
177
- - Authentication flows
178
- - Realtime features
179
- - File uploads
172
+ See `sdk-react/examples/` for a working example app.
180
173
 
181
174
  ## Contributing
182
175
 
183
- Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
176
+ Contributions are welcome! Please see the project repository for contribution guidelines.
184
177
 
185
178
  ## License
186
179
 
@@ -188,8 +181,8 @@ MIT © Fluxbase
188
181
 
189
182
  ## Links
190
183
 
191
- - [Documentation](../../docs/docs/sdks/react-hooks.md)
192
- - [API Reference](../../docs/static/api/sdk-react/)
184
+ - [Documentation](../../docs/src/content/docs/sdk/)
185
+ - [API Reference](../../docs/src/content/docs/api/sdk-react/)
193
186
  - [Core SDK](../sdk/)
194
187
  - [GitHub](https://github.com/nimbleflux/fluxbase)
195
188
  - [Issues](https://github.com/nimbleflux/fluxbase/issues)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nimbleflux/fluxbase-sdk-react",
3
- "version": "2026.5.5-rc.2",
3
+ "version": "2026.6.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.5-rc.2",
42
+ "@nimbleflux/fluxbase-sdk": "^2026.6.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
@@ -144,6 +144,49 @@ export { useSubmitJob, useJobStatus } from "./use-jobs";
144
144
  // Branching hooks
145
145
  export { useBranches } from "./use-branches";
146
146
 
147
+ // RPC hooks
148
+ export { useRPCList, useInvokeRPC } from "./use-rpc";
149
+
150
+ // Vector hooks
151
+ export { useVectorEmbed, useVectorSearch } from "./use-vector";
152
+
153
+ // Secrets hooks
154
+ export { useSecrets, useCreateSecret, useUpdateSecret, useDeleteSecret } from "./use-secrets";
155
+
156
+ // Service Keys hooks
157
+ export {
158
+ useServiceKeys,
159
+ useCreateServiceKey,
160
+ useRotateServiceKey,
161
+ useRevokeServiceKey,
162
+ } from "./use-service-keys";
163
+
164
+ // AI hooks
165
+ export {
166
+ useChatbots,
167
+ useConversations,
168
+ useConversation,
169
+ useDeleteConversation,
170
+ useAIChat,
171
+ type ChatMessage,
172
+ } from "./use-ai";
173
+
174
+ // Knowledge Base hooks
175
+ export {
176
+ useKnowledgeBases,
177
+ useKnowledgeBase,
178
+ useCreateKnowledgeBase,
179
+ useUpdateKnowledgeBase,
180
+ useDeleteKnowledgeBase,
181
+ useKBDocuments,
182
+ useAddDocument,
183
+ useUploadDocument,
184
+ useDeleteDocument,
185
+ useKBSearch,
186
+ useKBEntities,
187
+ useKnowledgeGraph,
188
+ } from "./use-knowledge-base";
189
+
147
190
  // Re-export types from SDK
148
191
  export type {
149
192
  FluxbaseClient,
@@ -91,6 +91,33 @@ export function createMockClient(
91
91
  }),
92
92
  ...overrides.realtime,
93
93
  },
94
+ jobs: {
95
+ submit: vi.fn().mockResolvedValue({ data: { id: "job-1", status: "pending" }, error: null }),
96
+ get: vi.fn().mockResolvedValue({ data: { id: "job-1", status: "completed" }, error: null }),
97
+ list: vi.fn().mockResolvedValue({ data: [], error: null }),
98
+ cancel: vi.fn().mockResolvedValue({ data: null, error: null }),
99
+ retry: vi.fn().mockResolvedValue({ data: { id: "job-2", status: "pending" }, error: null }),
100
+ getLogs: vi.fn().mockResolvedValue({ data: [], error: null }),
101
+ ...overrides.jobs,
102
+ },
103
+ functions: {
104
+ invoke: vi.fn().mockResolvedValue({ data: null, error: null }),
105
+ list: vi.fn().mockResolvedValue({ data: [], error: null }),
106
+ get: vi.fn().mockResolvedValue({ data: null, error: null }),
107
+ ...overrides.functions,
108
+ },
109
+ branching: {
110
+ list: vi.fn().mockResolvedValue({ data: { branches: [], total: 0, limit: 50, offset: 0 }, error: null }),
111
+ get: vi.fn().mockResolvedValue({ data: null, error: null }),
112
+ create: vi.fn().mockResolvedValue({ data: { id: "b1", slug: "test-branch", status: "creating" }, error: null }),
113
+ delete: vi.fn().mockResolvedValue({ error: null }),
114
+ reset: vi.fn().mockResolvedValue({ data: { id: "b1", slug: "test-branch", status: "creating" }, error: null }),
115
+ getActivity: vi.fn().mockResolvedValue({ data: [], error: null }),
116
+ getPoolStats: vi.fn().mockResolvedValue({ data: [], error: null }),
117
+ exists: vi.fn().mockResolvedValue(false),
118
+ waitForReady: vi.fn().mockResolvedValue({ data: null, error: null }),
119
+ ...overrides.branching,
120
+ },
94
121
  graphql: {
95
122
  execute: vi.fn().mockResolvedValue({ data: null, errors: null }),
96
123
  query: vi.fn().mockResolvedValue({ data: null, errors: null }),
@@ -142,12 +169,159 @@ export function createMockClient(
142
169
  test: vi.fn().mockResolvedValue({}),
143
170
  },
144
171
  },
172
+ serviceKeys: {
173
+ list: vi.fn().mockResolvedValue({ data: [], error: null }),
174
+ get: vi.fn().mockResolvedValue({ data: null, error: null }),
175
+ create: vi.fn().mockResolvedValue({ data: null, error: null }),
176
+ update: vi.fn().mockResolvedValue({ data: null, error: null }),
177
+ delete: vi.fn().mockResolvedValue({ error: null }),
178
+ disable: vi.fn().mockResolvedValue({ error: null }),
179
+ enable: vi.fn().mockResolvedValue({ error: null }),
180
+ revoke: vi.fn().mockResolvedValue({ error: null }),
181
+ deprecate: vi.fn().mockResolvedValue({ data: null, error: null }),
182
+ rotate: vi.fn().mockResolvedValue({ data: null, error: null }),
183
+ getRevocationHistory: vi.fn().mockResolvedValue({ data: null, error: null }),
184
+ },
185
+ migrations: {
186
+ list: vi.fn().mockResolvedValue({ data: [], error: null }),
187
+ apply: vi
188
+ .fn()
189
+ .mockResolvedValue({ data: { message: "Migration applied" }, error: null }),
190
+ rollback: vi
191
+ .fn()
192
+ .mockResolvedValue({ data: { message: "Migration rolled back" }, error: null }),
193
+ sync: vi.fn().mockResolvedValue({ data: null, error: null }),
194
+ },
195
+ impersonation: {
196
+ impersonateUser: vi
197
+ .fn()
198
+ .mockResolvedValue({
199
+ session: null,
200
+ target_user: null,
201
+ access_token: "",
202
+ refresh_token: "",
203
+ expires_in: 0,
204
+ }),
205
+ impersonateAnon: vi
206
+ .fn()
207
+ .mockResolvedValue({
208
+ session: null,
209
+ target_user: null,
210
+ access_token: "",
211
+ refresh_token: "",
212
+ expires_in: 0,
213
+ }),
214
+ stop: vi
215
+ .fn()
216
+ .mockResolvedValue({ success: true, message: "Impersonation stopped" }),
217
+ getCurrent: vi.fn().mockResolvedValue({ session: null, target_user: null }),
218
+ listSessions: vi.fn().mockResolvedValue({ sessions: [], total: 0 }),
219
+ },
220
+ ddl: {
221
+ listSchemas: vi.fn().mockResolvedValue({ schemas: [] }),
222
+ createSchema: vi
223
+ .fn()
224
+ .mockResolvedValue({ message: "Schema created", schema: "" }),
225
+ listTables: vi.fn().mockResolvedValue({ tables: [] }),
226
+ deleteTable: vi
227
+ .fn()
228
+ .mockResolvedValue({ message: "Table deleted" }),
229
+ },
145
230
  ...overrides.admin,
146
231
  },
232
+ rpc: Object.assign(
233
+ vi.fn().mockResolvedValue({ data: null, error: null }),
234
+ {
235
+ list: vi.fn().mockResolvedValue({ data: [], error: null }),
236
+ invoke: vi.fn().mockResolvedValue({ data: null, error: null }),
237
+ getStatus: vi.fn().mockResolvedValue({ data: null, error: null }),
238
+ getLogs: vi.fn().mockResolvedValue({ data: [], error: null }),
239
+ waitForCompletion: vi.fn().mockResolvedValue({ data: null, error: null }),
240
+ },
241
+ ),
242
+ secrets: {
243
+ list: vi.fn().mockResolvedValue([]),
244
+ create: vi.fn().mockResolvedValue({ id: "1", name: "test", scope: "global", version: 1, created_at: "", updated_at: "" }),
245
+ get: vi.fn().mockResolvedValue({ id: "1", name: "test", scope: "global", version: 1, created_at: "", updated_at: "" }),
246
+ update: vi.fn().mockResolvedValue({ id: "1", name: "test", scope: "global", version: 2, created_at: "", updated_at: "" }),
247
+ delete: vi.fn().mockResolvedValue(undefined),
248
+ getVersions: vi.fn().mockResolvedValue([]),
249
+ rollback: vi.fn().mockResolvedValue({ id: "1", name: "test", scope: "global", version: 3, created_at: "", updated_at: "" }),
250
+ stats: vi.fn().mockResolvedValue({ total: 0, expiring_soon: 0, expired: 0 }),
251
+ getById: vi.fn().mockResolvedValue({ id: "1", name: "test", scope: "global", version: 1, created_at: "", updated_at: "" }),
252
+ updateById: vi.fn().mockResolvedValue({ id: "1", name: "test", scope: "global", version: 2, created_at: "", updated_at: "" }),
253
+ deleteById: vi.fn().mockResolvedValue(undefined),
254
+ getVersionsById: vi.fn().mockResolvedValue([]),
255
+ rollbackById: vi.fn().mockResolvedValue({ id: "1", name: "test", scope: "global", version: 3, created_at: "", updated_at: "" }),
256
+ ...overrides.secrets,
257
+ },
258
+ vector: {
259
+ embed: vi.fn().mockResolvedValue({ data: null, error: null }),
260
+ search: vi.fn().mockResolvedValue({ data: null, error: null }),
261
+ ...overrides.vector,
262
+ },
147
263
  ...overrides,
148
264
  } as unknown as FluxbaseClient;
149
265
  }
150
266
 
267
+ /**
268
+ * Create a mock client with AI/Knowledge Base support
269
+ */
270
+ export function createMockAIClient(
271
+ overrides: Partial<FluxbaseClient> = {},
272
+ ): FluxbaseClient {
273
+ const base = createMockClient(overrides);
274
+ return {
275
+ ...base,
276
+ ai: {
277
+ listChatbots: vi.fn().mockResolvedValue({ data: [], error: null }),
278
+ getChatbot: vi.fn().mockResolvedValue({ data: null, error: null }),
279
+ lookupChatbot: vi.fn().mockResolvedValue({ data: null, error: null }),
280
+ createChat: vi.fn().mockReturnValue({
281
+ connect: vi.fn().mockResolvedValue(undefined),
282
+ disconnect: vi.fn(),
283
+ isConnected: vi.fn().mockReturnValue(true),
284
+ startChat: vi.fn().mockResolvedValue("conv-1"),
285
+ sendMessage: vi.fn().mockResolvedValue(undefined),
286
+ cancel: vi.fn(),
287
+ getAccumulatedContent: vi.fn().mockReturnValue(""),
288
+ }),
289
+ listConversations: vi
290
+ .fn()
291
+ .mockResolvedValue({ data: { conversations: [], total: 0, has_more: false }, error: null }),
292
+ getConversation: vi.fn().mockResolvedValue({ data: null, error: null }),
293
+ deleteConversation: vi.fn().mockResolvedValue({ error: null }),
294
+ updateConversation: vi.fn().mockResolvedValue({ data: null, error: null }),
295
+ ...overrides.ai,
296
+ },
297
+ knowledgeBase: {
298
+ list: vi.fn().mockResolvedValue({ data: [], error: null }),
299
+ get: vi.fn().mockResolvedValue({ data: null, error: null }),
300
+ create: vi.fn().mockResolvedValue({ data: null, error: null }),
301
+ update: vi.fn().mockResolvedValue({ data: null, error: null }),
302
+ delete: vi.fn().mockResolvedValue({ data: true, error: null }),
303
+ listDocuments: vi.fn().mockResolvedValue({ data: [], error: null }),
304
+ getDocument: vi.fn().mockResolvedValue({ data: null, error: null }),
305
+ addDocument: vi.fn().mockResolvedValue({ data: null, error: null }),
306
+ uploadDocument: vi.fn().mockResolvedValue({ data: null, error: null }),
307
+ updateDocument: vi.fn().mockResolvedValue({ data: null, error: null }),
308
+ deleteDocument: vi.fn().mockResolvedValue({ data: true, error: null }),
309
+ deleteDocumentsByFilter: vi.fn().mockResolvedValue({ data: null, error: null }),
310
+ search: vi.fn().mockResolvedValue({ data: null, error: null }),
311
+ listEntities: vi.fn().mockResolvedValue({ data: [], error: null }),
312
+ searchEntities: vi.fn().mockResolvedValue({ data: [], error: null }),
313
+ getEntityRelationships: vi.fn().mockResolvedValue({ data: [], error: null }),
314
+ getKnowledgeGraph: vi.fn().mockResolvedValue({ data: null, error: null }),
315
+ ...overrides.knowledgeBase,
316
+ },
317
+ vector: {
318
+ embed: vi.fn().mockResolvedValue({ data: null, error: null }),
319
+ search: vi.fn().mockResolvedValue({ data: null, error: null }),
320
+ ...overrides.vector,
321
+ },
322
+ } as unknown as FluxbaseClient;
323
+ }
324
+
151
325
  /**
152
326
  * Create a fresh QueryClient for testing
153
327
  */
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Tests for AI hooks
3
+ */
4
+
5
+ import { describe, it, expect, vi, beforeEach } from "vitest";
6
+ import { renderHook, waitFor, act } from "@testing-library/react";
7
+ import {
8
+ useChatbots,
9
+ useConversations,
10
+ useConversation,
11
+ useDeleteConversation,
12
+ } from "./use-ai";
13
+ import {
14
+ createMockAIClient,
15
+ createWrapper,
16
+ } from "./test-utils";
17
+ import type { FluxbaseClient } from "@nimbleflux/fluxbase-sdk";
18
+
19
+ describe("useChatbots", () => {
20
+ it("should list chatbots", async () => {
21
+ const mockChatbots = [
22
+ { id: "1", name: "Bot1", namespace: "default", enabled: true, version: "1", source: "filesystem" },
23
+ ];
24
+ const client = createMockAIClient({
25
+ ai: {
26
+ listChatbots: vi.fn().mockResolvedValue({ data: mockChatbots, error: null }),
27
+ } as any,
28
+ });
29
+
30
+ const { result } = renderHook(() => useChatbots(), {
31
+ wrapper: createWrapper(client),
32
+ });
33
+
34
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
35
+ expect(result.current.data).toEqual(mockChatbots);
36
+ });
37
+
38
+ it("should filter by namespace", async () => {
39
+ const listChatbots = vi
40
+ .fn()
41
+ .mockResolvedValue({ data: [], error: null });
42
+ const client = createMockAIClient({
43
+ ai: { listChatbots } as any,
44
+ });
45
+
46
+ renderHook(() => useChatbots("my-ns"), {
47
+ wrapper: createWrapper(client),
48
+ });
49
+
50
+ await waitFor(() => {
51
+ expect(listChatbots).toHaveBeenCalledWith("my-ns");
52
+ });
53
+ });
54
+
55
+ it("should handle errors", async () => {
56
+ const client = createMockAIClient({
57
+ ai: {
58
+ listChatbots: vi
59
+ .fn()
60
+ .mockResolvedValue({ data: null, error: new Error("Failed") }),
61
+ } as any,
62
+ });
63
+
64
+ const { result } = renderHook(() => useChatbots(), {
65
+ wrapper: createWrapper(client),
66
+ });
67
+
68
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
69
+ expect(result.current.error).toBeInstanceOf(Error);
70
+ });
71
+ });
72
+
73
+ describe("useConversations", () => {
74
+ it("should list conversations", async () => {
75
+ const mockData = {
76
+ conversations: [
77
+ { id: "c1", chatbot: "bot1", namespace: "default", title: "Test", created_at: "", updated_at: "" },
78
+ ],
79
+ total: 1,
80
+ has_more: false,
81
+ };
82
+ const client = createMockAIClient({
83
+ ai: {
84
+ listConversations: vi
85
+ .fn()
86
+ .mockResolvedValue({ data: mockData, error: null }),
87
+ } as any,
88
+ });
89
+
90
+ const { result } = renderHook(() => useConversations(), {
91
+ wrapper: createWrapper(client),
92
+ });
93
+
94
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
95
+ expect(result.current.data).toEqual(mockData);
96
+ });
97
+
98
+ it("should pass options to listConversations", async () => {
99
+ const listConversations = vi
100
+ .fn()
101
+ .mockResolvedValue({ data: { conversations: [], total: 0, has_more: false }, error: null });
102
+ const client = createMockAIClient({
103
+ ai: { listConversations } as any,
104
+ });
105
+
106
+ renderHook(
107
+ () => useConversations({ chatbot: "my-bot", limit: 10 }),
108
+ { wrapper: createWrapper(client) },
109
+ );
110
+
111
+ await waitFor(() => {
112
+ expect(listConversations).toHaveBeenCalledWith({
113
+ chatbot: "my-bot",
114
+ limit: 10,
115
+ });
116
+ });
117
+ });
118
+ });
119
+
120
+ describe("useConversation", () => {
121
+ it("should get a conversation by ID", async () => {
122
+ const mockConv = { id: "c1", chatbot: "bot1", namespace: "default", created_at: "", updated_at: "", messages: [] };
123
+ const client = createMockAIClient({
124
+ ai: {
125
+ getConversation: vi
126
+ .fn()
127
+ .mockResolvedValue({ data: mockConv, error: null }),
128
+ } as any,
129
+ });
130
+
131
+ const { result } = renderHook(() => useConversation("c1"), {
132
+ wrapper: createWrapper(client),
133
+ });
134
+
135
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
136
+ expect(result.current.data).toEqual(mockConv);
137
+ });
138
+
139
+ it("should not fetch when conversationId is null", () => {
140
+ const client = createMockAIClient();
141
+ const { result } = renderHook(() => useConversation(null), {
142
+ wrapper: createWrapper(client),
143
+ });
144
+ expect(result.current.isLoading).toBe(false);
145
+ expect(result.current.data).toBeUndefined();
146
+ });
147
+ });
148
+
149
+ describe("useDeleteConversation", () => {
150
+ it("should delete a conversation and invalidate queries", async () => {
151
+ const deleteConversation = vi
152
+ .fn()
153
+ .mockResolvedValue({ error: null });
154
+ const client = createMockAIClient({
155
+ ai: { deleteConversation } as any,
156
+ });
157
+
158
+ const { result } = renderHook(() => useDeleteConversation(), {
159
+ wrapper: createWrapper(client),
160
+ });
161
+
162
+ await act(async () => {
163
+ await result.current.mutateAsync("conv-1");
164
+ });
165
+
166
+ expect(deleteConversation).toHaveBeenCalledWith("conv-1");
167
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
168
+ });
169
+
170
+ it("should handle errors", async () => {
171
+ const client = createMockAIClient({
172
+ ai: {
173
+ deleteConversation: vi
174
+ .fn()
175
+ .mockResolvedValue({ error: new Error("Not found") }),
176
+ } as any,
177
+ });
178
+
179
+ const { result } = renderHook(() => useDeleteConversation(), {
180
+ wrapper: createWrapper(client),
181
+ });
182
+
183
+ await act(async () => {
184
+ try {
185
+ await result.current.mutateAsync("conv-1");
186
+ } catch {
187
+ // expected
188
+ }
189
+ });
190
+
191
+ await waitFor(() => expect(result.current.isError).toBe(true));
192
+ });
193
+ });