@rozenite/sqlite-plugin 1.7.0-rc.2 → 1.8.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.
@@ -0,0 +1,152 @@
1
+ import { useCallback } from 'react';
2
+ import { useRozenitePluginAgentTool, type AgentTool } from '@rozenite/agent-bridge';
3
+ import { formatSqliteError } from '../shared/bridge-values';
4
+ import { normalizeSingleStatementSql, splitSqlStatements } from '../shared/sql';
5
+ import type { SqliteExecuteStatementsError } from '../shared/types';
6
+ import type { SqliteDatabaseView } from './sqlite-view';
7
+
8
+ type ExecuteSqlInput = {
9
+ databaseId: string;
10
+ sql: string;
11
+ };
12
+
13
+ const pluginId = '@rozenite/sqlite-plugin';
14
+
15
+ const listDatabasesTool: AgentTool = {
16
+ name: 'list-databases',
17
+ description: 'List all registered SQLite databases.',
18
+ inputSchema: { type: 'object', properties: {} },
19
+ };
20
+
21
+ const executeSqlTool: AgentTool = {
22
+ name: 'execute-sql',
23
+ description:
24
+ 'Execute one or more SQL statements against a database. Supports SELECT, INSERT, UPDATE, DELETE, PRAGMA, DDL, and multi-statement scripts. Returns per-statement results including rows, columns, and metadata. Statements are executed in order and stop on first error.',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {
28
+ databaseId: {
29
+ type: 'string',
30
+ description: 'Database ID from list-databases.',
31
+ },
32
+ sql: {
33
+ type: 'string',
34
+ description:
35
+ 'SQL to execute. May contain multiple semicolon-separated statements.',
36
+ },
37
+ },
38
+ required: ['databaseId', 'sql'],
39
+ },
40
+ };
41
+
42
+ const isExecuteStatementsError = (
43
+ error: unknown,
44
+ ): error is SqliteExecuteStatementsError =>
45
+ error instanceof Error &&
46
+ ('completedResults' in error || 'failedStatementIndex' in error);
47
+
48
+ export const useSqliteAgentTools = (views: SqliteDatabaseView[]) => {
49
+ const resolveDatabase = useCallback(
50
+ (databaseId: string) => {
51
+ const database = views.find((view) => view.id === databaseId);
52
+
53
+ if (!database) {
54
+ const available = views.map((v) => v.id).join(', ');
55
+ throw new Error(
56
+ `Unknown databaseId "${databaseId}". Available: ${available || '(none)'}`,
57
+ );
58
+ }
59
+
60
+ return database;
61
+ },
62
+ [views],
63
+ );
64
+
65
+ useRozenitePluginAgentTool({
66
+ pluginId,
67
+ tool: listDatabasesTool,
68
+ handler: () => ({
69
+ databases: views.map(({ id, name, adapterId, adapterName }) => ({
70
+ id,
71
+ name,
72
+ adapterId,
73
+ adapterName,
74
+ })),
75
+ }),
76
+ });
77
+
78
+ useRozenitePluginAgentTool<ExecuteSqlInput>({
79
+ pluginId,
80
+ tool: executeSqlTool,
81
+ handler: async ({ databaseId, sql }) => {
82
+ const database = resolveDatabase(databaseId);
83
+ const statementSegments = splitSqlStatements(sql);
84
+
85
+ if (statementSegments.length === 0) {
86
+ throw new Error('SQL cannot be empty.');
87
+ }
88
+
89
+ const statementInputs = statementSegments.map((segment) => ({
90
+ sql: normalizeSingleStatementSql(segment.text),
91
+ }));
92
+
93
+ try {
94
+ const results = await database.executeStatements(statementInputs);
95
+
96
+ return {
97
+ databaseId,
98
+ totalStatementCount: statementSegments.length,
99
+ failedStatementIndex: null,
100
+ statements: statementSegments.map((_, index) => {
101
+ const result = results[index]!;
102
+ return {
103
+ index,
104
+ sql: statementInputs[index]!.sql,
105
+ rows: result.rows,
106
+ columns: result.columns,
107
+ metadata: result.metadata,
108
+ };
109
+ }),
110
+ };
111
+ } catch (error) {
112
+ if (!isExecuteStatementsError(error)) {
113
+ throw new Error(formatSqliteError(error));
114
+ }
115
+
116
+ const failedStatementIndex = Math.max(
117
+ 0,
118
+ Math.min(
119
+ typeof error.failedStatementIndex === 'number'
120
+ ? error.failedStatementIndex
121
+ : (error.completedResults?.length ?? 0),
122
+ statementSegments.length - 1,
123
+ ),
124
+ );
125
+ const completedResults = (error.completedResults ?? []).slice(
126
+ 0,
127
+ failedStatementIndex,
128
+ );
129
+
130
+ return {
131
+ databaseId,
132
+ totalStatementCount: statementSegments.length,
133
+ failedStatementIndex,
134
+ statements: [
135
+ ...completedResults.map((result, index) => ({
136
+ index,
137
+ sql: statementInputs[index]!.sql,
138
+ rows: result.rows,
139
+ columns: result.columns,
140
+ metadata: result.metadata,
141
+ })),
142
+ {
143
+ index: failedStatementIndex,
144
+ sql: statementInputs[failedStatementIndex]!.sql,
145
+ error: formatSqliteError(error),
146
+ },
147
+ ],
148
+ };
149
+ }
150
+ },
151
+ });
152
+ };
package/vite.config.ts CHANGED
@@ -5,6 +5,9 @@ import { rozenitePlugin } from '@rozenite/vite-plugin';
5
5
  export default defineConfig({
6
6
  root: __dirname,
7
7
  plugins: [rozenitePlugin()],
8
+ test: {
9
+ passWithNoTests: true,
10
+ },
8
11
  base: './',
9
12
  build: {
10
13
  outDir: './dist',