@lobehub/lobehub 2.0.0-next.194 → 2.0.0-next.196

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 (61) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/setting.json +0 -3
  4. package/locales/bg-BG/setting.json +0 -3
  5. package/locales/de-DE/setting.json +0 -3
  6. package/locales/en-US/setting.json +0 -3
  7. package/locales/es-ES/setting.json +0 -3
  8. package/locales/fa-IR/setting.json +0 -3
  9. package/locales/fr-FR/setting.json +0 -3
  10. package/locales/it-IT/setting.json +0 -3
  11. package/locales/ja-JP/setting.json +0 -3
  12. package/locales/ko-KR/setting.json +0 -3
  13. package/locales/nl-NL/setting.json +0 -3
  14. package/locales/pl-PL/setting.json +0 -3
  15. package/locales/pt-BR/setting.json +0 -3
  16. package/locales/ru-RU/setting.json +0 -3
  17. package/locales/tr-TR/setting.json +0 -3
  18. package/locales/vi-VN/setting.json +0 -3
  19. package/locales/zh-CN/setting.json +0 -3
  20. package/locales/zh-TW/setting.json +0 -3
  21. package/package.json +1 -1
  22. package/packages/const/src/fetch.ts +1 -4
  23. package/packages/database/src/models/user.ts +8 -0
  24. package/packages/database/src/repositories/aiInfra/index.test.ts +11 -8
  25. package/packages/database/src/repositories/dataExporter/index.test.ts +11 -9
  26. package/packages/database/src/repositories/tableViewer/index.test.ts +13 -14
  27. package/packages/model-runtime/src/providers/zhipu/index.ts +6 -6
  28. package/packages/types/src/auth.ts +0 -4
  29. package/packages/utils/src/server/xor.test.ts +1 -2
  30. package/src/app/(backend)/_deprecated/createBizOpenAI/auth.test.ts +7 -41
  31. package/src/app/(backend)/_deprecated/createBizOpenAI/auth.ts +1 -15
  32. package/src/app/(backend)/_deprecated/createBizOpenAI/index.ts +2 -9
  33. package/src/app/(backend)/middleware/auth/index.ts +0 -1
  34. package/src/app/(backend)/middleware/auth/utils.test.ts +2 -42
  35. package/src/app/(backend)/middleware/auth/utils.ts +3 -17
  36. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +0 -5
  37. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +0 -6
  38. package/src/app/(backend)/webapi/plugin/gateway/route.ts +2 -32
  39. package/src/app/[variants]/(main)/settings/common/features/Common/Common.tsx +1 -16
  40. package/src/envs/app.ts +2 -0
  41. package/src/libs/trpc/lambda/middleware/index.ts +1 -0
  42. package/src/libs/trpc/lambda/middleware/telemetry.test.ts +237 -0
  43. package/src/libs/trpc/lambda/middleware/telemetry.ts +74 -0
  44. package/src/locales/default/setting.ts +0 -3
  45. package/src/server/routers/lambda/market/index.ts +1 -93
  46. package/src/server/routers/tools/_helpers/index.ts +1 -0
  47. package/src/server/routers/tools/_helpers/scheduleToolCallReport.ts +113 -0
  48. package/src/server/routers/tools/index.ts +2 -2
  49. package/src/server/routers/tools/market.ts +375 -0
  50. package/src/server/routers/tools/mcp.ts +77 -20
  51. package/src/services/chat/index.ts +0 -2
  52. package/src/services/codeInterpreter.ts +6 -6
  53. package/src/services/mcp.test.ts +60 -46
  54. package/src/services/mcp.ts +67 -48
  55. package/src/store/chat/slices/plugin/action.test.ts +191 -0
  56. package/src/store/chat/slices/plugin/actions/internals.ts +2 -18
  57. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +31 -44
  58. package/packages/database/src/client/db.test.ts +0 -52
  59. package/packages/database/src/client/db.ts +0 -195
  60. package/packages/database/src/client/type.ts +0 -6
  61. package/src/server/routers/tools/codeInterpreter.ts +0 -255
@@ -1429,4 +1429,195 @@ describe('ChatPluginAction', () => {
1429
1429
  });
1430
1430
  });
1431
1431
  });
1432
+
1433
+ describe('Plugin invoke functions use optimisticUpdateToolMessage', () => {
1434
+ const messageId = 'message-id';
1435
+ const payload: ChatToolPayload = {
1436
+ apiName: 'test-api',
1437
+ arguments: '{}',
1438
+ id: 'tool-call-id',
1439
+ identifier: 'test-plugin',
1440
+ type: 'default',
1441
+ };
1442
+
1443
+ describe('invokeMCPTypePlugin', () => {
1444
+ it('should use optimisticUpdateToolMessage for successful result', async () => {
1445
+ const mockResult = {
1446
+ content: 'mcp result content',
1447
+ state: { content: [], isError: false },
1448
+ success: true,
1449
+ };
1450
+
1451
+ // Mock the mcpService
1452
+ const mcpService = await import('@/services/mcp');
1453
+ vi.spyOn(mcpService.mcpService, 'invokeMcpToolCall').mockResolvedValue(mockResult);
1454
+
1455
+ const optimisticUpdateToolMessageMock = vi.fn().mockResolvedValue(undefined);
1456
+
1457
+ act(() => {
1458
+ useChatStore.setState({
1459
+ activeAgentId: 'session-id',
1460
+ messagesMap: { [messageMapKey({ agentId: 'session-id' })]: [] },
1461
+ optimisticUpdateToolMessage: optimisticUpdateToolMessageMock,
1462
+ replaceMessages: vi.fn(),
1463
+ messageOperationMap: {},
1464
+ operations: {},
1465
+ });
1466
+ });
1467
+
1468
+ const { result } = renderHook(() => useChatStore());
1469
+
1470
+ await act(async () => {
1471
+ await result.current.invokeMCPTypePlugin(messageId, payload);
1472
+ });
1473
+
1474
+ expect(optimisticUpdateToolMessageMock).toHaveBeenCalledWith(
1475
+ messageId,
1476
+ {
1477
+ content: mockResult.content,
1478
+ pluginError: undefined,
1479
+ pluginState: mockResult.state,
1480
+ },
1481
+ undefined,
1482
+ );
1483
+ });
1484
+
1485
+ it('should use optimisticUpdateToolMessage for error result', async () => {
1486
+ const mockResult = {
1487
+ content: 'error content',
1488
+ error: { message: 'test error' },
1489
+ state: { content: [], isError: true },
1490
+ success: false,
1491
+ };
1492
+
1493
+ const mcpService = await import('@/services/mcp');
1494
+ vi.spyOn(mcpService.mcpService, 'invokeMcpToolCall').mockResolvedValue(mockResult);
1495
+
1496
+ const optimisticUpdateToolMessageMock = vi.fn().mockResolvedValue(undefined);
1497
+
1498
+ act(() => {
1499
+ useChatStore.setState({
1500
+ activeAgentId: 'session-id',
1501
+ messagesMap: { [messageMapKey({ agentId: 'session-id' })]: [] },
1502
+ optimisticUpdateToolMessage: optimisticUpdateToolMessageMock,
1503
+ replaceMessages: vi.fn(),
1504
+ messageOperationMap: {},
1505
+ operations: {},
1506
+ });
1507
+ });
1508
+
1509
+ const { result } = renderHook(() => useChatStore());
1510
+
1511
+ await act(async () => {
1512
+ await result.current.invokeMCPTypePlugin(messageId, payload);
1513
+ });
1514
+
1515
+ expect(optimisticUpdateToolMessageMock).toHaveBeenCalledWith(
1516
+ messageId,
1517
+ {
1518
+ content: mockResult.content,
1519
+ pluginError: mockResult.error,
1520
+ pluginState: undefined,
1521
+ },
1522
+ undefined,
1523
+ );
1524
+ });
1525
+ });
1526
+
1527
+ describe('invokeKlavisTypePlugin', () => {
1528
+ it('should use optimisticUpdateToolMessage for successful result', async () => {
1529
+ const mockResult = {
1530
+ content: 'klavis result content',
1531
+ state: { data: 'test-data' },
1532
+ success: true,
1533
+ };
1534
+
1535
+ // Mock useToolStore to return a server
1536
+ vi.spyOn(useToolStore, 'getState').mockReturnValue({
1537
+ servers: [{ identifier: 'test-plugin', serverUrl: 'http://test.com' }],
1538
+ callKlavisTool: vi.fn().mockResolvedValue({
1539
+ success: true,
1540
+ data: mockResult,
1541
+ }),
1542
+ } as any);
1543
+
1544
+ const optimisticUpdateToolMessageMock = vi.fn().mockResolvedValue(undefined);
1545
+
1546
+ act(() => {
1547
+ useChatStore.setState({
1548
+ activeAgentId: 'session-id',
1549
+ messagesMap: { [messageMapKey({ agentId: 'session-id' })]: [] },
1550
+ optimisticUpdateToolMessage: optimisticUpdateToolMessageMock,
1551
+ replaceMessages: vi.fn(),
1552
+ messageOperationMap: {},
1553
+ operations: {},
1554
+ });
1555
+ });
1556
+
1557
+ const { result } = renderHook(() => useChatStore());
1558
+
1559
+ await act(async () => {
1560
+ await result.current.invokeKlavisTypePlugin(messageId, payload);
1561
+ });
1562
+
1563
+ expect(optimisticUpdateToolMessageMock).toHaveBeenCalledWith(
1564
+ messageId,
1565
+ {
1566
+ content: mockResult.content,
1567
+ pluginError: undefined,
1568
+ pluginState: mockResult.state,
1569
+ },
1570
+ undefined,
1571
+ );
1572
+ });
1573
+ });
1574
+
1575
+ describe('invokeCloudCodeInterpreterTool', () => {
1576
+ it('should use optimisticUpdateToolMessage for successful result', async () => {
1577
+ const mockResult = {
1578
+ content: 'code interpreter result',
1579
+ state: { output: 'test output' },
1580
+ success: true,
1581
+ };
1582
+
1583
+ // Mock CloudSandboxExecutionRuntime using doMock for dynamic mocking
1584
+ vi.doMock('@lobechat/builtin-tool-cloud-sandbox/executionRuntime', () => ({
1585
+ CloudSandboxExecutionRuntime: class {
1586
+ 'test-api' = vi.fn().mockResolvedValue(mockResult);
1587
+ },
1588
+ }));
1589
+
1590
+ const optimisticUpdateToolMessageMock = vi.fn().mockResolvedValue(undefined);
1591
+
1592
+ act(() => {
1593
+ useChatStore.setState({
1594
+ activeAgentId: 'session-id',
1595
+ messagesMap: { [messageMapKey({ agentId: 'session-id' })]: [] },
1596
+ optimisticUpdateToolMessage: optimisticUpdateToolMessageMock,
1597
+ replaceMessages: vi.fn(),
1598
+ messageOperationMap: {},
1599
+ operations: {},
1600
+ });
1601
+ });
1602
+
1603
+ const { result } = renderHook(() => useChatStore());
1604
+
1605
+ await act(async () => {
1606
+ await result.current.invokeCloudCodeInterpreterTool(messageId, payload);
1607
+ });
1608
+
1609
+ expect(optimisticUpdateToolMessageMock).toHaveBeenCalledWith(
1610
+ messageId,
1611
+ {
1612
+ content: mockResult.content,
1613
+ pluginError: undefined,
1614
+ pluginState: mockResult.state,
1615
+ },
1616
+ undefined,
1617
+ );
1618
+
1619
+ vi.doUnmock('@lobechat/builtin-tool-cloud-sandbox/executionRuntime');
1620
+ });
1621
+ });
1622
+ });
1432
1623
  });
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
2
2
  import { ToolNameResolver } from '@lobechat/context-engine';
3
- import { type ChatToolPayload, type MessageToolCall, type ToolsCallingContext } from '@lobechat/types';
3
+ import { type ChatToolPayload, type MessageToolCall } from '@lobechat/types';
4
4
  import { type LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
5
5
  import { type StateCreator } from 'zustand/vanilla';
6
6
 
@@ -9,8 +9,6 @@ import { useToolStore } from '@/store/tool';
9
9
  import { klavisStoreSelectors, pluginSelectors } from '@/store/tool/selectors';
10
10
  import { builtinTools } from '@/tools';
11
11
 
12
- import { displayMessageSelectors } from '../../message/selectors';
13
-
14
12
  /**
15
13
  * Internal utility methods and runtime state management
16
14
  * These are building blocks used by other actions
@@ -20,11 +18,6 @@ export interface PluginInternalsAction {
20
18
  * Transform tool calls from runtime format to storage format
21
19
  */
22
20
  internal_transformToolCalls: (toolCalls: MessageToolCall[]) => ChatToolPayload[];
23
-
24
- /**
25
- * Construct tools calling context for plugin invocation
26
- */
27
- internal_constructToolsCallingContext: (id: string) => ToolsCallingContext | undefined;
28
21
  }
29
22
 
30
23
  export const pluginInternals: StateCreator<
@@ -32,7 +25,7 @@ export const pluginInternals: StateCreator<
32
25
  [['zustand/devtools', never]],
33
26
  [],
34
27
  PluginInternalsAction
35
- > = (set, get) => ({
28
+ > = () => ({
36
29
  internal_transformToolCalls: (toolCalls) => {
37
30
  const toolNameResolver = new ToolNameResolver();
38
31
 
@@ -77,13 +70,4 @@ export const pluginInternals: StateCreator<
77
70
  source: sourceMap[payload.identifier],
78
71
  }));
79
72
  },
80
-
81
- internal_constructToolsCallingContext: (id: string) => {
82
- const message = displayMessageSelectors.getDisplayMessageById(id)(get());
83
- if (!message) return;
84
-
85
- return {
86
- topicId: message.topicId,
87
- };
88
- },
89
73
  });
@@ -261,12 +261,6 @@ export const pluginTypes: StateCreator<
261
261
  },
262
262
 
263
263
  invokeCloudCodeInterpreterTool: async (id, payload) => {
264
- const {
265
- optimisticUpdateMessageContent,
266
- optimisticUpdatePluginState,
267
- optimisticUpdateMessagePluginError,
268
- } = get();
269
-
270
264
  // Get message to extract topicId
271
265
  const message = dbMessageSelectors.getDbMessageById(id)(get());
272
266
 
@@ -336,16 +330,16 @@ export const pluginTypes: StateCreator<
336
330
 
337
331
  const context = operationId ? { operationId } : undefined;
338
332
 
339
- await Promise.all([
340
- optimisticUpdateMessageContent(id, data.content, undefined, context),
341
- (async () => {
342
- if (data.success && data.state) {
343
- await optimisticUpdatePluginState(id, data.state, context);
344
- } else if (!data.success && data.error) {
345
- await optimisticUpdateMessagePluginError(id, data.error, context);
346
- }
347
- })(),
348
- ]);
333
+ // Use optimisticUpdateToolMessage to update content and state/error in a single call
334
+ await get().optimisticUpdateToolMessage(
335
+ id,
336
+ {
337
+ content: data.content,
338
+ pluginError: data.success ? undefined : data.error,
339
+ pluginState: data.success ? data.state : undefined,
340
+ },
341
+ context,
342
+ );
349
343
 
350
344
  // Handle exportFile: save exported file and associate with assistant message (parent)
351
345
  if (payload.apiName === 'exportFile' && data.success && data.state) {
@@ -422,12 +416,6 @@ export const pluginTypes: StateCreator<
422
416
  },
423
417
 
424
418
  invokeKlavisTypePlugin: async (id, payload) => {
425
- const {
426
- optimisticUpdateMessageContent,
427
- optimisticUpdatePluginState,
428
- optimisticUpdateMessagePluginError,
429
- } = get();
430
-
431
419
  let data: MCPToolCallResult | undefined;
432
420
 
433
421
  // Get message to extract sessionId/topicId
@@ -510,13 +498,16 @@ export const pluginTypes: StateCreator<
510
498
  // operationId already declared above, reuse it
511
499
  const context = operationId ? { operationId } : undefined;
512
500
 
513
- await Promise.all([
514
- optimisticUpdateMessageContent(id, data.content, undefined, context),
515
- (async () => {
516
- if (data.success) await optimisticUpdatePluginState(id, data.state, context);
517
- else await optimisticUpdateMessagePluginError(id, data.error, context);
518
- })(),
519
- ]);
501
+ // Use optimisticUpdateToolMessage to update content and state/error in a single call
502
+ await get().optimisticUpdateToolMessage(
503
+ id,
504
+ {
505
+ content: data.content,
506
+ pluginError: data.success ? undefined : data.error,
507
+ pluginState: data.success ? data.state : undefined,
508
+ },
509
+ context,
510
+ );
520
511
 
521
512
  return data.content;
522
513
  },
@@ -561,12 +552,6 @@ export const pluginTypes: StateCreator<
561
552
  },
562
553
 
563
554
  invokeMCPTypePlugin: async (id, payload) => {
564
- const {
565
- optimisticUpdateMessageContent,
566
- internal_constructToolsCallingContext,
567
- optimisticUpdatePluginState,
568
- optimisticUpdateMessagePluginError,
569
- } = get();
570
555
  let data: MCPToolCallResult | undefined;
571
556
 
572
557
  // Get message to extract agentId/topicId
@@ -586,10 +571,9 @@ export const pluginTypes: StateCreator<
586
571
  );
587
572
 
588
573
  try {
589
- const context = internal_constructToolsCallingContext(id);
590
574
  const result = await mcpService.invokeMcpToolCall(payload, {
591
575
  signal: abortController?.signal,
592
- topicId: context?.topicId,
576
+ topicId: message?.topicId,
593
577
  });
594
578
 
595
579
  if (!!result) data = result;
@@ -620,13 +604,16 @@ export const pluginTypes: StateCreator<
620
604
  // operationId already declared above, reuse it
621
605
  const context = operationId ? { operationId } : undefined;
622
606
 
623
- await Promise.all([
624
- optimisticUpdateMessageContent(id, data.content, undefined, context),
625
- (async () => {
626
- if (data.success) await optimisticUpdatePluginState(id, data.state, context);
627
- else await optimisticUpdateMessagePluginError(id, data.error, context);
628
- })(),
629
- ]);
607
+ // Use optimisticUpdateToolMessage to update content and state/error in a single call
608
+ await get().optimisticUpdateToolMessage(
609
+ id,
610
+ {
611
+ content: data.content,
612
+ pluginError: data.success ? undefined : data.error,
613
+ pluginState: data.success ? data.state : undefined,
614
+ },
615
+ context,
616
+ );
630
617
 
631
618
  return data.content;
632
619
  },
@@ -1,52 +0,0 @@
1
- import { PGlite } from '@electric-sql/pglite';
2
- import { beforeEach, describe, expect, it, vi } from 'vitest';
3
-
4
- vi.mock('@electric-sql/pglite', () => ({
5
- PGlite: vi.fn(() => ({})),
6
- }));
7
-
8
- vi.mock('@electric-sql/pglite/vector', () => ({
9
- vector: vi.fn(),
10
- }));
11
-
12
- vi.mock('drizzle-orm/pglite', () => ({
13
- drizzle: vi.fn(() => ({
14
- dialect: {
15
- migrate: vi.fn().mockResolvedValue(undefined),
16
- },
17
- })),
18
- }));
19
-
20
- beforeEach(() => {
21
- vi.clearAllMocks();
22
- vi.resetModules();
23
- });
24
-
25
- describe('DatabaseManager', () => {
26
- describe('initializeDB', () => {
27
- it('should initialize database with PGlite', async () => {
28
- const { initializeDB } = await import('./db');
29
- await initializeDB();
30
-
31
- expect(PGlite).toHaveBeenCalledWith('idb://lobechat', {
32
- extensions: { vector: expect.any(Function) },
33
- relaxedDurability: true,
34
- });
35
- });
36
-
37
- it('should only initialize once when called multiple times', async () => {
38
- const { initializeDB } = await import('./db');
39
- await Promise.all([initializeDB(), initializeDB()]);
40
-
41
- expect(PGlite).toHaveBeenCalledTimes(1);
42
- });
43
- });
44
-
45
- describe('clientDB proxy', () => {
46
- it('should provide access to database after initialization', async () => {
47
- const { clientDB, initializeDB } = await import('./db');
48
- await initializeDB();
49
- expect(clientDB).toBeDefined();
50
- });
51
- });
52
- });
@@ -1,195 +0,0 @@
1
- import { PGlite } from '@electric-sql/pglite';
2
- import { vector } from '@electric-sql/pglite/vector';
3
- import { sql } from 'drizzle-orm';
4
- import { PgliteDatabase, drizzle } from 'drizzle-orm/pglite';
5
- import { Md5 } from 'ts-md5';
6
-
7
- import migrations from '../core/migrations.json';
8
- import { DrizzleMigrationModel } from '../models/drizzleMigration';
9
- import * as schema from '../schemas';
10
-
11
- const pgliteSchemaHashCache = 'LOBE_CHAT_PGLITE_SCHEMA_HASH';
12
- const DB_NAME = 'lobechat';
13
-
14
- type DrizzleInstance = PgliteDatabase<typeof schema>;
15
-
16
- class DatabaseManager {
17
- private static instance: DatabaseManager;
18
- private dbInstance: DrizzleInstance | null = null;
19
- private initPromise: Promise<DrizzleInstance> | null = null;
20
- private isLocalDBSchemaSynced = false;
21
-
22
- private constructor() {}
23
-
24
- static getInstance() {
25
- if (!DatabaseManager.instance) {
26
- DatabaseManager.instance = new DatabaseManager();
27
- }
28
- return DatabaseManager.instance;
29
- }
30
-
31
- private async migrate(): Promise<DrizzleInstance> {
32
- if (this.isLocalDBSchemaSynced) return this.db;
33
-
34
- let hash: string | undefined;
35
- if (typeof localStorage !== 'undefined') {
36
- const cacheHash = localStorage.getItem(pgliteSchemaHashCache);
37
- hash = Md5.hashStr(JSON.stringify(migrations));
38
- // if hash is the same, no need to migrate
39
- if (hash === cacheHash) {
40
- try {
41
- const drizzleMigration = new DrizzleMigrationModel(this.db as any);
42
-
43
- // Check if tables exist in database
44
- const tableCount = await drizzleMigration.getTableCounts();
45
-
46
- // If table count > 0, consider database properly initialized
47
- if (tableCount > 0) {
48
- this.isLocalDBSchemaSynced = true;
49
- return this.db;
50
- }
51
- } catch (error) {
52
- console.warn('Error checking table existence, proceeding with migration', error);
53
- }
54
- }
55
- }
56
-
57
- const start = Date.now();
58
- try {
59
- // @ts-expect-error - migrate internal API
60
- await this.db.dialect.migrate(migrations, this.db.session, {});
61
-
62
- if (typeof localStorage !== 'undefined' && hash) {
63
- localStorage.setItem(pgliteSchemaHashCache, hash);
64
- }
65
-
66
- this.isLocalDBSchemaSynced = true;
67
- console.info(`🗂 Migration success, take ${Date.now() - start}ms`);
68
- } catch (cause) {
69
- console.error('❌ Local database schema migration failed', cause);
70
- throw cause;
71
- }
72
-
73
- return this.db;
74
- }
75
-
76
- async initialize(): Promise<DrizzleInstance> {
77
- if (this.initPromise) return this.initPromise;
78
-
79
- this.initPromise = (async () => {
80
- if (this.dbInstance) return this.dbInstance;
81
-
82
- const time = Date.now();
83
-
84
- // 直接使用 pglite,自动处理 wasm 加载
85
- const pglite = new PGlite(`idb://${DB_NAME}`, {
86
- extensions: { vector },
87
- relaxedDurability: true,
88
- });
89
-
90
- this.dbInstance = drizzle({ client: pglite, schema });
91
-
92
- await this.migrate();
93
-
94
- console.log(`✅ Database initialized in ${Date.now() - time}ms`);
95
-
96
- return this.dbInstance;
97
- })();
98
-
99
- return this.initPromise;
100
- }
101
-
102
- get db(): DrizzleInstance {
103
- if (!this.dbInstance) {
104
- throw new Error('Database not initialized. Please call initialize() first.');
105
- }
106
- return this.dbInstance;
107
- }
108
-
109
- createProxy(): DrizzleInstance {
110
- return new Proxy({} as DrizzleInstance, {
111
- get: (target, prop) => {
112
- return this.db[prop as keyof DrizzleInstance];
113
- },
114
- });
115
- }
116
-
117
- async resetDatabase(): Promise<void> {
118
- // 1. Close existing PGlite connection
119
- if (this.dbInstance) {
120
- try {
121
- // @ts-ignore
122
- await (this.dbInstance.session as any).client.close();
123
- console.log('PGlite instance closed successfully.');
124
- } catch (e) {
125
- console.error('Error closing PGlite instance:', e);
126
- }
127
- }
128
-
129
- // 2. Reset database instance and initialization state
130
- this.dbInstance = null;
131
- this.initPromise = null;
132
- this.isLocalDBSchemaSynced = false;
133
-
134
- // 3. Delete IndexedDB database
135
- return new Promise<void>((resolve, reject) => {
136
- if (typeof indexedDB === 'undefined') {
137
- console.warn('IndexedDB is not available, cannot delete database');
138
- resolve();
139
- return;
140
- }
141
-
142
- const dbName = `/pglite/${DB_NAME}`;
143
- const request = indexedDB.deleteDatabase(dbName);
144
-
145
- request.onsuccess = () => {
146
- console.log(`✅ Database '${dbName}' reset successfully`);
147
-
148
- if (typeof localStorage !== 'undefined') {
149
- localStorage.removeItem(pgliteSchemaHashCache);
150
- }
151
-
152
- resolve();
153
- };
154
-
155
- // eslint-disable-next-line unicorn/prefer-add-event-listener
156
- request.onerror = (event) => {
157
- const error = (event.target as IDBOpenDBRequest)?.error;
158
- console.error(`❌ Error resetting database '${dbName}':`, error);
159
- reject(
160
- new Error(
161
- `Failed to reset database '${dbName}'. Error: ${error?.message || 'Unknown error'}`,
162
- ),
163
- );
164
- };
165
-
166
- request.onblocked = (event) => {
167
- console.warn(`Deletion of database '${dbName}' is blocked.`, event);
168
- reject(
169
- new Error(
170
- `Failed to reset database '${dbName}' because it is blocked by other open connections.`,
171
- ),
172
- );
173
- };
174
- });
175
- }
176
- }
177
-
178
- // Export singleton
179
- const dbManager = DatabaseManager.getInstance();
180
-
181
- export const clientDB = dbManager.createProxy();
182
-
183
- export const initializeDB = () => dbManager.initialize();
184
-
185
- export const resetClientDatabase = async () => {
186
- await dbManager.resetDatabase();
187
- };
188
-
189
- export const updateMigrationRecord = async (migrationHash: string) => {
190
- await clientDB.execute(
191
- sql`INSERT INTO "drizzle"."__drizzle_migrations" ("hash", "created_at") VALUES (${migrationHash}, ${Date.now()});`,
192
- );
193
-
194
- await initializeDB();
195
- };
@@ -1,6 +0,0 @@
1
- export interface InitMeta {
2
- dbName: string;
3
- fsBundle: Blob;
4
- vectorBundlePath: string;
5
- wasmModule: WebAssembly.Module;
6
- }