@xpert-ai/plugin-sdk 3.18.5 → 3.18.7

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 (37) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +44 -0
  3. package/data-workbench.cjs.default.js +1 -0
  4. package/data-workbench.cjs.js +669 -0
  5. package/data-workbench.cjs.mjs +2 -0
  6. package/data-workbench.d.ts +1 -0
  7. package/data-workbench.esm.js +663 -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/core/runtime-capability.d.ts +4 -0
  15. package/src/lib/core/strategy-bus.d.ts +3 -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-pagination.d.ts +6 -0
  20. package/src/lib/data/datasource/workbench-sql-lexer.d.ts +11 -0
  21. package/src/lib/data/datasource/workbench-sql-syntax.d.ts +3 -0
  22. package/src/lib/data/datasource/workbench-sql.d.ts +12 -0
  23. package/src/lib/data/datasource/workbench.d.ts +133 -0
  24. package/src/lib/rag/analyzer/index.d.ts +3 -0
  25. package/src/lib/rag/analyzer/strategy.decorator.d.ts +2 -0
  26. package/src/lib/rag/analyzer/strategy.interface.d.ts +11 -0
  27. package/src/lib/rag/analyzer/strategy.registry.d.ts +8 -0
  28. package/src/lib/rag/index.d.ts +1 -0
  29. package/src/lib/runtime/capabilities/index.d.ts +1 -0
  30. package/src/lib/runtime/capabilities/knowledge-graph.d.ts +59 -0
  31. package/src/lib/tool-provider/component.d.ts +2 -0
  32. package/src/lib/tool-provider/decorators.d.ts +1 -1
  33. package/src/lib/tool-provider/index.d.ts +3 -0
  34. package/src/lib/tool-provider/mcp-methods.d.ts +39 -0
  35. package/src/lib/tool-provider/provider-methods.d.ts +5 -0
  36. package/src/lib/tool-provider/tool-result.d.ts +1 -1
  37. package/src/lib/tool-provider/types.d.ts +12 -1
@@ -0,0 +1,669 @@
1
+ 'use strict';
2
+
3
+ var _extends = require('./_extends.cjs.js');
4
+
5
+ const DataSourceRuntimeCapability = Object.freeze({
6
+ id: 'platform.datasource.workbench',
7
+ description: 'Scoped database workbench access; credentials remain host-owned.',
8
+ __type: undefined
9
+ });
10
+
11
+ /** Mask comments and quoted content without changing source offsets. Shared by guards and pagination. */ function lexWorkbenchSql(source) {
12
+ if (!source.trim() || source.length > 100000 || source.includes('\0')) throw new Error('invalid_sql');
13
+ const masked = source.split('');
14
+ const quoted = new Map();
15
+ const hide = (start, end, kind)=>{
16
+ for(let i = start; i < end; i++)if (masked[i] !== '\n') masked[i] = ' ';
17
+ if (kind) {
18
+ const marker = start + 1;
19
+ quoted.set(marker, kind);
20
+ masked[marker] = kind === 'literal' ? '?' : 'i';
21
+ }
22
+ };
23
+ let index = 0;
24
+ while(index < source.length){
25
+ const start = index, c = source[index], next = source[index + 1];
26
+ if (c === '-' && next === '-' || c === '#') {
27
+ while(index < source.length && source[index] !== '\n')index++;
28
+ hide(start, index);
29
+ continue;
30
+ }
31
+ if (c === '/' && next === '*') {
32
+ if (source[index + 2] === '!') throw new Error('executable_comment_not_supported');
33
+ const end = source.indexOf('*/', index + 2);
34
+ if (end < 0) throw new Error('unterminated_comment');
35
+ index = end + 2;
36
+ hide(start, index);
37
+ continue;
38
+ }
39
+ if (c === "'" || c === '"' || c === '`') {
40
+ let closed = false;
41
+ index++;
42
+ while(index < source.length){
43
+ if (source[index] === '\\') throw new Error('use_parameters_for_backslash_literals');
44
+ if (source[index] === c) {
45
+ if (source[index + 1] === c) {
46
+ index += 2;
47
+ continue;
48
+ }
49
+ index++;
50
+ closed = true;
51
+ break;
52
+ }
53
+ index++;
54
+ }
55
+ if (!closed) throw new Error('unterminated_literal');
56
+ hide(start, index, c === "'" ? 'literal' : 'identifier');
57
+ continue;
58
+ }
59
+ if (c === '$') {
60
+ var _source_slice_match;
61
+ const tag = (_source_slice_match = source.slice(index).match(/^\$(?:[A-Za-z_][A-Za-z_0-9]*)?\$/)) == null ? void 0 : _source_slice_match[0];
62
+ if (tag) {
63
+ const end = source.indexOf(tag, index + tag.length);
64
+ if (end < 0) throw new Error('unterminated_literal');
65
+ index = end + tag.length;
66
+ hide(start, index, 'literal');
67
+ continue;
68
+ }
69
+ }
70
+ index++;
71
+ }
72
+ const text = masked.join('');
73
+ const tokens = [
74
+ ...text.matchAll(/[A-Z_][A-Z_0-9]*|[0-9]+|[^\s]/gi)
75
+ ].map((match)=>{
76
+ var _quoted_get;
77
+ return {
78
+ text: match[0].toUpperCase(),
79
+ start: match.index,
80
+ end: match.index + match[0].length,
81
+ kind: (_quoted_get = quoted.get(match.index)) != null ? _quoted_get : /^[A-Z_]/i.test(match[0]) ? 'word' : /^[0-9]/.test(match[0]) ? 'number' : 'symbol'
82
+ };
83
+ });
84
+ return {
85
+ masked: text,
86
+ tokens
87
+ };
88
+ }
89
+
90
+ const MODIFIED_TYPES = new Set('DECIMAL NUMERIC CHAR VARCHAR NCHAR NVARCHAR BINARY VARBINARY DATETIME TIME TIMESTAMP'.split(' '));
91
+ /** Positions where name(...) declares columns or a built-in type, rather than invoking a function. */ function sqlDeclarationPositions(tokens) {
92
+ const declarations = new Set();
93
+ const closing = new Map(), parents = new Map();
94
+ const stack = [];
95
+ tokens.forEach((token, index)=>{
96
+ if (stack.length) parents.set(index, stack[stack.length - 1]);
97
+ if (token.text === '(') stack.push(index);
98
+ if (token.text === ')') {
99
+ const open = stack.pop();
100
+ if (open !== undefined) closing.set(open, index);
101
+ }
102
+ });
103
+ const name = (index)=>{
104
+ var _tokens_index;
105
+ return [
106
+ 'word',
107
+ 'identifier'
108
+ ].includes((_tokens_index = tokens[index]) == null ? void 0 : _tokens_index.kind);
109
+ };
110
+ const is = (index, word)=>{
111
+ var _tokens_index;
112
+ return ((_tokens_index = tokens[index]) == null ? void 0 : _tokens_index.text) === word;
113
+ };
114
+ for(let i = 0; i < tokens.length; i++){
115
+ if (tokens[i].kind !== 'word' || !is(i, 'WITH')) continue;
116
+ let cursor = i + 1;
117
+ if (is(cursor, 'RECURSIVE')) cursor++;
118
+ while(name(cursor)){
119
+ const declaration = cursor++;
120
+ let columns = false;
121
+ if (is(cursor, '(')) {
122
+ const end = closing.get(cursor);
123
+ if (end === undefined || end === cursor + 1) break;
124
+ const columnTokens = tokens.slice(cursor + 1, end);
125
+ if (columnTokens.length % 2 === 0 || !columnTokens.every((token, n)=>n % 2 === 0 ? [
126
+ 'word',
127
+ 'identifier'
128
+ ].includes(token.kind) : token.text === ',')) break;
129
+ columns = true;
130
+ cursor = end + 1;
131
+ }
132
+ if (!is(cursor++, 'AS')) break;
133
+ if (is(cursor, 'NOT')) cursor++;
134
+ if (is(cursor, 'MATERIALIZED')) cursor++;
135
+ if (!is(cursor, '(')) break;
136
+ const end = closing.get(cursor);
137
+ if (end === undefined) break;
138
+ if (columns) declarations.add(tokens[declaration].start);
139
+ cursor = end + 1;
140
+ if (!is(cursor, ',')) break;
141
+ cursor++;
142
+ }
143
+ }
144
+ tokens.forEach((token, i)=>{
145
+ var _tokens_;
146
+ if (token.kind !== 'word' || !MODIFIED_TYPES.has(token.text) || !is(i + 1, '(')) return;
147
+ const end = closing.get(i + 1);
148
+ if (end === undefined) return;
149
+ const args = tokens.slice(i + 2, end);
150
+ if (![
151
+ 1,
152
+ 3
153
+ ].includes(args.length) || !args.every((arg, n)=>n % 2 ? arg.text === ',' : arg.kind === 'number')) return;
154
+ const parent = parents.get(i);
155
+ const owner = parent === undefined ? undefined : (_tokens_ = tokens[parent - 1]) == null ? void 0 : _tokens_.text;
156
+ const cast = owner === 'CAST' && is(i - 1, 'AS');
157
+ const convert = owner === 'CONVERT' && is(i - 1, ',');
158
+ const postgresCast = is(i - 1, ':') && is(i - 2, ':');
159
+ if (cast || convert || postgresCast) declarations.add(token.start);
160
+ });
161
+ return declarations;
162
+ }
163
+
164
+ const WRITE = new Set([
165
+ 'INSERT',
166
+ 'UPDATE',
167
+ 'DELETE',
168
+ 'CREATE',
169
+ 'ALTER',
170
+ 'DROP',
171
+ 'TRUNCATE',
172
+ 'RENAME'
173
+ ]);
174
+ const FORBIDDEN = new Set([
175
+ 'INTO',
176
+ 'OUTFILE',
177
+ 'DUMPFILE',
178
+ 'PROCEDURE',
179
+ 'CALL',
180
+ 'COPY',
181
+ 'LOAD',
182
+ 'EXPORT',
183
+ 'IMPORT',
184
+ 'GRANT',
185
+ 'REVOKE',
186
+ 'LOCK',
187
+ 'UNLOCK',
188
+ 'KILL',
189
+ 'SET',
190
+ 'RESET',
191
+ 'ANALYZE',
192
+ 'VACUUM',
193
+ 'OPTIMIZE',
194
+ 'ATTACH',
195
+ 'DETACH'
196
+ ]);
197
+ 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(' '));
198
+ // FROM/JOIN can introduce a parenthesized subquery, not a function call.
199
+ const SQL_PARENS = new Set('IN AS EXISTS OVER PARTITION VALUES SELECT WITH FROM JOIN AND OR NOT ON USING FILTER GROUPING ROLLUP CUBE DISTINCT EXPLAIN'.split(' '));
200
+ 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;
201
+ function splitWorkbenchSql(source) {
202
+ const { masked, tokens } = lexWorkbenchSql(source);
203
+ const statements = [];
204
+ let start = 0;
205
+ const finish = (end)=>{
206
+ const text = masked.slice(start, end);
207
+ if (text.trim()) {
208
+ var _text_toUpperCase_match;
209
+ const words = (_text_toUpperCase_match = text.toUpperCase().match(/[A-Z_][A-Z_0-9]*/g)) != null ? _text_toUpperCase_match : [];
210
+ const first = words[0];
211
+ let effect = 'unsupported';
212
+ if (WRITE.has(first)) effect = 'write';
213
+ if (first === 'SELECT' || first === 'WITH' || first === 'SHOW' || first === 'DESC' || first === 'DESCRIBE' || first === 'EXPLAIN') {
214
+ effect = 'read';
215
+ 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';
216
+ // eslint-disable-next-line no-control-regex -- intentional: reject any non-ASCII/control input
217
+ if (/[^\x00-\x7f]/.test(text) || /\.\s*[A-Z_][A-Z_0-9]*\s*\(/i.test(text)) effect = 'unsupported';
218
+ const declarations = sqlDeclarationPositions(tokens.filter((token)=>token.start >= start && token.end <= end));
219
+ if ([
220
+ ...text.matchAll(/([A-Z_][A-Z_0-9]*)\s*\(/gi)
221
+ ].some((match)=>!declarations.has(start + match.index) && !SAFE_FUNCTIONS.has(match[1].toUpperCase()) && !SQL_PARENS.has(match[1].toUpperCase()))) effect = 'unsupported';
222
+ 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.trim())) effect = 'unsupported';
223
+ }
224
+ // CREATE/ALTER routines, external resources and privileged SQL are outside this app.
225
+ if (effect === 'write' && /\b(FUNCTION|PROCEDURE|TRIGGER|USER|ROLE|CATALOG|RESOURCE|SYSTEM|SERVER|EXTENSION|FILE|OUTFILE|DUMPFILE|EXECUTE)\b/i.test(text)) effect = 'unsupported';
226
+ statements.push({
227
+ sql: source.slice(start, end).trim(),
228
+ words,
229
+ effect
230
+ });
231
+ }
232
+ start = end + 1;
233
+ };
234
+ for (const token of tokens){
235
+ if (token.kind === 'symbol' && token.text === ';') finish(token.start);
236
+ }
237
+ finish(source.length);
238
+ if (!statements.length || statements.length > 50) throw new Error('statement_count_exceeded');
239
+ return statements;
240
+ }
241
+ function requireReadStatement(sql) {
242
+ const statements = splitWorkbenchSql(sql);
243
+ if (statements.length !== 1 || statements[0].effect !== 'read') throw new Error('read_only_violation');
244
+ return statements[0].sql;
245
+ }
246
+ function quoteDatabaseIdentifier(name, engine) {
247
+ // eslint-disable-next-line no-control-regex -- intentional: reject control characters in identifiers
248
+ if (!name || name.length > 256 || /[\0-\x1f]/.test(name)) throw new Error('invalid_identifier');
249
+ const quote = engine === 'postgres' ? '"' : '`';
250
+ return quote + name.split(quote).join(quote + quote) + quote;
251
+ }
252
+
253
+ /** Apply a page to a MySQL result without turning its (possibly duplicate) output names into table columns. */ function paginateMysqlQuery(sql, parameters = [], limit, offset) {
254
+ const { tokens } = lexWorkbenchSql(sql);
255
+ const top = [];
256
+ let depth = 0;
257
+ for (const token of tokens){
258
+ if (token.text === '(') depth++;
259
+ if (!depth) top.push(token);
260
+ if (token.text === ')') depth--;
261
+ if (depth < 0) throw new Error('unsupported_pagination');
262
+ }
263
+ if (depth) throw new Error('unsupported_pagination');
264
+ const start = top.findIndex((token)=>token.kind === 'word' && token.text === 'LIMIT');
265
+ if (start < 0) return {
266
+ sql: `${sql}\nLIMIT ${limit + 1} OFFSET ${offset}`,
267
+ parameters
268
+ };
269
+ const clause = top.slice(start + 1);
270
+ const bindings = tokens.filter((token)=>token.text === '?' && token.kind === 'symbol');
271
+ const consumed = new Set();
272
+ const integer = (token)=>{
273
+ if ((token == null ? void 0 : token.kind) === 'number') return BigInt(token.text);
274
+ if ((token == null ? void 0 : token.kind) === 'symbol' && token.text === '?') {
275
+ const index = bindings.indexOf(token), value = parameters[index];
276
+ if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) {
277
+ consumed.add(index);
278
+ return BigInt(value);
279
+ }
280
+ }
281
+ throw new Error('unsupported_pagination');
282
+ };
283
+ let count, skip = 0n;
284
+ if (clause.length === 1) count = integer(clause[0]);
285
+ else if (clause.length === 3 && clause[1].text === ',') {
286
+ skip = integer(clause[0]);
287
+ count = integer(clause[2]);
288
+ } else if (clause.length === 3 && clause[1].kind === 'word' && clause[1].text === 'OFFSET') {
289
+ count = integer(clause[0]);
290
+ skip = integer(clause[2]);
291
+ } else throw new Error('unsupported_pagination');
292
+ const remaining = count > BigInt(offset) ? count - BigInt(offset) : 0n;
293
+ const size = remaining < BigInt(limit + 1) ? remaining : BigInt(limit + 1);
294
+ return {
295
+ sql: `${sql.slice(0, top[start].start)}LIMIT ${size} OFFSET ${skip + BigInt(offset)}${sql.slice(clause[clause.length - 1].end)}`,
296
+ parameters: parameters.filter((_, index)=>!consumed.has(index))
297
+ };
298
+ }
299
+
300
+ const number = (value)=>Number(value != null ? value : 0);
301
+ const string = (value)=>value == null ? '' : String(value);
302
+ const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.name.toLowerCase() === name.toLowerCase())];
303
+ /** One adapter per physical connection. A host session must not share it across users. */ class SqlDatabaseWorkbenchAdapter {
304
+ quote(name) {
305
+ return quoteDatabaseIdentifier(name, this.engine);
306
+ }
307
+ ref(object) {
308
+ this.assertLocation(object);
309
+ var _object_schema, _ref, _object_database;
310
+ 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;
311
+ if (!namespace) throw new Error('database_required');
312
+ return `${this.quote(namespace)}.${this.quote(object.name)}`;
313
+ }
314
+ assertLocation(location) {
315
+ if (location.engineCatalog && location.engineCatalog !== 'internal') throw new Error('external_catalog_not_supported');
316
+ if (this.engine === 'postgres' && location.database && location.database !== this.initial.database) throw new Error('database_requires_new_connection');
317
+ }
318
+ async raw(sql, parameters = [], maxRows = 1001, signal, timeoutMs = 30000) {
319
+ if (this.closed) throw new Error('session_closed');
320
+ return this.transport.execute(sql, parameters, {
321
+ maxRows,
322
+ timeoutMs,
323
+ signal
324
+ });
325
+ }
326
+ async capabilities() {
327
+ if (!this.version) {
328
+ var _result_rows_;
329
+ const result = await this.raw(this.engine === 'doris' ? 'SELECT @@version_comment AS version' : 'SELECT version() AS version', [], 1);
330
+ this.version = string((_result_rows_ = result.rows[0]) == null ? void 0 : _result_rows_[0]);
331
+ }
332
+ const supported = this.engine !== 'doris' || /(?:^|[^0-9])(?:2\.1|3\.\d+|4\.\d+)(?:[.\s_-]|$)/.test(this.version);
333
+ return {
334
+ engine: this.engine,
335
+ version: this.version,
336
+ query: true,
337
+ explain: true,
338
+ transactions: this.engine !== 'doris',
339
+ cancel: true,
340
+ import: supported,
341
+ writes: supported,
342
+ nativeReadOnly: this.engine !== 'doris',
343
+ objectKinds: [
344
+ 'table',
345
+ 'view'
346
+ ],
347
+ diagnostics: this.engine === 'doris' ? [
348
+ ...supported ? [] : [
349
+ 'doris_version_unverified_writes_disabled'
350
+ ],
351
+ 'doris_permissions_enforce_read_only',
352
+ 'external_catalog_not_supported',
353
+ 'doris_transactions_not_exposed'
354
+ ] : []
355
+ };
356
+ }
357
+ async locations() {
358
+ 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");
359
+ return result.rows.map((row)=>this.engine === 'postgres' ? {
360
+ database: this.initial.database,
361
+ schema: string(row[0])
362
+ } : _extends._extends({
363
+ database: string(row[0])
364
+ }, this.engine === 'doris' ? {
365
+ engineCatalog: 'internal'
366
+ } : {}));
367
+ }
368
+ async objects(input) {
369
+ this.assertLocation(input);
370
+ var _input_page, _input_pageSize;
371
+ 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));
372
+ var _input_schema, _ref, _input_database;
373
+ 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;
374
+ if (!namespace) return {
375
+ items: [],
376
+ page,
377
+ pageSize,
378
+ hasMore: false
379
+ };
380
+ const placeholder = (i)=>this.engine === 'postgres' ? `$${i}` : '?';
381
+ var _input_search;
382
+ 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}`, [
383
+ namespace,
384
+ `%${(_input_search = input.search) != null ? _input_search : ''}%`
385
+ ], pageSize + 1);
386
+ return {
387
+ items: result.rows.slice(0, pageSize).map((row)=>({
388
+ database: input.database,
389
+ schema: input.schema,
390
+ engineCatalog: input.engineCatalog,
391
+ name: string(row[0]),
392
+ kind: string(row[1]).includes('VIEW') ? 'view' : 'table'
393
+ })),
394
+ page,
395
+ pageSize,
396
+ hasMore: result.rows.length > pageSize
397
+ };
398
+ }
399
+ async describe(object) {
400
+ const reference = this.ref(object);
401
+ var _object_schema, _ref, _object_database, _ref1;
402
+ 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 : '';
403
+ const p = (i)=>this.engine === 'postgres' ? `$${i}` : '?';
404
+ 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`, [
405
+ namespace,
406
+ object.name
407
+ ]);
408
+ const columns = metadata.rows.map((row, index)=>({
409
+ id: `c${index}`,
410
+ name: string(cell(metadata, row, 'column_name')),
411
+ dataType: string(cell(metadata, row, 'data_type')),
412
+ nullable: string(cell(metadata, row, 'is_nullable')) === 'YES',
413
+ ordinal: number(cell(metadata, row, 'ordinal_position')),
414
+ defaultValue: cell(metadata, row, 'column_default') == null ? null : string(cell(metadata, row, 'column_default'))
415
+ }));
416
+ if (!columns.length) throw new Error('object_not_found_or_denied');
417
+ const keys = [], diagnostics = [];
418
+ let definition = '', model;
419
+ if (this.engine === 'postgres') {
420
+ 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`, [
421
+ namespace,
422
+ object.name
423
+ ]);
424
+ for (const row of result.rows){
425
+ const name = string(cell(result, row, 'constraint_name')), kind = string(cell(result, row, 'constraint_type'));
426
+ let key = keys.find((item)=>item.name === name);
427
+ if (!key) {
428
+ key = {
429
+ name,
430
+ kind: kind === 'PRIMARY KEY' ? 'primary' : kind === 'FOREIGN KEY' ? 'foreign' : 'unique',
431
+ columns: []
432
+ };
433
+ keys.push(key);
434
+ }
435
+ key.columns.push(string(cell(result, row, 'column_name')));
436
+ if (key.kind === 'foreign') {
437
+ var _key;
438
+ key.referencedTable = {
439
+ schema: string(cell(result, row, 'foreign_schema')),
440
+ name: string(cell(result, row, 'foreign_table')),
441
+ kind: 'table'
442
+ };
443
+ var _referencedColumns;
444
+ ((_referencedColumns = (_key = key).referencedColumns) != null ? _referencedColumns : _key.referencedColumns = []).push(string(cell(result, row, 'foreign_column')));
445
+ }
446
+ }
447
+ if (object.kind === 'view') {
448
+ var _source_rows_;
449
+ const source = await this.raw('SELECT pg_get_viewdef($1::regclass, true) AS definition', [
450
+ reference
451
+ ], 1);
452
+ definition = `CREATE VIEW ${reference} AS\n${string((_source_rows_ = source.rows[0]) == null ? void 0 : _source_rows_[0])}`;
453
+ } else {
454
+ 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);`;
455
+ diagnostics.push('definition_reconstructed_from_metadata');
456
+ }
457
+ } else {
458
+ var _source_rows_1;
459
+ const source = await this.raw(`SHOW CREATE ${object.kind === 'view' ? 'VIEW' : 'TABLE'} ${reference}`, [], 1);
460
+ definition = string((_source_rows_1 = source.rows[0]) == null ? void 0 : _source_rows_1[1]);
461
+ if (this.engine === 'doris') {
462
+ const match = definition.match(/\b(UNIQUE|DUPLICATE|AGGREGATE)\s+KEY\s*\(([^)]*)\)/i);
463
+ 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;
464
+ if (model === 'unique' && match) keys.push({
465
+ name: 'UNIQUE KEY',
466
+ kind: 'primary',
467
+ columns: [
468
+ ...match[2].matchAll(/`((?:``|[^`])+)`/g)
469
+ ].map((item)=>item[1].split('``').join('`'))
470
+ });
471
+ } else {
472
+ const index = await this.raw(`SHOW INDEX FROM ${reference}`);
473
+ for (const row of index.rows){
474
+ const name = string(cell(index, row, 'Key_name'));
475
+ let key = keys.find((item)=>item.name === name);
476
+ if (!key) {
477
+ key = {
478
+ name,
479
+ kind: name === 'PRIMARY' ? 'primary' : number(cell(index, row, 'Non_unique')) === 0 ? 'unique' : 'index',
480
+ columns: []
481
+ };
482
+ keys.push(key);
483
+ }
484
+ key.columns.push(string(cell(index, row, 'Column_name')));
485
+ }
486
+ 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', [
487
+ namespace,
488
+ object.name
489
+ ]);
490
+ for (const row of foreign.rows){
491
+ var _key_referencedColumns;
492
+ let key = keys.find((item)=>item.name === string(row[0]));
493
+ if (!key) {
494
+ key = {
495
+ name: string(row[0]),
496
+ kind: 'foreign',
497
+ columns: [],
498
+ referencedTable: {
499
+ database: string(row[2]),
500
+ name: string(row[3]),
501
+ kind: 'table'
502
+ },
503
+ referencedColumns: []
504
+ };
505
+ keys.push(key);
506
+ }
507
+ key.columns.push(string(row[1]));
508
+ (_key_referencedColumns = key.referencedColumns) == null ? void 0 : _key_referencedColumns.push(string(row[4]));
509
+ }
510
+ }
511
+ }
512
+ 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)=>{
513
+ var _columns_find;
514
+ return ((_columns_find = columns.find((column)=>column.name === name)) == null ? void 0 : _columns_find.nullable) === false;
515
+ }));
516
+ return {
517
+ object,
518
+ columns,
519
+ keys,
520
+ definition,
521
+ model,
522
+ editable,
523
+ diagnostics
524
+ };
525
+ }
526
+ async query(input, signal) {
527
+ this.assertLocation(input);
528
+ if (input.database && input.database !== this.initial.database) throw new Error('database_requires_new_connection');
529
+ if (input.schema && input.schema !== this.initial.schema) throw new Error('schema_requires_new_connection');
530
+ if (this.busy) throw new Error('session_busy');
531
+ if (input.mode === 'read') {
532
+ requireReadStatement(input.sql);
533
+ }
534
+ const statements = splitWorkbenchSql(input.sql);
535
+ if (statements.length !== 1 || statements[0].effect === 'unsupported') throw new Error('unsupported_statement');
536
+ var _input_limit, _input_offset;
537
+ 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));
538
+ if (input.mode === 'write' && !(await this.capabilities()).writes) throw new Error('engine_version_writes_unavailable');
539
+ if (input.mode === 'write' && offset) throw new Error('mutation_cannot_be_paginated');
540
+ let sql = statements[0].sql;
541
+ let parameters = input.parameters;
542
+ if (input.mode === 'read' && [
543
+ 'SELECT',
544
+ 'WITH'
545
+ ].includes(statements[0].words[0])) {
546
+ if (this.engine === 'mysql') ({ sql, parameters } = paginateMysqlQuery(sql, parameters, limit, offset));
547
+ else sql = `SELECT * FROM (\n${sql}\n) AS ${this.quote('_db_studio_page')} LIMIT ${limit + 1} OFFSET ${offset}`;
548
+ } else if (offset) throw new Error('statement_cannot_be_paginated');
549
+ this.busy = true;
550
+ let readTransaction = false;
551
+ try {
552
+ if (input.mode === 'read' && this.engine !== 'doris' && !this.transactionOpen) {
553
+ await this.raw(this.engine === 'postgres' ? 'BEGIN READ ONLY' : 'START TRANSACTION READ ONLY', [], 1);
554
+ readTransaction = true;
555
+ }
556
+ var _input_timeoutMs;
557
+ const result = await this.raw(sql, parameters, limit + 1, signal, Math.max(1, Math.min((_input_timeoutMs = input.timeoutMs) != null ? _input_timeoutMs : 30000, 120000)));
558
+ const hasMore = result.rows.length > limit || result.truncated;
559
+ return _extends._extends({}, result, {
560
+ rows: result.rows.slice(0, limit),
561
+ hasMore,
562
+ truncated: result.truncated
563
+ });
564
+ } finally{
565
+ try {
566
+ if (readTransaction) {
567
+ try {
568
+ await this.raw('ROLLBACK', [], 1);
569
+ } catch (e) {
570
+ await this.close();
571
+ }
572
+ }
573
+ } finally{
574
+ this.busy = false;
575
+ }
576
+ }
577
+ }
578
+ async explain(input, signal) {
579
+ const sql = requireReadStatement(input.sql);
580
+ if (!/^(SELECT|WITH)\b/i.test(sql)) throw new Error('explain_requires_select');
581
+ const prefix = this.engine === 'postgres' ? 'EXPLAIN (FORMAT JSON) ' : this.engine === 'mysql' ? 'EXPLAIN FORMAT=JSON ' : 'EXPLAIN ';
582
+ return this.query(_extends._extends({}, input, {
583
+ sql: prefix + sql,
584
+ mode: 'read'
585
+ }), signal);
586
+ }
587
+ async transaction(action) {
588
+ if (this.engine === 'doris') throw new Error('transactions_not_supported');
589
+ if (this.busy) throw new Error('session_busy');
590
+ if (action === 'begin' && this.transactionOpen) throw new Error('transaction_already_open');
591
+ if (action !== 'begin' && !this.transactionOpen) throw new Error('transaction_not_open');
592
+ this.busy = true;
593
+ try {
594
+ await this.raw(action.toUpperCase(), [], 1);
595
+ this.transactionOpen = action === 'begin';
596
+ } finally{
597
+ this.busy = false;
598
+ }
599
+ }
600
+ async importRows(input, signal) {
601
+ this.assertLocation(input);
602
+ if (this.busy) throw new Error('session_busy');
603
+ if (!(await this.capabilities()).import) throw new Error('engine_version_import_unavailable');
604
+ if (new Set(input.columns).size !== input.columns.length) throw new Error('duplicate_import_columns');
605
+ 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');
606
+ if (this.transport.importRows) {
607
+ this.busy = true;
608
+ try {
609
+ return await this.transport.importRows(input, signal);
610
+ } finally{
611
+ this.busy = false;
612
+ }
613
+ }
614
+ if (this.engine === 'doris') throw new Error('stream_load_required');
615
+ const reference = this.ref(_extends._extends({}, input, {
616
+ name: input.table,
617
+ kind: 'table'
618
+ }));
619
+ await this.transaction('begin');
620
+ try {
621
+ for(let i = 0; i < input.rows.length; i += 100){
622
+ const batch = input.rows.slice(i, i + 100), values = batch.flat();
623
+ let n = 0;
624
+ const placeholders = batch.map((row)=>`(${row.map(()=>this.engine === 'postgres' ? `$${++n}` : '?').join(',')})`).join(',');
625
+ await this.query({
626
+ sql: `INSERT INTO ${reference} (${input.columns.map((name)=>this.quote(name)).join(',')}) VALUES ${placeholders}`,
627
+ parameters: values,
628
+ mode: 'write'
629
+ }, signal);
630
+ }
631
+ await this.transaction('commit');
632
+ return {
633
+ outcome: 'succeeded',
634
+ loadedRows: input.rows.length,
635
+ filteredRows: 0,
636
+ label: input.operationId,
637
+ diagnostics: []
638
+ };
639
+ } catch (error) {
640
+ try {
641
+ await this.transaction('rollback');
642
+ } catch (e) {
643
+ await this.close();
644
+ }
645
+ throw error;
646
+ }
647
+ }
648
+ async cancel() {
649
+ await this.close();
650
+ }
651
+ async close() {
652
+ this.closed = true;
653
+ await this.transport.close();
654
+ }
655
+ constructor(engine, transport, initial = {}){
656
+ this.transactionOpen = false;
657
+ this.busy = false;
658
+ this.closed = false;
659
+ this.engine = engine;
660
+ this.transport = transport;
661
+ this.initial = initial;
662
+ }
663
+ }
664
+
665
+ exports.DataSourceRuntimeCapability = DataSourceRuntimeCapability;
666
+ exports.SqlDatabaseWorkbenchAdapter = SqlDatabaseWorkbenchAdapter;
667
+ exports.quoteDatabaseIdentifier = quoteDatabaseIdentifier;
668
+ exports.requireReadStatement = requireReadStatement;
669
+ exports.splitWorkbenchSql = splitWorkbenchSql;
@@ -0,0 +1,2 @@
1
+ export * from './data-workbench.cjs.js';
2
+ export { _default as default } from './data-workbench.cjs.default.js';
@@ -0,0 +1 @@
1
+ export * from "./src/data-workbench";