@xpert-ai/plugin-sdk 3.18.4 → 3.18.6

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 (34) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +44 -0
  3. package/data-workbench.cjs.default.js +1 -0
  4. package/data-workbench.cjs.js +518 -0
  5. package/data-workbench.cjs.mjs +2 -0
  6. package/data-workbench.d.ts +1 -0
  7. package/data-workbench.esm.js +512 -0
  8. package/index.cjs.js +3118 -33
  9. package/index.esm.js +3109 -39
  10. package/model-provider.cjs.js +9 -0
  11. package/model-provider.esm.js +8 -2
  12. package/package.json +33 -27
  13. package/src/data-workbench.d.ts +3 -0
  14. package/src/lib/ai-model/utils/tokenizer.d.ts +6 -0
  15. package/src/lib/core/runtime-capability.d.ts +4 -0
  16. package/src/lib/data/datasource/index.d.ts +3 -0
  17. package/src/lib/data/datasource/types.d.ts +2 -0
  18. package/src/lib/data/datasource/workbench-adapter.d.ts +39 -0
  19. package/src/lib/data/datasource/workbench-sql.d.ts +12 -0
  20. package/src/lib/data/datasource/workbench.d.ts +133 -0
  21. package/src/lib/mcp/resource-template.d.ts +2 -3
  22. package/src/lib/mcp/resource.d.ts +5 -1
  23. package/src/lib/rag/textsplitter/strategy.interface.d.ts +15 -4
  24. package/src/lib/rag/transformer/strategy.interface.d.ts +3 -0
  25. package/src/lib/runtime/capabilities/index.d.ts +1 -0
  26. package/src/lib/runtime/capabilities/knowledge-graph.d.ts +59 -0
  27. package/src/lib/runtime/capabilities/workspace-files.d.ts +2 -0
  28. package/src/lib/tool-provider/component.d.ts +2 -0
  29. package/src/lib/tool-provider/decorators.d.ts +1 -1
  30. package/src/lib/tool-provider/index.d.ts +3 -0
  31. package/src/lib/tool-provider/mcp-methods.d.ts +39 -0
  32. package/src/lib/tool-provider/provider-methods.d.ts +5 -0
  33. package/src/lib/tool-provider/tool-result.d.ts +1 -1
  34. package/src/lib/tool-provider/types.d.ts +12 -1
@@ -0,0 +1,512 @@
1
+ import { _ as _extends } from './_extends.esm.js';
2
+
3
+ const DataSourceRuntimeCapability = Object.freeze({
4
+ id: 'platform.datasource.workbench',
5
+ description: 'Scoped database workbench access; credentials remain host-owned.',
6
+ __type: undefined
7
+ });
8
+
9
+ const WRITE = new Set([
10
+ 'INSERT',
11
+ 'UPDATE',
12
+ 'DELETE',
13
+ 'CREATE',
14
+ 'ALTER',
15
+ 'DROP',
16
+ 'TRUNCATE',
17
+ 'RENAME'
18
+ ]);
19
+ const FORBIDDEN = new Set([
20
+ 'INTO',
21
+ 'OUTFILE',
22
+ 'DUMPFILE',
23
+ 'PROCEDURE',
24
+ 'CALL',
25
+ 'COPY',
26
+ 'LOAD',
27
+ 'EXPORT',
28
+ 'IMPORT',
29
+ 'GRANT',
30
+ 'REVOKE',
31
+ 'LOCK',
32
+ 'UNLOCK',
33
+ 'KILL',
34
+ 'SET',
35
+ 'RESET',
36
+ 'ANALYZE',
37
+ 'VACUUM',
38
+ 'OPTIMIZE',
39
+ 'ATTACH',
40
+ 'DETACH'
41
+ ]);
42
+ const SAFE_FUNCTIONS = new Set('ABS ACOS ASIN ATAN ATAN2 AVG CAST CEIL CEILING COALESCE CONCAT CONCAT_WS CONVERT COS COUNT CURDATE CURRENT_DATE CURRENT_TIMESTAMP DATE DATE_ADD DATE_SUB DATE_FORMAT DATEDIFF DAY DAYOFMONTH DAYOFWEEK EXTRACT FLOOR FORMAT GREATEST GROUP_CONCAT IF IFNULL JSON_EXTRACT JSON_LENGTH JSON_UNQUOTE LEAST LEFT LENGTH LN LOG LOWER LPAD LTRIM MAX MD5 MIN MOD MONTH NOW NULLIF POWER RAND REPLACE REVERSE RIGHT ROUND ROW_NUMBER RANK DENSE_RANK LAG LEAD FIRST_VALUE LAST_VALUE LPAD RPAD RTRIM SIGN SIN SQRT STDDEV STDDEV_POP STDDEV_SAMP SUBSTR SUBSTRING SUM TAN TO_CHAR TO_DATE TO_TIMESTAMP TRIM TRUNC UPPER VAR_POP VAR_SAMP VERSION WEEK YEAR DATABASE CURRENT_DATABASE CURRENT_SCHEMA ARRAY_AGG STRING_AGG JSON_AGG JSON_BUILD_OBJECT PG_GET_VIEWDEF'.split(' '));
43
+ const SQL_PARENS = new Set('IN AS EXISTS OVER PARTITION VALUES SELECT WITH AND OR NOT ON USING FILTER GROUPING ROLLUP CUBE DISTINCT EXPLAIN'.split(' '));
44
+ const UNSAFE_FUNCTIONS = /\b(pg_sleep|sleep|benchmark|dblink\w*|lo_\w+|pg_(?:read|write|ls|stat|terminate|cancel|reload|rotate|log|create|drop|promote|switch|backup|replication|advisory)\w*|get_lock|release_lock|load_file|nextval|setval|set_config|http\w*|s3|hdfs|jdbc|mysql|postgresql|file|url)\s*\(/i;
45
+ function splitWorkbenchSql(source) {
46
+ if (!source.trim() || source.length > 100000 || source.includes('\0')) throw new Error('invalid_sql');
47
+ const statements = [];
48
+ let start = 0, index = 0, clean = '';
49
+ const finish = (end)=>{
50
+ const text = clean.trim();
51
+ if (text) {
52
+ var _text_toUpperCase_match;
53
+ const words = (_text_toUpperCase_match = text.toUpperCase().match(/[A-Z_][A-Z_0-9]*/g)) != null ? _text_toUpperCase_match : [];
54
+ const first = words[0];
55
+ let effect = 'unsupported';
56
+ if (WRITE.has(first)) effect = 'write';
57
+ if (first === 'SELECT' || first === 'WITH' || first === 'SHOW' || first === 'DESC' || first === 'DESCRIBE' || first === 'EXPLAIN') {
58
+ effect = 'read';
59
+ if (words.some((word, index)=>WRITE.has(word) && !(first === 'SHOW' && index === 1 && word === 'CREATE') || FORBIDDEN.has(word)) || UNSAFE_FUNCTIONS.test(text) || /\bFOR\s+(UPDATE|SHARE)\b/i.test(text) || /:=|\bEXPLAIN\s+ANALYZE\b|@/i.test(text)) effect = 'unsupported';
60
+ // eslint-disable-next-line no-control-regex -- intentional: reject any non-ASCII/control input
61
+ if (/[^\x00-\x7f]/.test(text) || /\.\s*[A-Z_][A-Z_0-9]*\s*\(/i.test(text)) effect = 'unsupported';
62
+ if ([
63
+ ...text.matchAll(/([A-Z_][A-Z_0-9]*)\s*\(/gi)
64
+ ].some((match)=>!SAFE_FUNCTIONS.has(match[1].toUpperCase()) && !SQL_PARENS.has(match[1].toUpperCase()))) effect = 'unsupported';
65
+ if (first === 'SHOW' && !/^(SHOW\s+(DATABASES|SCHEMAS|CATALOGS|TABLES|FULL\s+TABLES|COLUMNS|FULL\s+COLUMNS|INDEX|INDEXES|KEYS|CREATE\s+TABLE|CREATE\s+VIEW|PARTITIONS|VARIABLES|STATUS|GRANTS))\b/i.test(text)) effect = 'unsupported';
66
+ }
67
+ // CREATE/ALTER routines, external resources and privileged SQL are outside this app.
68
+ if (effect === 'write' && /\b(FUNCTION|PROCEDURE|TRIGGER|USER|ROLE|CATALOG|RESOURCE|SYSTEM|SERVER|EXTENSION|FILE|OUTFILE|DUMPFILE|EXECUTE)\b/i.test(text)) effect = 'unsupported';
69
+ statements.push({
70
+ sql: source.slice(start, end).trim(),
71
+ words,
72
+ effect
73
+ });
74
+ }
75
+ clean = '';
76
+ start = end + 1;
77
+ };
78
+ while(index < source.length){
79
+ const c = source[index], next = source[index + 1];
80
+ if (c === '-' && next === '-' || c === '#') {
81
+ while(index < source.length && source[index] !== '\n')index++;
82
+ clean += ' ';
83
+ continue;
84
+ }
85
+ if (c === '/' && next === '*') {
86
+ if (source[index + 2] === '!') throw new Error('executable_comment_not_supported');
87
+ const end = source.indexOf('*/', index + 2);
88
+ if (end < 0) throw new Error('unterminated_comment');
89
+ index = end + 2;
90
+ clean += ' ';
91
+ continue;
92
+ }
93
+ if (c === "'" || c === '"' || c === '`') {
94
+ const quote = c;
95
+ let closed = false;
96
+ index++;
97
+ while(index < source.length){
98
+ if (source[index] === '\\') throw new Error('use_parameters_for_backslash_literals');
99
+ if (source[index] === quote) {
100
+ if (source[index + 1] === quote) {
101
+ index += 2;
102
+ continue;
103
+ }
104
+ index++;
105
+ closed = true;
106
+ break;
107
+ }
108
+ index++;
109
+ }
110
+ if (!closed) throw new Error('unterminated_literal');
111
+ clean += quote === "'" ? ' ? ' : ' identifier ';
112
+ continue;
113
+ }
114
+ if (c === '$') {
115
+ var _source_slice_match;
116
+ const tag = (_source_slice_match = source.slice(index).match(/^\$(?:[A-Za-z_][A-Za-z_0-9]*)?\$/)) == null ? void 0 : _source_slice_match[0];
117
+ if (tag) {
118
+ const end = source.indexOf(tag, index + tag.length);
119
+ if (end < 0) throw new Error('unterminated_literal');
120
+ index = end + tag.length;
121
+ clean += ' ? ';
122
+ continue;
123
+ }
124
+ }
125
+ if (c === ';') {
126
+ finish(index);
127
+ index++;
128
+ continue;
129
+ }
130
+ clean += c;
131
+ index++;
132
+ }
133
+ finish(source.length);
134
+ if (!statements.length || statements.length > 50) throw new Error('statement_count_exceeded');
135
+ return statements;
136
+ }
137
+ function requireReadStatement(sql) {
138
+ const statements = splitWorkbenchSql(sql);
139
+ if (statements.length !== 1 || statements[0].effect !== 'read') throw new Error('read_only_violation');
140
+ return statements[0].sql;
141
+ }
142
+ function quoteDatabaseIdentifier(name, engine) {
143
+ // eslint-disable-next-line no-control-regex -- intentional: reject control characters in identifiers
144
+ if (!name || name.length > 256 || /[\0-\x1f]/.test(name)) throw new Error('invalid_identifier');
145
+ const quote = engine === 'postgres' ? '"' : '`';
146
+ return quote + name.split(quote).join(quote + quote) + quote;
147
+ }
148
+
149
+ const number = (value)=>Number(value != null ? value : 0);
150
+ const string = (value)=>value == null ? '' : String(value);
151
+ const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.name.toLowerCase() === name.toLowerCase())];
152
+ /** One adapter per physical connection. A host session must not share it across users. */ class SqlDatabaseWorkbenchAdapter {
153
+ quote(name) {
154
+ return quoteDatabaseIdentifier(name, this.engine);
155
+ }
156
+ ref(object) {
157
+ this.assertLocation(object);
158
+ var _object_schema, _ref, _object_database;
159
+ const namespace = this.engine === 'postgres' ? (_ref = (_object_schema = object.schema) != null ? _object_schema : this.initial.schema) != null ? _ref : 'public' : (_object_database = object.database) != null ? _object_database : this.initial.database;
160
+ if (!namespace) throw new Error('database_required');
161
+ return `${this.quote(namespace)}.${this.quote(object.name)}`;
162
+ }
163
+ assertLocation(location) {
164
+ if (location.engineCatalog && location.engineCatalog !== 'internal') throw new Error('external_catalog_not_supported');
165
+ if (this.engine === 'postgres' && location.database && location.database !== this.initial.database) throw new Error('database_requires_new_connection');
166
+ }
167
+ async raw(sql, parameters = [], maxRows = 1001, signal, timeoutMs = 30000) {
168
+ if (this.closed) throw new Error('session_closed');
169
+ return this.transport.execute(sql, parameters, {
170
+ maxRows,
171
+ timeoutMs,
172
+ signal
173
+ });
174
+ }
175
+ async capabilities() {
176
+ if (!this.version) {
177
+ var _result_rows_;
178
+ const result = await this.raw(this.engine === 'doris' ? 'SELECT @@version_comment AS version' : 'SELECT version() AS version', [], 1);
179
+ this.version = string((_result_rows_ = result.rows[0]) == null ? void 0 : _result_rows_[0]);
180
+ }
181
+ const supported = this.engine !== 'doris' || /(?:^|[^0-9])(?:2\.1|3\.\d+|4\.\d+)(?:[.\s_-]|$)/.test(this.version);
182
+ return {
183
+ engine: this.engine,
184
+ version: this.version,
185
+ query: true,
186
+ explain: true,
187
+ transactions: this.engine !== 'doris',
188
+ cancel: true,
189
+ import: supported,
190
+ writes: supported,
191
+ nativeReadOnly: this.engine !== 'doris',
192
+ objectKinds: [
193
+ 'table',
194
+ 'view'
195
+ ],
196
+ diagnostics: this.engine === 'doris' ? [
197
+ ...supported ? [] : [
198
+ 'doris_version_unverified_writes_disabled'
199
+ ],
200
+ 'doris_permissions_enforce_read_only',
201
+ 'external_catalog_not_supported',
202
+ 'doris_transactions_not_exposed'
203
+ ] : []
204
+ };
205
+ }
206
+ async locations() {
207
+ const result = await this.raw(this.engine === 'postgres' ? "SELECT schema_name AS name FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg_%' AND schema_name <> 'information_schema' ORDER BY schema_name LIMIT 1000" : "SELECT schema_name AS name FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','performance_schema','mysql','sys') ORDER BY schema_name LIMIT 1000");
208
+ return result.rows.map((row)=>this.engine === 'postgres' ? {
209
+ database: this.initial.database,
210
+ schema: string(row[0])
211
+ } : _extends({
212
+ database: string(row[0])
213
+ }, this.engine === 'doris' ? {
214
+ engineCatalog: 'internal'
215
+ } : {}));
216
+ }
217
+ async objects(input) {
218
+ this.assertLocation(input);
219
+ var _input_page, _input_pageSize;
220
+ const page = Math.max(1, Math.min((_input_page = input.page) != null ? _input_page : 1, 10000)), pageSize = Math.max(1, Math.min((_input_pageSize = input.pageSize) != null ? _input_pageSize : 100, 1000));
221
+ var _input_schema, _ref, _input_database;
222
+ const namespace = this.engine === 'postgres' ? (_ref = (_input_schema = input.schema) != null ? _input_schema : this.initial.schema) != null ? _ref : 'public' : (_input_database = input.database) != null ? _input_database : this.initial.database;
223
+ if (!namespace) return {
224
+ items: [],
225
+ page,
226
+ pageSize,
227
+ hasMore: false
228
+ };
229
+ const placeholder = (i)=>this.engine === 'postgres' ? `$${i}` : '?';
230
+ var _input_search;
231
+ const result = await this.raw(`SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = ${placeholder(1)} AND table_name LIKE ${placeholder(2)} ORDER BY table_name LIMIT ${pageSize + 1} OFFSET ${(page - 1) * pageSize}`, [
232
+ namespace,
233
+ `%${(_input_search = input.search) != null ? _input_search : ''}%`
234
+ ], pageSize + 1);
235
+ return {
236
+ items: result.rows.slice(0, pageSize).map((row)=>({
237
+ database: input.database,
238
+ schema: input.schema,
239
+ engineCatalog: input.engineCatalog,
240
+ name: string(row[0]),
241
+ kind: string(row[1]).includes('VIEW') ? 'view' : 'table'
242
+ })),
243
+ page,
244
+ pageSize,
245
+ hasMore: result.rows.length > pageSize
246
+ };
247
+ }
248
+ async describe(object) {
249
+ const reference = this.ref(object);
250
+ var _object_schema, _ref, _object_database, _ref1;
251
+ const namespace = this.engine === 'postgres' ? (_ref = (_object_schema = object.schema) != null ? _object_schema : this.initial.schema) != null ? _ref : 'public' : (_ref1 = (_object_database = object.database) != null ? _object_database : this.initial.database) != null ? _ref1 : '';
252
+ const p = (i)=>this.engine === 'postgres' ? `$${i}` : '?';
253
+ const metadata = await this.raw(this.engine === 'postgres' ? `SELECT a.attname AS column_name, pg_catalog.format_type(a.atttypid,a.atttypmod) AS data_type, CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS is_nullable, a.attnum AS ordinal_position, pg_get_expr(d.adbin,d.adrelid) AS column_default FROM pg_catalog.pg_attribute a JOIN pg_catalog.pg_class c ON c.oid=a.attrelid JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid=a.attrelid AND d.adnum=a.attnum WHERE n.nspname=$1 AND c.relname=$2 AND a.attnum>0 AND NOT a.attisdropped ORDER BY a.attnum LIMIT 1000` : `SELECT column_name, column_type AS data_type, is_nullable, ordinal_position, column_default FROM information_schema.columns WHERE table_schema = ${p(1)} AND table_name = ${p(2)} ORDER BY ordinal_position LIMIT 1000`, [
254
+ namespace,
255
+ object.name
256
+ ]);
257
+ const columns = metadata.rows.map((row, index)=>({
258
+ id: `c${index}`,
259
+ name: string(cell(metadata, row, 'column_name')),
260
+ dataType: string(cell(metadata, row, 'data_type')),
261
+ nullable: string(cell(metadata, row, 'is_nullable')) === 'YES',
262
+ ordinal: number(cell(metadata, row, 'ordinal_position')),
263
+ defaultValue: cell(metadata, row, 'column_default') == null ? null : string(cell(metadata, row, 'column_default'))
264
+ }));
265
+ if (!columns.length) throw new Error('object_not_found_or_denied');
266
+ const keys = [], diagnostics = [];
267
+ let definition = '', model;
268
+ if (this.engine === 'postgres') {
269
+ const result = await this.raw(`SELECT tc.constraint_name, tc.constraint_type, kcu.column_name, kcu.ordinal_position, ccu.table_schema AS foreign_schema, ccu.table_name AS foreign_table, ccu.column_name AS foreign_column FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog=kcu.constraint_catalog AND tc.constraint_schema=kcu.constraint_schema AND tc.constraint_name=kcu.constraint_name LEFT JOIN information_schema.referential_constraints rc ON rc.constraint_catalog=tc.constraint_catalog AND rc.constraint_schema=tc.constraint_schema AND rc.constraint_name=tc.constraint_name LEFT JOIN information_schema.key_column_usage ccu ON ccu.constraint_catalog=rc.unique_constraint_catalog AND ccu.constraint_schema=rc.unique_constraint_schema AND ccu.constraint_name=rc.unique_constraint_name AND ccu.ordinal_position=kcu.position_in_unique_constraint WHERE tc.table_schema=$1 AND tc.table_name=$2 ORDER BY tc.constraint_name,kcu.ordinal_position LIMIT 1000`, [
270
+ namespace,
271
+ object.name
272
+ ]);
273
+ for (const row of result.rows){
274
+ const name = string(cell(result, row, 'constraint_name')), kind = string(cell(result, row, 'constraint_type'));
275
+ let key = keys.find((item)=>item.name === name);
276
+ if (!key) {
277
+ key = {
278
+ name,
279
+ kind: kind === 'PRIMARY KEY' ? 'primary' : kind === 'FOREIGN KEY' ? 'foreign' : 'unique',
280
+ columns: []
281
+ };
282
+ keys.push(key);
283
+ }
284
+ key.columns.push(string(cell(result, row, 'column_name')));
285
+ if (key.kind === 'foreign') {
286
+ var _key;
287
+ key.referencedTable = {
288
+ schema: string(cell(result, row, 'foreign_schema')),
289
+ name: string(cell(result, row, 'foreign_table')),
290
+ kind: 'table'
291
+ };
292
+ var _referencedColumns;
293
+ ((_referencedColumns = (_key = key).referencedColumns) != null ? _referencedColumns : _key.referencedColumns = []).push(string(cell(result, row, 'foreign_column')));
294
+ }
295
+ }
296
+ if (object.kind === 'view') {
297
+ var _source_rows_;
298
+ const source = await this.raw('SELECT pg_get_viewdef($1::regclass, true) AS definition', [
299
+ reference
300
+ ], 1);
301
+ definition = `CREATE VIEW ${reference} AS\n${string((_source_rows_ = source.rows[0]) == null ? void 0 : _source_rows_[0])}`;
302
+ } else {
303
+ definition = `CREATE TABLE ${reference} (\n${columns.map((column)=>` ${this.quote(column.name)} ${column.dataType}${column.nullable ? '' : ' NOT NULL'}${column.defaultValue === null ? '' : ` DEFAULT ${column.defaultValue}`}`).join(',\n')}\n);`;
304
+ diagnostics.push('definition_reconstructed_from_metadata');
305
+ }
306
+ } else {
307
+ var _source_rows_1;
308
+ const source = await this.raw(`SHOW CREATE ${object.kind === 'view' ? 'VIEW' : 'TABLE'} ${reference}`, [], 1);
309
+ definition = string((_source_rows_1 = source.rows[0]) == null ? void 0 : _source_rows_1[1]);
310
+ if (this.engine === 'doris') {
311
+ const match = definition.match(/\b(UNIQUE|DUPLICATE|AGGREGATE)\s+KEY\s*\(([^)]*)\)/i);
312
+ model = (match == null ? void 0 : match[1].toUpperCase()) === 'UNIQUE' ? 'unique' : (match == null ? void 0 : match[1].toUpperCase()) === 'DUPLICATE' ? 'duplicate' : (match == null ? void 0 : match[1].toUpperCase()) === 'AGGREGATE' ? 'aggregate' : undefined;
313
+ if (model === 'unique' && match) keys.push({
314
+ name: 'UNIQUE KEY',
315
+ kind: 'primary',
316
+ columns: [
317
+ ...match[2].matchAll(/`((?:``|[^`])+)`/g)
318
+ ].map((item)=>item[1].split('``').join('`'))
319
+ });
320
+ } else {
321
+ const index = await this.raw(`SHOW INDEX FROM ${reference}`);
322
+ for (const row of index.rows){
323
+ const name = string(cell(index, row, 'Key_name'));
324
+ let key = keys.find((item)=>item.name === name);
325
+ if (!key) {
326
+ key = {
327
+ name,
328
+ kind: name === 'PRIMARY' ? 'primary' : number(cell(index, row, 'Non_unique')) === 0 ? 'unique' : 'index',
329
+ columns: []
330
+ };
331
+ keys.push(key);
332
+ }
333
+ key.columns.push(string(cell(index, row, 'Column_name')));
334
+ }
335
+ const foreign = await this.raw('SELECT constraint_name,column_name,referenced_table_schema,referenced_table_name,referenced_column_name FROM information_schema.key_column_usage WHERE table_schema=? AND table_name=? AND referenced_table_name IS NOT NULL ORDER BY constraint_name,ordinal_position LIMIT 1000', [
336
+ namespace,
337
+ object.name
338
+ ]);
339
+ for (const row of foreign.rows){
340
+ var _key_referencedColumns;
341
+ let key = keys.find((item)=>item.name === string(row[0]));
342
+ if (!key) {
343
+ key = {
344
+ name: string(row[0]),
345
+ kind: 'foreign',
346
+ columns: [],
347
+ referencedTable: {
348
+ database: string(row[2]),
349
+ name: string(row[3]),
350
+ kind: 'table'
351
+ },
352
+ referencedColumns: []
353
+ };
354
+ keys.push(key);
355
+ }
356
+ key.columns.push(string(row[1]));
357
+ (_key_referencedColumns = key.referencedColumns) == null ? void 0 : _key_referencedColumns.push(string(row[4]));
358
+ }
359
+ }
360
+ }
361
+ const editable = (this.engine !== 'doris' || (await this.capabilities()).writes) && object.kind === 'table' && keys.some((key)=>(key.kind === 'primary' || key.kind === 'unique') && key.columns.length > 0 && key.columns.every((name)=>{
362
+ var _columns_find;
363
+ return ((_columns_find = columns.find((column)=>column.name === name)) == null ? void 0 : _columns_find.nullable) === false;
364
+ }));
365
+ return {
366
+ object,
367
+ columns,
368
+ keys,
369
+ definition,
370
+ model,
371
+ editable,
372
+ diagnostics
373
+ };
374
+ }
375
+ async query(input, signal) {
376
+ this.assertLocation(input);
377
+ if (input.database && input.database !== this.initial.database) throw new Error('database_requires_new_connection');
378
+ if (input.schema && input.schema !== this.initial.schema) throw new Error('schema_requires_new_connection');
379
+ if (this.busy) throw new Error('session_busy');
380
+ if (input.mode === 'read') {
381
+ requireReadStatement(input.sql);
382
+ }
383
+ const statements = splitWorkbenchSql(input.sql);
384
+ if (statements.length !== 1 || statements[0].effect === 'unsupported') throw new Error('unsupported_statement');
385
+ var _input_limit, _input_offset;
386
+ const limit = Math.max(1, Math.min((_input_limit = input.limit) != null ? _input_limit : 100, 1000)), offset = Math.max(0, Math.min((_input_offset = input.offset) != null ? _input_offset : 0, 100000));
387
+ if (input.mode === 'write' && !(await this.capabilities()).writes) throw new Error('engine_version_writes_unavailable');
388
+ if (input.mode === 'write' && offset) throw new Error('mutation_cannot_be_paginated');
389
+ let sql = statements[0].sql;
390
+ // Wrap a SELECT instead of appending LIMIT after an existing LIMIT or trailing comment.
391
+ if (input.mode === 'read' && [
392
+ 'SELECT',
393
+ 'WITH'
394
+ ].includes(statements[0].words[0])) sql = `SELECT * FROM (\n${sql}\n) AS ${this.quote('_db_studio_page')} LIMIT ${limit + 1} OFFSET ${offset}`;
395
+ else if (offset) throw new Error('statement_cannot_be_paginated');
396
+ this.busy = true;
397
+ let readTransaction = false;
398
+ try {
399
+ if (input.mode === 'read' && this.engine !== 'doris' && !this.transactionOpen) {
400
+ await this.raw(this.engine === 'postgres' ? 'BEGIN READ ONLY' : 'START TRANSACTION READ ONLY', [], 1);
401
+ readTransaction = true;
402
+ }
403
+ var _input_timeoutMs;
404
+ const result = await this.raw(sql, input.parameters, limit + 1, signal, Math.max(1, Math.min((_input_timeoutMs = input.timeoutMs) != null ? _input_timeoutMs : 30000, 120000)));
405
+ const hasMore = result.rows.length > limit || result.truncated;
406
+ return _extends({}, result, {
407
+ rows: result.rows.slice(0, limit),
408
+ hasMore,
409
+ truncated: result.truncated
410
+ });
411
+ } finally{
412
+ try {
413
+ if (readTransaction) {
414
+ try {
415
+ await this.raw('ROLLBACK', [], 1);
416
+ } catch (e) {
417
+ await this.close();
418
+ }
419
+ }
420
+ } finally{
421
+ this.busy = false;
422
+ }
423
+ }
424
+ }
425
+ async explain(input, signal) {
426
+ const sql = requireReadStatement(input.sql);
427
+ if (!/^(SELECT|WITH)\b/i.test(sql)) throw new Error('explain_requires_select');
428
+ const prefix = this.engine === 'postgres' ? 'EXPLAIN (FORMAT JSON) ' : this.engine === 'mysql' ? 'EXPLAIN FORMAT=JSON ' : 'EXPLAIN ';
429
+ return this.query(_extends({}, input, {
430
+ sql: prefix + sql,
431
+ mode: 'read'
432
+ }), signal);
433
+ }
434
+ async transaction(action) {
435
+ if (this.engine === 'doris') throw new Error('transactions_not_supported');
436
+ if (this.busy) throw new Error('session_busy');
437
+ if (action === 'begin' && this.transactionOpen) throw new Error('transaction_already_open');
438
+ if (action !== 'begin' && !this.transactionOpen) throw new Error('transaction_not_open');
439
+ this.busy = true;
440
+ try {
441
+ await this.raw(action.toUpperCase(), [], 1);
442
+ this.transactionOpen = action === 'begin';
443
+ } finally{
444
+ this.busy = false;
445
+ }
446
+ }
447
+ async importRows(input, signal) {
448
+ this.assertLocation(input);
449
+ if (this.busy) throw new Error('session_busy');
450
+ if (!(await this.capabilities()).import) throw new Error('engine_version_import_unavailable');
451
+ if (new Set(input.columns).size !== input.columns.length) throw new Error('duplicate_import_columns');
452
+ if (!input.rows.length || input.rows.length > 10000 || !input.columns.length || input.columns.length > 500 || input.rows.some((row)=>row.length !== input.columns.length)) throw new Error('invalid_import_shape');
453
+ if (this.transport.importRows) {
454
+ this.busy = true;
455
+ try {
456
+ return await this.transport.importRows(input, signal);
457
+ } finally{
458
+ this.busy = false;
459
+ }
460
+ }
461
+ if (this.engine === 'doris') throw new Error('stream_load_required');
462
+ const reference = this.ref(_extends({}, input, {
463
+ name: input.table,
464
+ kind: 'table'
465
+ }));
466
+ await this.transaction('begin');
467
+ try {
468
+ for(let i = 0; i < input.rows.length; i += 100){
469
+ const batch = input.rows.slice(i, i + 100), values = batch.flat();
470
+ let n = 0;
471
+ const placeholders = batch.map((row)=>`(${row.map(()=>this.engine === 'postgres' ? `$${++n}` : '?').join(',')})`).join(',');
472
+ await this.query({
473
+ sql: `INSERT INTO ${reference} (${input.columns.map((name)=>this.quote(name)).join(',')}) VALUES ${placeholders}`,
474
+ parameters: values,
475
+ mode: 'write'
476
+ }, signal);
477
+ }
478
+ await this.transaction('commit');
479
+ return {
480
+ outcome: 'succeeded',
481
+ loadedRows: input.rows.length,
482
+ filteredRows: 0,
483
+ label: input.operationId,
484
+ diagnostics: []
485
+ };
486
+ } catch (error) {
487
+ try {
488
+ await this.transaction('rollback');
489
+ } catch (e) {
490
+ await this.close();
491
+ }
492
+ throw error;
493
+ }
494
+ }
495
+ async cancel() {
496
+ await this.close();
497
+ }
498
+ async close() {
499
+ this.closed = true;
500
+ await this.transport.close();
501
+ }
502
+ constructor(engine, transport, initial = {}){
503
+ this.transactionOpen = false;
504
+ this.busy = false;
505
+ this.closed = false;
506
+ this.engine = engine;
507
+ this.transport = transport;
508
+ this.initial = initial;
509
+ }
510
+ }
511
+
512
+ export { DataSourceRuntimeCapability, SqlDatabaseWorkbenchAdapter, quoteDatabaseIdentifier, requireReadStatement, splitWorkbenchSql };