@lobehub/chat 1.46.6 → 1.47.0

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/changelog/v1.json +18 -0
  3. package/netlify.toml +1 -1
  4. package/package.json +2 -1
  5. package/src/app/(main)/discover/(detail)/provider/[slug]/features/ProviderConfig.tsx +2 -2
  6. package/src/app/(main)/settings/hooks/useCategory.tsx +3 -3
  7. package/src/app/(main)/settings/provider/(detail)/[id]/ClientMode.tsx +25 -0
  8. package/src/app/(main)/settings/provider/(detail)/[id]/page.tsx +2 -1
  9. package/src/app/(main)/settings/provider/ProviderMenu/SortProviderModal/index.tsx +0 -1
  10. package/src/const/settings/knowledge.ts +1 -1
  11. package/src/database/client/migrations.json +11 -0
  12. package/src/database/repositories/tableViewer/index.test.ts +256 -0
  13. package/src/database/repositories/tableViewer/index.ts +251 -0
  14. package/src/database/server/models/aiProvider.ts +2 -2
  15. package/src/features/DevPanel/FloatPanel.tsx +136 -0
  16. package/src/features/DevPanel/PostgresViewer/DataTable/Table.tsx +157 -0
  17. package/src/features/DevPanel/PostgresViewer/DataTable/TableCell.tsx +34 -0
  18. package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +67 -0
  19. package/src/features/DevPanel/PostgresViewer/Schema.tsx +196 -0
  20. package/src/features/DevPanel/PostgresViewer/TableColumns.tsx +67 -0
  21. package/src/features/DevPanel/PostgresViewer/index.tsx +19 -0
  22. package/src/features/DevPanel/PostgresViewer/useTableColumns.ts +13 -0
  23. package/src/features/DevPanel/index.tsx +12 -0
  24. package/src/features/ModelSwitchPanel/index.tsx +4 -2
  25. package/src/hooks/useEnabledChatModels.ts +2 -2
  26. package/src/hooks/useModelContextWindowTokens.ts +2 -2
  27. package/src/hooks/useModelHasContextWindowToken.ts +2 -2
  28. package/src/hooks/useModelSupportToolUse.ts +2 -2
  29. package/src/hooks/useModelSupportVision.ts +2 -2
  30. package/src/layout/GlobalProvider/index.tsx +2 -2
  31. package/src/server/globalConfig/parseFilesConfig.test.ts +122 -3
  32. package/src/server/globalConfig/parseFilesConfig.ts +19 -13
  33. package/src/server/modules/S3/index.ts +3 -0
  34. package/src/services/_auth.ts +2 -2
  35. package/src/services/aiModel/client.ts +60 -0
  36. package/src/services/aiModel/index.test.ts +10 -0
  37. package/src/services/aiModel/index.ts +5 -0
  38. package/src/services/aiModel/server.ts +47 -0
  39. package/src/services/aiModel/type.ts +30 -0
  40. package/src/services/aiProvider/client.ts +64 -0
  41. package/src/services/aiProvider/index.test.ts +10 -0
  42. package/src/services/aiProvider/index.ts +5 -0
  43. package/src/services/aiProvider/server.ts +43 -0
  44. package/src/services/aiProvider/type.ts +26 -0
  45. package/src/services/chat.ts +5 -5
  46. package/src/services/tableViewer/client.ts +16 -0
  47. package/src/services/tableViewer/index.ts +3 -0
  48. package/src/store/aiInfra/slices/aiProvider/action.ts +2 -2
  49. package/src/types/knowledgeBase/index.ts +1 -1
  50. package/src/types/serverConfig.ts +6 -0
  51. package/src/types/tableViewer.ts +30 -0
  52. package/src/types/user/settings/filesConfig.ts +1 -1
  53. package/tests/utils.tsx +46 -0
  54. package/src/features/DebugUI/Content.tsx +0 -34
  55. package/src/features/DebugUI/index.tsx +0 -20
  56. package/src/services/aiModel.ts +0 -52
  57. package/src/services/aiProvider.ts +0 -47
package/CHANGELOG.md CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 1.47.0](https://github.com/lobehub/lobe-chat/compare/v1.46.7...v1.47.0)
6
+
7
+ <sup>Released on **2025-01-17**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Support new ai provider in client pglite.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Support new ai provider in client pglite, closes [#5488](https://github.com/lobehub/lobe-chat/issues/5488) ([08f505f](https://github.com/lobehub/lobe-chat/commit/08f505f))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.46.7](https://github.com/lobehub/lobe-chat/compare/v1.46.6...v1.46.7)
31
+
32
+ <sup>Released on **2025-01-17**</sup>
33
+
34
+ #### 🐛 Bug Fixes
35
+
36
+ - **misc**: Improve validation for provider and model in parseFilesConfig, temporarily disable S3 client integrity check for Cloudflare R2.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's fixed
44
+
45
+ - **misc**: Improve validation for provider and model in parseFilesConfig, closes [#5454](https://github.com/lobehub/lobe-chat/issues/5454) ([b4808f8](https://github.com/lobehub/lobe-chat/commit/b4808f8))
46
+ - **misc**: Temporarily disable S3 client integrity check for Cloudflare R2, closes [#5479](https://github.com/lobehub/lobe-chat/issues/5479) ([a638238](https://github.com/lobehub/lobe-chat/commit/a638238))
47
+
48
+ </details>
49
+
50
+ <div align="right">
51
+
52
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
53
+
54
+ </div>
55
+
5
56
  ### [Version 1.46.6](https://github.com/lobehub/lobe-chat/compare/v1.46.5...v1.46.6)
6
57
 
7
58
  <sup>Released on **2025-01-16**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Support new ai provider in client pglite."
6
+ ]
7
+ },
8
+ "date": "2025-01-17",
9
+ "version": "1.47.0"
10
+ },
11
+ {
12
+ "children": {
13
+ "fixes": [
14
+ "Improve validation for provider and model in parseFilesConfig, temporarily disable S3 client integrity check for Cloudflare R2."
15
+ ]
16
+ },
17
+ "date": "2025-01-17",
18
+ "version": "1.46.7"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "fixes": [
package/netlify.toml CHANGED
@@ -1,5 +1,5 @@
1
1
  [build]
2
- command = "pnpm run build"
2
+ command = "rm -rf .next node_modules/.cache && pnpm run build"
3
3
  publish = ".next"
4
4
 
5
5
  [build.environment]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.46.6",
3
+ "version": "1.47.0",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -206,6 +206,7 @@
206
206
  "react-layout-kit": "^1.9.1",
207
207
  "react-lazy-load": "^4.0.1",
208
208
  "react-pdf": "^9.2.1",
209
+ "react-rnd": "^10.4.14",
209
210
  "react-scan": "^0.0.54",
210
211
  "react-virtuoso": "^4.12.3",
211
212
  "react-wrap-balancer": "^1.1.1",
@@ -10,7 +10,7 @@ import { memo } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import { FlexboxProps } from 'react-layout-kit';
12
12
 
13
- import { isServerMode } from '@/const/version';
13
+ import { isDeprecatedEdition } from '@/const/version';
14
14
  import { DiscoverProviderItem } from '@/types/discover';
15
15
 
16
16
  const useStyles = createStyles(({ css }) => ({
@@ -32,7 +32,7 @@ const ProviderConfig = memo<ProviderConfigProps>(({ data, identifier }) => {
32
32
 
33
33
  const router = useRouter();
34
34
  const openSettings = () => {
35
- router.push(!isServerMode ? '/settings/llm' : `/settings/provider/${identifier}`);
35
+ router.push(isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${identifier}`);
36
36
  };
37
37
 
38
38
  const icon = <Icon icon={SquareArrowOutUpRight} size={{ fontSize: 16 }} />;
@@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import type { MenuProps } from '@/components/Menu';
10
- import { isServerMode } from '@/const/version';
10
+ import { isDeprecatedEdition } from '@/const/version';
11
11
  import { SettingsTabs } from '@/store/global/initialState';
12
12
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
13
13
 
@@ -53,7 +53,7 @@ export const useCategory = () => {
53
53
  },
54
54
  showLLM &&
55
55
  // TODO: Remove /llm when v2.0
56
- !isServerMode
56
+ (isDeprecatedEdition
57
57
  ? {
58
58
  icon: <Icon icon={Brain} />,
59
59
  key: SettingsTabs.LLM,
@@ -71,7 +71,7 @@ export const useCategory = () => {
71
71
  {t('tab.provider')}
72
72
  </Link>
73
73
  ),
74
- },
74
+ }),
75
75
 
76
76
  enableSTT && {
77
77
  icon: <Icon icon={Mic2} />,
@@ -0,0 +1,25 @@
1
+ 'use client';
2
+
3
+ import { memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+ import useSWR from 'swr';
6
+
7
+ import { aiProviderService } from '@/services/aiProvider';
8
+
9
+ import ModelList from '../../features/ModelList';
10
+ import ProviderConfig from '../../features/ProviderConfig';
11
+
12
+ const ClientMode = memo<{ id: string }>(({ id }) => {
13
+ const { data, isLoading } = useSWR('get-client-provider', () =>
14
+ aiProviderService.getAiProviderById(id),
15
+ );
16
+
17
+ return (
18
+ <Flexbox gap={24} paddingBlock={8}>
19
+ {!isLoading && data && <ProviderConfig {...data} />}
20
+ <ModelList id={id} />
21
+ </Flexbox>
22
+ );
23
+ });
24
+
25
+ export default ClientMode;
@@ -8,6 +8,7 @@ import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
8
8
  import { PagePropsWithId } from '@/types/next';
9
9
  import { getUserAuth } from '@/utils/server/auth';
10
10
 
11
+ import ClientMode from './ClientMode';
11
12
  import ProviderDetail from './index';
12
13
 
13
14
  const Page = async (props: PagePropsWithId) => {
@@ -33,7 +34,7 @@ const Page = async (props: PagePropsWithId) => {
33
34
  return <ProviderDetail {...userCard} />;
34
35
  }
35
36
 
36
- return <div>not found</div>;
37
+ return <ClientMode id={params.id} />;
37
38
  };
38
39
 
39
40
  export default Page;
@@ -72,7 +72,6 @@ const ConfigGroupModal = memo<ConfigGroupModalProps>(({ open, onCancel, defaultI
72
72
  id: item.id,
73
73
  sort: index,
74
74
  }));
75
- console.log(sortMap);
76
75
  setLoading(true);
77
76
  await updateAiProviderSort(sortMap);
78
77
  setLoading(false);
@@ -20,6 +20,6 @@ export const DEFAULT_FILE_RERANK_MODEL_ITEM: FilesConfigItem = {
20
20
 
21
21
  export const DEFAULT_FILES_CONFIG: FilesConfig = {
22
22
  embeddingModel: DEFAULT_FILE_EMBEDDING_MODEL_ITEM,
23
- queryModel: DEFAULT_RERANK_QUERY_MODE,
23
+ queryMode: DEFAULT_RERANK_QUERY_MODE,
24
24
  rerankerModel: DEFAULT_FILE_RERANK_MODEL_ITEM,
25
25
  };
@@ -282,5 +282,16 @@
282
282
  "bps": true,
283
283
  "folderMillis": 1731858381716,
284
284
  "hash": "d8263bfefe296ed366379c7b7fc65195d12e6a1c0a9f1c96097ea28f2123fe50"
285
+ },
286
+ {
287
+ "sql": [
288
+ "CREATE TABLE \"ai_models\" (\n\t\"id\" varchar(150) NOT NULL,\n\t\"display_name\" varchar(200),\n\t\"description\" text,\n\t\"organization\" varchar(100),\n\t\"enabled\" boolean,\n\t\"provider_id\" varchar(64) NOT NULL,\n\t\"type\" varchar(20) DEFAULT 'chat' NOT NULL,\n\t\"sort\" integer,\n\t\"user_id\" text NOT NULL,\n\t\"pricing\" jsonb,\n\t\"parameters\" jsonb DEFAULT '{}'::jsonb,\n\t\"config\" jsonb,\n\t\"abilities\" jsonb DEFAULT '{}'::jsonb,\n\t\"context_window_tokens\" integer,\n\t\"source\" varchar(20),\n\t\"released_at\" varchar(10),\n\t\"accessed_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"created_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"updated_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\tCONSTRAINT \"ai_models_id_provider_id_user_id_pk\" PRIMARY KEY(\"id\",\"provider_id\",\"user_id\")\n);\n",
289
+ "\nCREATE TABLE \"ai_providers\" (\n\t\"id\" varchar(64) NOT NULL,\n\t\"name\" text,\n\t\"user_id\" text NOT NULL,\n\t\"sort\" integer,\n\t\"enabled\" boolean,\n\t\"fetch_on_client\" boolean,\n\t\"check_model\" text,\n\t\"logo\" text,\n\t\"description\" text,\n\t\"key_vaults\" text,\n\t\"source\" varchar(20),\n\t\"settings\" jsonb,\n\t\"accessed_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"created_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\t\"updated_at\" timestamp with time zone DEFAULT now() NOT NULL,\n\tCONSTRAINT \"ai_providers_id_user_id_pk\" PRIMARY KEY(\"id\",\"user_id\")\n);\n",
290
+ "\nALTER TABLE \"ai_models\" ADD CONSTRAINT \"ai_models_user_id_users_id_fk\" FOREIGN KEY (\"user_id\") REFERENCES \"public\".\"users\"(\"id\") ON DELETE cascade ON UPDATE no action;",
291
+ "\nALTER TABLE \"ai_providers\" ADD CONSTRAINT \"ai_providers_user_id_users_id_fk\" FOREIGN KEY (\"user_id\") REFERENCES \"public\".\"users\"(\"id\") ON DELETE cascade ON UPDATE no action;"
292
+ ],
293
+ "bps": true,
294
+ "folderMillis": 1735834653361,
295
+ "hash": "845a692ceabbfc3caf252a97d3e19a213bc0c433df2689900135f9cfded2cf49"
285
296
  }
286
297
  ]
@@ -0,0 +1,256 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ import { clientDB, initializeDB } from '@/database/client/db';
4
+
5
+ import { TableViewerRepo } from './index';
6
+
7
+ const userId = 'user-table-viewer';
8
+ const repo = new TableViewerRepo(clientDB as any, userId);
9
+
10
+ // Mock database execution
11
+ const mockExecute = vi.fn();
12
+ const mockDB = {
13
+ execute: mockExecute,
14
+ };
15
+
16
+ beforeEach(async () => {
17
+ await initializeDB();
18
+ vi.clearAllMocks();
19
+ });
20
+
21
+ describe('TableViewerRepo', () => {
22
+ describe('getAllTables', () => {
23
+ it('should return all tables with counts', async () => {
24
+ const result = await repo.getAllTables();
25
+
26
+ expect(result.length).toEqual(39);
27
+ expect(result[0]).toEqual({ name: 'agents', count: 0, type: 'BASE TABLE' });
28
+ });
29
+
30
+ it('should handle custom schema', async () => {
31
+ const result = await repo.getAllTables('custom_schema');
32
+ expect(result).toBeDefined();
33
+ });
34
+ });
35
+
36
+ describe('getTableDetails', () => {
37
+ it('should return table column details', async () => {
38
+ const tableName = 'test_table';
39
+ const mockColumns = {
40
+ rows: [
41
+ {
42
+ column_name: 'id',
43
+ data_type: 'uuid',
44
+ is_nullable: 'NO',
45
+ column_default: null,
46
+ is_primary_key: true,
47
+ foreign_key: null,
48
+ },
49
+ ],
50
+ };
51
+
52
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
53
+ mockExecute.mockResolvedValueOnce(mockColumns);
54
+
55
+ const result = await testRepo.getTableDetails(tableName);
56
+
57
+ expect(result).toEqual([
58
+ {
59
+ name: 'id',
60
+ type: 'uuid',
61
+ nullable: false,
62
+ defaultValue: null,
63
+ isPrimaryKey: true,
64
+ foreignKey: null,
65
+ },
66
+ ]);
67
+ });
68
+ });
69
+
70
+ describe('getTableData', () => {
71
+ it('should return paginated data with filters', async () => {
72
+ const tableName = 'test_table';
73
+ const pagination = {
74
+ page: 1,
75
+ pageSize: 10,
76
+ sortBy: 'id',
77
+ sortOrder: 'desc' as const,
78
+ };
79
+ const filters = [
80
+ {
81
+ column: 'name',
82
+ operator: 'contains' as const,
83
+ value: 'test',
84
+ },
85
+ ];
86
+
87
+ const mockData = {
88
+ rows: [{ id: 1, name: 'test' }],
89
+ };
90
+ const mockCount = {
91
+ rows: [{ total: 1 }],
92
+ };
93
+
94
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
95
+ mockExecute.mockResolvedValueOnce(mockData).mockResolvedValueOnce(mockCount);
96
+
97
+ const result = await testRepo.getTableData(tableName, pagination, filters);
98
+
99
+ expect(result).toEqual({
100
+ data: mockData.rows,
101
+ pagination: {
102
+ page: 1,
103
+ pageSize: 10,
104
+ total: 1,
105
+ },
106
+ });
107
+ });
108
+ });
109
+
110
+ describe('updateRow', () => {
111
+ it('should update and return row data', async () => {
112
+ const tableName = 'test_table';
113
+ const id = '123';
114
+ const primaryKeyColumn = 'id';
115
+ const data = { name: 'updated' };
116
+
117
+ const mockResult = {
118
+ rows: [{ id: '123', name: 'updated' }],
119
+ };
120
+
121
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
122
+ mockExecute.mockResolvedValueOnce(mockResult);
123
+
124
+ const result = await testRepo.updateRow(tableName, id, primaryKeyColumn, data);
125
+
126
+ expect(result).toEqual(mockResult.rows[0]);
127
+ expect(mockExecute).toHaveBeenCalledTimes(1);
128
+ });
129
+ });
130
+
131
+ describe('deleteRow', () => {
132
+ it('should delete a row', async () => {
133
+ const tableName = 'test_table';
134
+ const id = '123';
135
+ const primaryKeyColumn = 'id';
136
+
137
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
138
+ mockExecute.mockResolvedValueOnce({ rows: [] });
139
+
140
+ await testRepo.deleteRow(tableName, id, primaryKeyColumn);
141
+
142
+ expect(mockExecute).toHaveBeenCalledTimes(1);
143
+ });
144
+ });
145
+
146
+ describe('insertRow', () => {
147
+ it('should insert and return new row data', async () => {
148
+ const tableName = 'test_table';
149
+ const data = { name: 'new row' };
150
+
151
+ const mockResult = {
152
+ rows: [{ id: '123', name: 'new row' }],
153
+ };
154
+
155
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
156
+ mockExecute.mockResolvedValueOnce(mockResult);
157
+
158
+ const result = await testRepo.insertRow(tableName, data);
159
+
160
+ expect(result).toEqual(mockResult.rows[0]);
161
+ expect(mockExecute).toHaveBeenCalledTimes(1);
162
+ });
163
+ });
164
+
165
+ describe('getTableCount', () => {
166
+ it('should return table count', async () => {
167
+ const tableName = 'test_table';
168
+ const mockResult = {
169
+ rows: [{ total: 42 }],
170
+ };
171
+
172
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
173
+ mockExecute.mockResolvedValueOnce(mockResult);
174
+
175
+ const result = await testRepo.getTableCount(tableName);
176
+
177
+ expect(result).toBe(42);
178
+ expect(mockExecute).toHaveBeenCalledTimes(1);
179
+ });
180
+ });
181
+
182
+ describe('batchDelete', () => {
183
+ it('should delete multiple rows', async () => {
184
+ const tableName = 'test_table';
185
+ const ids = ['1', '2', '3'];
186
+ const primaryKeyColumn = 'id';
187
+
188
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
189
+ mockExecute.mockResolvedValueOnce({ rows: [] });
190
+
191
+ await testRepo.batchDelete(tableName, ids, primaryKeyColumn);
192
+
193
+ expect(mockExecute).toHaveBeenCalledTimes(1);
194
+ });
195
+ });
196
+
197
+ describe('exportTableData', () => {
198
+ it('should export table data with default pagination', async () => {
199
+ const tableName = 'test_table';
200
+ const mockData = {
201
+ rows: [{ id: 1, name: 'test' }],
202
+ };
203
+ const mockCount = {
204
+ rows: [{ total: 1 }],
205
+ };
206
+
207
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
208
+ mockExecute.mockResolvedValueOnce(mockData).mockResolvedValueOnce(mockCount);
209
+
210
+ const result = await testRepo.exportTableData(tableName);
211
+
212
+ expect(result).toEqual({
213
+ data: mockData.rows,
214
+ pagination: {
215
+ page: 1,
216
+ pageSize: 1000,
217
+ total: 1,
218
+ },
219
+ });
220
+ });
221
+
222
+ it('should export table data with custom pagination and filters', async () => {
223
+ const tableName = 'test_table';
224
+ const pagination = { page: 2, pageSize: 50 };
225
+ const filters = [
226
+ {
227
+ column: 'status',
228
+ operator: 'equals' as const,
229
+ value: 'active',
230
+ },
231
+ ];
232
+
233
+ const mockData = {
234
+ rows: [{ id: 1, status: 'active' }],
235
+ };
236
+ const mockCount = {
237
+ rows: [{ total: 1 }],
238
+ };
239
+
240
+ mockExecute.mockResolvedValueOnce(mockData).mockResolvedValueOnce(mockCount);
241
+
242
+ const testRepo = new TableViewerRepo(mockDB as any, userId);
243
+
244
+ const result = await testRepo.exportTableData(tableName, pagination, filters);
245
+
246
+ expect(result).toEqual({
247
+ data: mockData.rows,
248
+ pagination: {
249
+ page: 2,
250
+ pageSize: 50,
251
+ total: 1,
252
+ },
253
+ });
254
+ });
255
+ });
256
+ });