@xpert-ai/plugin-sdk 3.18.6 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @xpert-ai/plugin-sdk
2
2
 
3
+ ## 3.18.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 5213185: Expose keyword analyzer strategy interfaces, discovery metadata, and a scoped registry with exact source lookup. Add optional plugin removal preflight guards so hosts can protect active analyzer dependencies.
8
+
9
+ Correct read-only SQL classification for derived tables, CTE column declarations and type modifiers, and preserve duplicate MySQL output names with direct bounded pagination.
10
+
3
11
  ## 3.18.6
4
12
 
5
13
  ### Patch Changes
@@ -8,6 +8,159 @@ const DataSourceRuntimeCapability = Object.freeze({
8
8
  __type: undefined
9
9
  });
10
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
+
11
164
  const WRITE = new Set([
12
165
  'INSERT',
13
166
  'UPDATE',
@@ -42,15 +195,16 @@ const FORBIDDEN = new Set([
42
195
  'DETACH'
43
196
  ]);
44
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(' '));
45
- 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(' '));
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(' '));
46
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;
47
201
  function splitWorkbenchSql(source) {
48
- if (!source.trim() || source.length > 100000 || source.includes('\0')) throw new Error('invalid_sql');
202
+ const { masked, tokens } = lexWorkbenchSql(source);
49
203
  const statements = [];
50
- let start = 0, index = 0, clean = '';
204
+ let start = 0;
51
205
  const finish = (end)=>{
52
- const text = clean.trim();
53
- if (text) {
206
+ const text = masked.slice(start, end);
207
+ if (text.trim()) {
54
208
  var _text_toUpperCase_match;
55
209
  const words = (_text_toUpperCase_match = text.toUpperCase().match(/[A-Z_][A-Z_0-9]*/g)) != null ? _text_toUpperCase_match : [];
56
210
  const first = words[0];
@@ -61,10 +215,11 @@ function splitWorkbenchSql(source) {
61
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';
62
216
  // eslint-disable-next-line no-control-regex -- intentional: reject any non-ASCII/control input
63
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));
64
219
  if ([
65
220
  ...text.matchAll(/([A-Z_][A-Z_0-9]*)\s*\(/gi)
66
- ].some((match)=>!SAFE_FUNCTIONS.has(match[1].toUpperCase()) && !SQL_PARENS.has(match[1].toUpperCase()))) effect = 'unsupported';
67
- 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';
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';
68
223
  }
69
224
  // CREATE/ALTER routines, external resources and privileged SQL are outside this app.
70
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';
@@ -74,63 +229,10 @@ function splitWorkbenchSql(source) {
74
229
  effect
75
230
  });
76
231
  }
77
- clean = '';
78
232
  start = end + 1;
79
233
  };
80
- while(index < source.length){
81
- const c = source[index], next = source[index + 1];
82
- if (c === '-' && next === '-' || c === '#') {
83
- while(index < source.length && source[index] !== '\n')index++;
84
- clean += ' ';
85
- continue;
86
- }
87
- if (c === '/' && next === '*') {
88
- if (source[index + 2] === '!') throw new Error('executable_comment_not_supported');
89
- const end = source.indexOf('*/', index + 2);
90
- if (end < 0) throw new Error('unterminated_comment');
91
- index = end + 2;
92
- clean += ' ';
93
- continue;
94
- }
95
- if (c === "'" || c === '"' || c === '`') {
96
- const quote = c;
97
- let closed = false;
98
- index++;
99
- while(index < source.length){
100
- if (source[index] === '\\') throw new Error('use_parameters_for_backslash_literals');
101
- if (source[index] === quote) {
102
- if (source[index + 1] === quote) {
103
- index += 2;
104
- continue;
105
- }
106
- index++;
107
- closed = true;
108
- break;
109
- }
110
- index++;
111
- }
112
- if (!closed) throw new Error('unterminated_literal');
113
- clean += quote === "'" ? ' ? ' : ' identifier ';
114
- continue;
115
- }
116
- if (c === '$') {
117
- var _source_slice_match;
118
- const tag = (_source_slice_match = source.slice(index).match(/^\$(?:[A-Za-z_][A-Za-z_0-9]*)?\$/)) == null ? void 0 : _source_slice_match[0];
119
- if (tag) {
120
- const end = source.indexOf(tag, index + tag.length);
121
- if (end < 0) throw new Error('unterminated_literal');
122
- index = end + tag.length;
123
- clean += ' ? ';
124
- continue;
125
- }
126
- }
127
- if (c === ';') {
128
- finish(index);
129
- index++;
130
- continue;
131
- }
132
- clean += c;
133
- index++;
234
+ for (const token of tokens){
235
+ if (token.kind === 'symbol' && token.text === ';') finish(token.start);
134
236
  }
135
237
  finish(source.length);
136
238
  if (!statements.length || statements.length > 50) throw new Error('statement_count_exceeded');
@@ -148,6 +250,53 @@ function quoteDatabaseIdentifier(name, engine) {
148
250
  return quote + name.split(quote).join(quote + quote) + quote;
149
251
  }
150
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
+
151
300
  const number = (value)=>Number(value != null ? value : 0);
152
301
  const string = (value)=>value == null ? '' : String(value);
153
302
  const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.name.toLowerCase() === name.toLowerCase())];
@@ -389,12 +538,14 @@ const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.
389
538
  if (input.mode === 'write' && !(await this.capabilities()).writes) throw new Error('engine_version_writes_unavailable');
390
539
  if (input.mode === 'write' && offset) throw new Error('mutation_cannot_be_paginated');
391
540
  let sql = statements[0].sql;
392
- // Wrap a SELECT instead of appending LIMIT after an existing LIMIT or trailing comment.
541
+ let parameters = input.parameters;
393
542
  if (input.mode === 'read' && [
394
543
  'SELECT',
395
544
  'WITH'
396
- ].includes(statements[0].words[0])) sql = `SELECT * FROM (\n${sql}\n) AS ${this.quote('_db_studio_page')} LIMIT ${limit + 1} OFFSET ${offset}`;
397
- else if (offset) throw new Error('statement_cannot_be_paginated');
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');
398
549
  this.busy = true;
399
550
  let readTransaction = false;
400
551
  try {
@@ -403,7 +554,7 @@ const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.
403
554
  readTransaction = true;
404
555
  }
405
556
  var _input_timeoutMs;
406
- 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)));
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)));
407
558
  const hasMore = result.rows.length > limit || result.truncated;
408
559
  return _extends._extends({}, result, {
409
560
  rows: result.rows.slice(0, limit),
@@ -6,6 +6,159 @@ const DataSourceRuntimeCapability = Object.freeze({
6
6
  __type: undefined
7
7
  });
8
8
 
9
+ /** Mask comments and quoted content without changing source offsets. Shared by guards and pagination. */ function lexWorkbenchSql(source) {
10
+ if (!source.trim() || source.length > 100000 || source.includes('\0')) throw new Error('invalid_sql');
11
+ const masked = source.split('');
12
+ const quoted = new Map();
13
+ const hide = (start, end, kind)=>{
14
+ for(let i = start; i < end; i++)if (masked[i] !== '\n') masked[i] = ' ';
15
+ if (kind) {
16
+ const marker = start + 1;
17
+ quoted.set(marker, kind);
18
+ masked[marker] = kind === 'literal' ? '?' : 'i';
19
+ }
20
+ };
21
+ let index = 0;
22
+ while(index < source.length){
23
+ const start = index, c = source[index], next = source[index + 1];
24
+ if (c === '-' && next === '-' || c === '#') {
25
+ while(index < source.length && source[index] !== '\n')index++;
26
+ hide(start, index);
27
+ continue;
28
+ }
29
+ if (c === '/' && next === '*') {
30
+ if (source[index + 2] === '!') throw new Error('executable_comment_not_supported');
31
+ const end = source.indexOf('*/', index + 2);
32
+ if (end < 0) throw new Error('unterminated_comment');
33
+ index = end + 2;
34
+ hide(start, index);
35
+ continue;
36
+ }
37
+ if (c === "'" || c === '"' || c === '`') {
38
+ let closed = false;
39
+ index++;
40
+ while(index < source.length){
41
+ if (source[index] === '\\') throw new Error('use_parameters_for_backslash_literals');
42
+ if (source[index] === c) {
43
+ if (source[index + 1] === c) {
44
+ index += 2;
45
+ continue;
46
+ }
47
+ index++;
48
+ closed = true;
49
+ break;
50
+ }
51
+ index++;
52
+ }
53
+ if (!closed) throw new Error('unterminated_literal');
54
+ hide(start, index, c === "'" ? 'literal' : 'identifier');
55
+ continue;
56
+ }
57
+ if (c === '$') {
58
+ var _source_slice_match;
59
+ const tag = (_source_slice_match = source.slice(index).match(/^\$(?:[A-Za-z_][A-Za-z_0-9]*)?\$/)) == null ? void 0 : _source_slice_match[0];
60
+ if (tag) {
61
+ const end = source.indexOf(tag, index + tag.length);
62
+ if (end < 0) throw new Error('unterminated_literal');
63
+ index = end + tag.length;
64
+ hide(start, index, 'literal');
65
+ continue;
66
+ }
67
+ }
68
+ index++;
69
+ }
70
+ const text = masked.join('');
71
+ const tokens = [
72
+ ...text.matchAll(/[A-Z_][A-Z_0-9]*|[0-9]+|[^\s]/gi)
73
+ ].map((match)=>{
74
+ var _quoted_get;
75
+ return {
76
+ text: match[0].toUpperCase(),
77
+ start: match.index,
78
+ end: match.index + match[0].length,
79
+ kind: (_quoted_get = quoted.get(match.index)) != null ? _quoted_get : /^[A-Z_]/i.test(match[0]) ? 'word' : /^[0-9]/.test(match[0]) ? 'number' : 'symbol'
80
+ };
81
+ });
82
+ return {
83
+ masked: text,
84
+ tokens
85
+ };
86
+ }
87
+
88
+ const MODIFIED_TYPES = new Set('DECIMAL NUMERIC CHAR VARCHAR NCHAR NVARCHAR BINARY VARBINARY DATETIME TIME TIMESTAMP'.split(' '));
89
+ /** Positions where name(...) declares columns or a built-in type, rather than invoking a function. */ function sqlDeclarationPositions(tokens) {
90
+ const declarations = new Set();
91
+ const closing = new Map(), parents = new Map();
92
+ const stack = [];
93
+ tokens.forEach((token, index)=>{
94
+ if (stack.length) parents.set(index, stack[stack.length - 1]);
95
+ if (token.text === '(') stack.push(index);
96
+ if (token.text === ')') {
97
+ const open = stack.pop();
98
+ if (open !== undefined) closing.set(open, index);
99
+ }
100
+ });
101
+ const name = (index)=>{
102
+ var _tokens_index;
103
+ return [
104
+ 'word',
105
+ 'identifier'
106
+ ].includes((_tokens_index = tokens[index]) == null ? void 0 : _tokens_index.kind);
107
+ };
108
+ const is = (index, word)=>{
109
+ var _tokens_index;
110
+ return ((_tokens_index = tokens[index]) == null ? void 0 : _tokens_index.text) === word;
111
+ };
112
+ for(let i = 0; i < tokens.length; i++){
113
+ if (tokens[i].kind !== 'word' || !is(i, 'WITH')) continue;
114
+ let cursor = i + 1;
115
+ if (is(cursor, 'RECURSIVE')) cursor++;
116
+ while(name(cursor)){
117
+ const declaration = cursor++;
118
+ let columns = false;
119
+ if (is(cursor, '(')) {
120
+ const end = closing.get(cursor);
121
+ if (end === undefined || end === cursor + 1) break;
122
+ const columnTokens = tokens.slice(cursor + 1, end);
123
+ if (columnTokens.length % 2 === 0 || !columnTokens.every((token, n)=>n % 2 === 0 ? [
124
+ 'word',
125
+ 'identifier'
126
+ ].includes(token.kind) : token.text === ',')) break;
127
+ columns = true;
128
+ cursor = end + 1;
129
+ }
130
+ if (!is(cursor++, 'AS')) break;
131
+ if (is(cursor, 'NOT')) cursor++;
132
+ if (is(cursor, 'MATERIALIZED')) cursor++;
133
+ if (!is(cursor, '(')) break;
134
+ const end = closing.get(cursor);
135
+ if (end === undefined) break;
136
+ if (columns) declarations.add(tokens[declaration].start);
137
+ cursor = end + 1;
138
+ if (!is(cursor, ',')) break;
139
+ cursor++;
140
+ }
141
+ }
142
+ tokens.forEach((token, i)=>{
143
+ var _tokens_;
144
+ if (token.kind !== 'word' || !MODIFIED_TYPES.has(token.text) || !is(i + 1, '(')) return;
145
+ const end = closing.get(i + 1);
146
+ if (end === undefined) return;
147
+ const args = tokens.slice(i + 2, end);
148
+ if (![
149
+ 1,
150
+ 3
151
+ ].includes(args.length) || !args.every((arg, n)=>n % 2 ? arg.text === ',' : arg.kind === 'number')) return;
152
+ const parent = parents.get(i);
153
+ const owner = parent === undefined ? undefined : (_tokens_ = tokens[parent - 1]) == null ? void 0 : _tokens_.text;
154
+ const cast = owner === 'CAST' && is(i - 1, 'AS');
155
+ const convert = owner === 'CONVERT' && is(i - 1, ',');
156
+ const postgresCast = is(i - 1, ':') && is(i - 2, ':');
157
+ if (cast || convert || postgresCast) declarations.add(token.start);
158
+ });
159
+ return declarations;
160
+ }
161
+
9
162
  const WRITE = new Set([
10
163
  'INSERT',
11
164
  'UPDATE',
@@ -40,15 +193,16 @@ const FORBIDDEN = new Set([
40
193
  'DETACH'
41
194
  ]);
42
195
  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(' '));
196
+ // FROM/JOIN can introduce a parenthesized subquery, not a function call.
197
+ 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(' '));
44
198
  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
199
  function splitWorkbenchSql(source) {
46
- if (!source.trim() || source.length > 100000 || source.includes('\0')) throw new Error('invalid_sql');
200
+ const { masked, tokens } = lexWorkbenchSql(source);
47
201
  const statements = [];
48
- let start = 0, index = 0, clean = '';
202
+ let start = 0;
49
203
  const finish = (end)=>{
50
- const text = clean.trim();
51
- if (text) {
204
+ const text = masked.slice(start, end);
205
+ if (text.trim()) {
52
206
  var _text_toUpperCase_match;
53
207
  const words = (_text_toUpperCase_match = text.toUpperCase().match(/[A-Z_][A-Z_0-9]*/g)) != null ? _text_toUpperCase_match : [];
54
208
  const first = words[0];
@@ -59,10 +213,11 @@ function splitWorkbenchSql(source) {
59
213
  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
214
  // eslint-disable-next-line no-control-regex -- intentional: reject any non-ASCII/control input
61
215
  if (/[^\x00-\x7f]/.test(text) || /\.\s*[A-Z_][A-Z_0-9]*\s*\(/i.test(text)) effect = 'unsupported';
216
+ const declarations = sqlDeclarationPositions(tokens.filter((token)=>token.start >= start && token.end <= end));
62
217
  if ([
63
218
  ...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';
219
+ ].some((match)=>!declarations.has(start + match.index) && !SAFE_FUNCTIONS.has(match[1].toUpperCase()) && !SQL_PARENS.has(match[1].toUpperCase()))) effect = 'unsupported';
220
+ 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';
66
221
  }
67
222
  // CREATE/ALTER routines, external resources and privileged SQL are outside this app.
68
223
  if (effect === 'write' && /\b(FUNCTION|PROCEDURE|TRIGGER|USER|ROLE|CATALOG|RESOURCE|SYSTEM|SERVER|EXTENSION|FILE|OUTFILE|DUMPFILE|EXECUTE)\b/i.test(text)) effect = 'unsupported';
@@ -72,63 +227,10 @@ function splitWorkbenchSql(source) {
72
227
  effect
73
228
  });
74
229
  }
75
- clean = '';
76
230
  start = end + 1;
77
231
  };
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++;
232
+ for (const token of tokens){
233
+ if (token.kind === 'symbol' && token.text === ';') finish(token.start);
132
234
  }
133
235
  finish(source.length);
134
236
  if (!statements.length || statements.length > 50) throw new Error('statement_count_exceeded');
@@ -146,6 +248,53 @@ function quoteDatabaseIdentifier(name, engine) {
146
248
  return quote + name.split(quote).join(quote + quote) + quote;
147
249
  }
148
250
 
251
+ /** Apply a page to a MySQL result without turning its (possibly duplicate) output names into table columns. */ function paginateMysqlQuery(sql, parameters = [], limit, offset) {
252
+ const { tokens } = lexWorkbenchSql(sql);
253
+ const top = [];
254
+ let depth = 0;
255
+ for (const token of tokens){
256
+ if (token.text === '(') depth++;
257
+ if (!depth) top.push(token);
258
+ if (token.text === ')') depth--;
259
+ if (depth < 0) throw new Error('unsupported_pagination');
260
+ }
261
+ if (depth) throw new Error('unsupported_pagination');
262
+ const start = top.findIndex((token)=>token.kind === 'word' && token.text === 'LIMIT');
263
+ if (start < 0) return {
264
+ sql: `${sql}\nLIMIT ${limit + 1} OFFSET ${offset}`,
265
+ parameters
266
+ };
267
+ const clause = top.slice(start + 1);
268
+ const bindings = tokens.filter((token)=>token.text === '?' && token.kind === 'symbol');
269
+ const consumed = new Set();
270
+ const integer = (token)=>{
271
+ if ((token == null ? void 0 : token.kind) === 'number') return BigInt(token.text);
272
+ if ((token == null ? void 0 : token.kind) === 'symbol' && token.text === '?') {
273
+ const index = bindings.indexOf(token), value = parameters[index];
274
+ if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) {
275
+ consumed.add(index);
276
+ return BigInt(value);
277
+ }
278
+ }
279
+ throw new Error('unsupported_pagination');
280
+ };
281
+ let count, skip = 0n;
282
+ if (clause.length === 1) count = integer(clause[0]);
283
+ else if (clause.length === 3 && clause[1].text === ',') {
284
+ skip = integer(clause[0]);
285
+ count = integer(clause[2]);
286
+ } else if (clause.length === 3 && clause[1].kind === 'word' && clause[1].text === 'OFFSET') {
287
+ count = integer(clause[0]);
288
+ skip = integer(clause[2]);
289
+ } else throw new Error('unsupported_pagination');
290
+ const remaining = count > BigInt(offset) ? count - BigInt(offset) : 0n;
291
+ const size = remaining < BigInt(limit + 1) ? remaining : BigInt(limit + 1);
292
+ return {
293
+ sql: `${sql.slice(0, top[start].start)}LIMIT ${size} OFFSET ${skip + BigInt(offset)}${sql.slice(clause[clause.length - 1].end)}`,
294
+ parameters: parameters.filter((_, index)=>!consumed.has(index))
295
+ };
296
+ }
297
+
149
298
  const number = (value)=>Number(value != null ? value : 0);
150
299
  const string = (value)=>value == null ? '' : String(value);
151
300
  const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.name.toLowerCase() === name.toLowerCase())];
@@ -387,12 +536,14 @@ const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.
387
536
  if (input.mode === 'write' && !(await this.capabilities()).writes) throw new Error('engine_version_writes_unavailable');
388
537
  if (input.mode === 'write' && offset) throw new Error('mutation_cannot_be_paginated');
389
538
  let sql = statements[0].sql;
390
- // Wrap a SELECT instead of appending LIMIT after an existing LIMIT or trailing comment.
539
+ let parameters = input.parameters;
391
540
  if (input.mode === 'read' && [
392
541
  'SELECT',
393
542
  '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');
543
+ ].includes(statements[0].words[0])) {
544
+ if (this.engine === 'mysql') ({ sql, parameters } = paginateMysqlQuery(sql, parameters, limit, offset));
545
+ else sql = `SELECT * FROM (\n${sql}\n) AS ${this.quote('_db_studio_page')} LIMIT ${limit + 1} OFFSET ${offset}`;
546
+ } else if (offset) throw new Error('statement_cannot_be_paginated');
396
547
  this.busy = true;
397
548
  let readTransaction = false;
398
549
  try {
@@ -401,7 +552,7 @@ const cell = (result, row, name)=>row[result.columns.findIndex((column)=>column.
401
552
  readTransaction = true;
402
553
  }
403
554
  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)));
555
+ const result = await this.raw(sql, parameters, limit + 1, signal, Math.max(1, Math.min((_input_timeoutMs = input.timeoutMs) != null ? _input_timeoutMs : 30000, 120000)));
405
556
  const hasMore = result.rows.length > limit || result.truncated;
406
557
  return _extends({}, result, {
407
558
  rows: result.rows.slice(0, limit),
package/index.cjs.js CHANGED
@@ -515,6 +515,15 @@ function runWithRequestContext(req, res, next) {
515
515
  }
516
516
 
517
517
  exports.StrategyBus = class StrategyBus {
518
+ registerRemovalGuard(guard) {
519
+ this.removalGuards.add(guard);
520
+ return ()=>{
521
+ this.removalGuards.delete(guard);
522
+ };
523
+ }
524
+ async assertCanRemove(scopeKey, pluginNames) {
525
+ for (const guard of this.removalGuards)await guard(scopeKey, pluginNames);
526
+ }
518
527
  upsert(strategyType, entry) {
519
528
  this.subject.next({
520
529
  type: 'UPSERT',
@@ -532,6 +541,7 @@ exports.StrategyBus = class StrategyBus {
532
541
  } : {}));
533
542
  }
534
543
  constructor(){
544
+ this.removalGuards = new Set();
535
545
  this.subject = new rxjs.Subject();
536
546
  this.events$ = this.subject.asObservable();
537
547
  }
@@ -1031,6 +1041,34 @@ exports.KnowledgeStrategyRegistry = __decorate([
1031
1041
  ])
1032
1042
  ], exports.KnowledgeStrategyRegistry);
1033
1043
 
1044
+ const KEYWORD_ANALYZER_STRATEGY = 'KEYWORD_ANALYZER_STRATEGY';
1045
+ const KeywordAnalyzerStrategy = (provider)=>common.applyDecorators(common.SetMetadata(KEYWORD_ANALYZER_STRATEGY, provider), common.SetMetadata(STRATEGY_META_KEY, KEYWORD_ANALYZER_STRATEGY));
1046
+
1047
+ exports.KeywordAnalyzerRegistry = class KeywordAnalyzerRegistry extends BaseStrategyRegistry {
1048
+ /** Resolve a pinned source without allowing a higher-priority scope to shadow it. */ getBySource(provider, source, organizationId) {
1049
+ var _this_strategies_get;
1050
+ if (!this.resolveStrategyScopeKeys(organizationId).includes(source.scopeKey)) return undefined;
1051
+ const strategy = (_this_strategies_get = this.strategies.get(source.scopeKey)) == null ? void 0 : _this_strategies_get.get(provider);
1052
+ if (!strategy) return undefined;
1053
+ const registered = this.getSource(strategy);
1054
+ if (registered.kind !== source.kind || registered.scopeKey !== source.scopeKey) return undefined;
1055
+ if (source.kind === 'plugin' && (registered.kind !== 'plugin' || registered.pluginName !== source.pluginName)) {
1056
+ return undefined;
1057
+ }
1058
+ return strategy;
1059
+ }
1060
+ constructor(discoveryService, reflector){
1061
+ super(KEYWORD_ANALYZER_STRATEGY, discoveryService, reflector);
1062
+ }
1063
+ };
1064
+ exports.KeywordAnalyzerRegistry = __decorate([
1065
+ common.Injectable(),
1066
+ __metadata("design:paramtypes", [
1067
+ core.DiscoveryService,
1068
+ core.Reflector
1069
+ ])
1070
+ ], exports.KeywordAnalyzerRegistry);
1071
+
1034
1072
  const TOOLSET_STRATEGY = 'TOOLSET_STRATEGY';
1035
1073
  /**
1036
1074
  * Decorator to mark a provider as a Toolset Strategy
@@ -9849,7 +9887,9 @@ exports.ImageUnderstandingStrategy = ImageUnderstandingStrategy;
9849
9887
  exports.IntegrationStrategyKey = IntegrationStrategyKey;
9850
9888
  exports.JUMP_TO_TARGETS = JUMP_TO_TARGETS;
9851
9889
  exports.JsonSchemaValidator = JsonSchemaValidator;
9890
+ exports.KEYWORD_ANALYZER_STRATEGY = KEYWORD_ANALYZER_STRATEGY;
9852
9891
  exports.KNOWLEDGE_STRATEGY = KNOWLEDGE_STRATEGY;
9892
+ exports.KeywordAnalyzerStrategy = KeywordAnalyzerStrategy;
9853
9893
  exports.KnowledgeStrategyKey = KnowledgeStrategyKey;
9854
9894
  exports.LLMUsage = LLMUsage;
9855
9895
  exports.LargeLanguageModel = LargeLanguageModel;
package/index.esm.js CHANGED
@@ -497,6 +497,15 @@ function runWithRequestContext(req, res, next) {
497
497
  }
498
498
 
499
499
  let StrategyBus = class StrategyBus {
500
+ registerRemovalGuard(guard) {
501
+ this.removalGuards.add(guard);
502
+ return ()=>{
503
+ this.removalGuards.delete(guard);
504
+ };
505
+ }
506
+ async assertCanRemove(scopeKey, pluginNames) {
507
+ for (const guard of this.removalGuards)await guard(scopeKey, pluginNames);
508
+ }
500
509
  upsert(strategyType, entry) {
501
510
  this.subject.next({
502
511
  type: 'UPSERT',
@@ -514,6 +523,7 @@ let StrategyBus = class StrategyBus {
514
523
  } : {}));
515
524
  }
516
525
  constructor(){
526
+ this.removalGuards = new Set();
517
527
  this.subject = new Subject();
518
528
  this.events$ = this.subject.asObservable();
519
529
  }
@@ -1013,6 +1023,34 @@ KnowledgeStrategyRegistry = __decorate([
1013
1023
  ])
1014
1024
  ], KnowledgeStrategyRegistry);
1015
1025
 
1026
+ const KEYWORD_ANALYZER_STRATEGY = 'KEYWORD_ANALYZER_STRATEGY';
1027
+ const KeywordAnalyzerStrategy = (provider)=>applyDecorators(SetMetadata(KEYWORD_ANALYZER_STRATEGY, provider), SetMetadata(STRATEGY_META_KEY, KEYWORD_ANALYZER_STRATEGY));
1028
+
1029
+ let KeywordAnalyzerRegistry = class KeywordAnalyzerRegistry extends BaseStrategyRegistry {
1030
+ /** Resolve a pinned source without allowing a higher-priority scope to shadow it. */ getBySource(provider, source, organizationId) {
1031
+ var _this_strategies_get;
1032
+ if (!this.resolveStrategyScopeKeys(organizationId).includes(source.scopeKey)) return undefined;
1033
+ const strategy = (_this_strategies_get = this.strategies.get(source.scopeKey)) == null ? void 0 : _this_strategies_get.get(provider);
1034
+ if (!strategy) return undefined;
1035
+ const registered = this.getSource(strategy);
1036
+ if (registered.kind !== source.kind || registered.scopeKey !== source.scopeKey) return undefined;
1037
+ if (source.kind === 'plugin' && (registered.kind !== 'plugin' || registered.pluginName !== source.pluginName)) {
1038
+ return undefined;
1039
+ }
1040
+ return strategy;
1041
+ }
1042
+ constructor(discoveryService, reflector){
1043
+ super(KEYWORD_ANALYZER_STRATEGY, discoveryService, reflector);
1044
+ }
1045
+ };
1046
+ KeywordAnalyzerRegistry = __decorate([
1047
+ Injectable(),
1048
+ __metadata("design:paramtypes", [
1049
+ DiscoveryService,
1050
+ Reflector
1051
+ ])
1052
+ ], KeywordAnalyzerRegistry);
1053
+
1016
1054
  const TOOLSET_STRATEGY = 'TOOLSET_STRATEGY';
1017
1055
  /**
1018
1056
  * Decorator to mark a provider as a Toolset Strategy
@@ -9701,4 +9739,4 @@ function stableJson(value) {
9701
9739
  return `{${entries.map(([key, item])=>`${JSON.stringify(key)}:${stableJson(item)}`).join(',')}}`;
9702
9740
  }
9703
9741
 
9704
- export { ACCOUNT_BINDING_PERMISSION_SERVICE_TOKEN, AGENT_CHAT_DISPATCH_ERROR_STEER_TARGET_NOT_RUNNING, AGENT_CHAT_DISPATCH_MESSAGE_TYPE, AGENT_MIDDLEWARE_STRATEGY, AIModel, AIModelProviderNotFoundException, AIModelProviderRegistry, AIModelProviderStrategy, AI_MODEL_PROVIDER, AdapterDataSourceStrategy, AgentMiddlewareRegistry, AgentMiddlewareStrategy, AiModelNotFoundException, AssistantTaskRuntimeCapability, BOUND_IDENTITY_LOGIN_PERMISSION_SERVICE_TOKEN, BUILTIN_GLOBAL_SCOPE, BaseHTTPQueryRunner, BaseQueryRunner, BaseSQLQueryRunner, BaseSandbox, BaseStrategyRegistry, BuiltinToolset, CHAT_CHANNEL, CHAT_CHANNEL_TEXT_LIMITS, COLLABORATION_DOCUMENT_PROVIDER, CONNECTION_COMMAND_ROUTER_TOKEN, CONNECTOR_STRATEGY, CancelConversationCommand, ChatChannel, ChatChannelRegistry, ChatOAICompatReasoningModel, CollaborationDocumentProvider, CollaborationDocumentProviderRegistry, CollaborationRuntimeCapability, CommonParameterRules, ConnectorRuntimeCapability, ConnectorRuntimeFactoryCapability, ConnectorStrategyKey, ConnectorStrategyRegistry, CreateModelClientCommand, CredentialsValidateFailedError, DATASOURCE_STRATEGY, DBCreateTableMode, DBProtocolEnum, DBSyntaxEnum, DBTableAction, DBTableDataAction, DEFAULT_EXECUTION_CONFIG, DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS, DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_MS, DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_SEC, DEFAULT_SANDBOX_FILE_SEARCH_EXECUTION_OPTIONS, DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_MS, DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_SEC, DEFAULT_SANDBOX_SHELL_EXECUTION_OPTIONS, DEFAULT_SANDBOX_SHELL_TIMEOUT_MS, DEFAULT_SANDBOX_SHELL_TIMEOUT_SEC, DOCUMENT_SOURCE_STRATEGY, DOCUMENT_TRANSFORMER_STRATEGY, DataSourceStrategy, DataSourceStrategyRegistry, DecoratedAgentMiddlewareStrategy, DecoratedToolsetStrategy, DocumentSourceRegistry, DocumentSourceStrategy, DocumentTransformerRegistry, DocumentTransformerStrategy, EVOLUTION_RUNTIME_SERVICE_TOKEN, EVOLUTION_TARGET_PROVIDER, EvolutionChangeRuntimeCapability, EvolutionRuntimeCapability, EvolutionTargetProviderRegistry, EvolutionTargetProviderStrategy, FILE_STORAGE_PROVIDER, FILE_UPLOAD_TARGET_STRATEGY, FileRuntimeCapability, FileStorageProvider, FileStorageProviderRegistry, FileUploadTargetRegistry, FileUploadTargetStrategy, GLOBAL_ORGANIZATION_SCOPE, HANDOFF_PERMISSION_SERVICE_TOKEN, HANDOFF_PROCESSOR_STRATEGY, HANDOFF_QUEUE_SERVICE_TOKEN, HandoffProcessorRegistry, HandoffProcessorStrategy, IMAGE_UNDERSTANDING_STRATEGY, INTEGRATION_PERMISSION_SERVICE_TOKEN, INTEGRATION_STRATEGY, ImageGenerationModel, ImageUnderstandingRegistry, ImageUnderstandingStrategy, IntegrationStrategyKey, IntegrationStrategyRegistry, JUMP_TO_TARGETS, JsonSchemaValidator, KNOWLEDGE_STRATEGY, KnowledgeStrategyKey, KnowledgeStrategyRegistry, LLMUsage, LargeLanguageModel, MANAGED_CONNECTION_REGISTRY_TOKEN, MANAGED_QUEUE_HANDLER_REGISTRY_TOKEN, MANAGED_QUEUE_SERVICE_TOKEN, MCP_CAPABILITY_CHANGE_EVENT_TYPES, MODEL_OUTPUT_VALIDATION_ERROR_CODE, ModelOutputValidationError, ModelProvider, ORGANIZATION_METADATA_KEY, OpenAICompatibleReranker, PERMISSION_OPERATION_METADATA_KEY, PLUGIN_CONFIG_RESOLVER_TOKEN, PLUGIN_JOB_PROCESSOR_METADATA, PLUGIN_METADATA, PLUGIN_METADATA_KEY, PLUGIN_VERSION_METADATA_KEY, PLUGIN_WEBHOOK_AUTH_METADATA_KEY, PLUGIN_WEBHOOK_AUTH_SERVICE_TOKEN, PROVIDE_AI_MODEL_LLM, PROVIDE_AI_MODEL_MODERATION, PROVIDE_AI_MODEL_RERANK, PROVIDE_AI_MODEL_SPEECH2TEXT, PROVIDE_AI_MODEL_TEXT_EMBEDDING, PROVIDE_AI_MODEL_TTS, PluginJobProcessor, PluginWebhookAuth, PluginWebhookAuthGuard, RETRIEVER_STRATEGY, RequestContext, RequestContextMiddleware, RequirePermissionOperation, RerankModel, RetrieverRegistry, RetrieverStrategy, SANDBOX_PROVIDER, SANDBOX_RUNTIME_PROVIDER, SANDBOX_SHELL_TIMEOUT_LIMITS_SEC, SANDBOX_WORKSPACE_MAPPER, SKILL_SOURCE_PROVIDER, SPEECH_TO_TEXT_PERMISSION_SERVICE_TOKEN, SPEECH_TO_TEXT_SERVICE_TOKEN, SSOProviderRegistry, SSOProviderStrategyKey, SSO_BINDING_PERMISSION_SERVICE_TOKEN, SSO_PROVIDER, STRATEGY_META_KEY, SYSTEM_GLOBAL_SCOPE, SandboxProviderRegistry, SandboxProviderStrategy, SandboxRuntimeProviderRegistry, SandboxRuntimeProviderStrategy, SandboxWorkspaceMapperRegistry, SandboxWorkspaceMapperStrategy, SkillSourceProviderRegistry, SkillSourceProviderStrategy, Speech2TextChatModel, SpeechToTextModel, StrategyBus, TENANT_GLOBAL_SCOPE_PREFIX, TENANT_GLOBAL_SCOPE_SUFFIX, TEXT_SPLITTER_STRATEGY, TOOLSET_STRATEGY, TextEmbeddingModelManager, TextSplitterRegistry, TextSplitterStrategy, TextToSpeechModel, ToolsetRegistry, ToolsetStrategy, USER_PERMISSION_SERVICE_TOKEN, VECTOR_STORE_STRATEGY, VIDEO_GENERATION_PERMISSION_SERVICE_TOKEN, VIDEO_GENERATION_SERVICE_TOKEN, VIDEO_GENERATOR_FAMILIES, VIEW_EXTENSION_CACHE_SERVICE_TOKEN, VIEW_EXTENSION_PROVIDER, VectorStoreRegistry, VectorStoreStrategy, VideoGenerationModel, ViewExtensionProvider, ViewExtensionProviderRegistry, WORKFLOW_NODE_STRATEGY, WORKFLOW_TRIGGER_STRATEGY, WorkflowNodeRegistry, WorkflowNodeStrategy, WorkflowTriggerRegistry, WorkflowTriggerStrategy, WorkspaceFilesRuntimeCapability, WrapWorkflowNodeExecutionCommand, XPERT_AGENT_MIDDLEWARE_RUNTIME_TOKEN, XPERT_TOOL_METHOD_METADATA, XPERT_TOOL_PROVIDER, XPERT_TOOL_PROVIDER_METADATA, XPERT_TOOL_RESULT_FORMAT_VERSION, XpFileSystem, XpertPrompt, XpertResource, XpertResourceTemplate, XpertServerPlugin, XpertTool, XpertToolProvider, XpertToolProviderCoordinator, XpertToolProviderRegistry, als, appendSandboxMessage, assertConnectorDefinition, buildSandboxTimeoutMessage, calcTokenUsage, calculateLLMUsagePrice, calculateModelPrice, calculateModelUsageCharge, chunkText, countTextTokens, countTokensSafe, createI18nInstance, createPluginLogger, createRuntimeCapability, defineAgentMessageType, defineChannelMessageType, defineMcpApp, defineMcpCapabilities, defineMcpPrompt, defineMcpResource, defineMcpResourceTemplate, defineXpertTool, derivePluginArtifactNamespace, describeXpertToolProvider, downloadRemoteFile, formatSandboxTimeout, getDefaultTenantId, getErrorMessage, getModelContextSize, getPermissionOperationMetadata, getPositionList, getPositionMap, getRequestContext, getRequiredPermissionOperation, getTenantGlobalScopeKey, getXpertToolMethod, isDevelopmentSandboxRuntimeEnvironment, isModelOutputValidationError, isModelUsagePricingConfig, isRemoteFile, isSandboxBackend, isSandboxManagedServiceAdapter, isSandboxServiceProxyAdapter, isSandboxTerminalAdapter, isStrategyInstance, isStructuredMessageType, isTenantGlobalScopeKey, loadYamlFile, mergeCredentials, mergeParentChildChunks, normalizeContextSize, pluginArtifactTableName, prepareToolResult, processAsyncAIGCManagedJob, renderRemoteModuleIframeHtml, renderRemoteReactIframeHtml, renderRemoteVueIframeHtml, resolveModelUsagePricingSnapshot, resolveSandboxBackend, resolveSandboxExecutionOptions, resolveSandboxManagedServiceAdapter, resolveSandboxServiceProxyAdapter, resolveSandboxTerminalAdapter, resolveStrategyMetadataTarget, resolveTenantGlobalScopeKey, resolveTokenUsage, resolveXpertMcpExtensions, runWithRequestContext, runtimeToolProviderComponent, secondsToMilliseconds, setDefaultTenantId, sumTokenUsage, textTokenBoundaries };
9742
+ export { ACCOUNT_BINDING_PERMISSION_SERVICE_TOKEN, AGENT_CHAT_DISPATCH_ERROR_STEER_TARGET_NOT_RUNNING, AGENT_CHAT_DISPATCH_MESSAGE_TYPE, AGENT_MIDDLEWARE_STRATEGY, AIModel, AIModelProviderNotFoundException, AIModelProviderRegistry, AIModelProviderStrategy, AI_MODEL_PROVIDER, AdapterDataSourceStrategy, AgentMiddlewareRegistry, AgentMiddlewareStrategy, AiModelNotFoundException, AssistantTaskRuntimeCapability, BOUND_IDENTITY_LOGIN_PERMISSION_SERVICE_TOKEN, BUILTIN_GLOBAL_SCOPE, BaseHTTPQueryRunner, BaseQueryRunner, BaseSQLQueryRunner, BaseSandbox, BaseStrategyRegistry, BuiltinToolset, CHAT_CHANNEL, CHAT_CHANNEL_TEXT_LIMITS, COLLABORATION_DOCUMENT_PROVIDER, CONNECTION_COMMAND_ROUTER_TOKEN, CONNECTOR_STRATEGY, CancelConversationCommand, ChatChannel, ChatChannelRegistry, ChatOAICompatReasoningModel, CollaborationDocumentProvider, CollaborationDocumentProviderRegistry, CollaborationRuntimeCapability, CommonParameterRules, ConnectorRuntimeCapability, ConnectorRuntimeFactoryCapability, ConnectorStrategyKey, ConnectorStrategyRegistry, CreateModelClientCommand, CredentialsValidateFailedError, DATASOURCE_STRATEGY, DBCreateTableMode, DBProtocolEnum, DBSyntaxEnum, DBTableAction, DBTableDataAction, DEFAULT_EXECUTION_CONFIG, DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS, DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_MS, DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_SEC, DEFAULT_SANDBOX_FILE_SEARCH_EXECUTION_OPTIONS, DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_MS, DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_SEC, DEFAULT_SANDBOX_SHELL_EXECUTION_OPTIONS, DEFAULT_SANDBOX_SHELL_TIMEOUT_MS, DEFAULT_SANDBOX_SHELL_TIMEOUT_SEC, DOCUMENT_SOURCE_STRATEGY, DOCUMENT_TRANSFORMER_STRATEGY, DataSourceStrategy, DataSourceStrategyRegistry, DecoratedAgentMiddlewareStrategy, DecoratedToolsetStrategy, DocumentSourceRegistry, DocumentSourceStrategy, DocumentTransformerRegistry, DocumentTransformerStrategy, EVOLUTION_RUNTIME_SERVICE_TOKEN, EVOLUTION_TARGET_PROVIDER, EvolutionChangeRuntimeCapability, EvolutionRuntimeCapability, EvolutionTargetProviderRegistry, EvolutionTargetProviderStrategy, FILE_STORAGE_PROVIDER, FILE_UPLOAD_TARGET_STRATEGY, FileRuntimeCapability, FileStorageProvider, FileStorageProviderRegistry, FileUploadTargetRegistry, FileUploadTargetStrategy, GLOBAL_ORGANIZATION_SCOPE, HANDOFF_PERMISSION_SERVICE_TOKEN, HANDOFF_PROCESSOR_STRATEGY, HANDOFF_QUEUE_SERVICE_TOKEN, HandoffProcessorRegistry, HandoffProcessorStrategy, IMAGE_UNDERSTANDING_STRATEGY, INTEGRATION_PERMISSION_SERVICE_TOKEN, INTEGRATION_STRATEGY, ImageGenerationModel, ImageUnderstandingRegistry, ImageUnderstandingStrategy, IntegrationStrategyKey, IntegrationStrategyRegistry, JUMP_TO_TARGETS, JsonSchemaValidator, KEYWORD_ANALYZER_STRATEGY, KNOWLEDGE_STRATEGY, KeywordAnalyzerRegistry, KeywordAnalyzerStrategy, KnowledgeStrategyKey, KnowledgeStrategyRegistry, LLMUsage, LargeLanguageModel, MANAGED_CONNECTION_REGISTRY_TOKEN, MANAGED_QUEUE_HANDLER_REGISTRY_TOKEN, MANAGED_QUEUE_SERVICE_TOKEN, MCP_CAPABILITY_CHANGE_EVENT_TYPES, MODEL_OUTPUT_VALIDATION_ERROR_CODE, ModelOutputValidationError, ModelProvider, ORGANIZATION_METADATA_KEY, OpenAICompatibleReranker, PERMISSION_OPERATION_METADATA_KEY, PLUGIN_CONFIG_RESOLVER_TOKEN, PLUGIN_JOB_PROCESSOR_METADATA, PLUGIN_METADATA, PLUGIN_METADATA_KEY, PLUGIN_VERSION_METADATA_KEY, PLUGIN_WEBHOOK_AUTH_METADATA_KEY, PLUGIN_WEBHOOK_AUTH_SERVICE_TOKEN, PROVIDE_AI_MODEL_LLM, PROVIDE_AI_MODEL_MODERATION, PROVIDE_AI_MODEL_RERANK, PROVIDE_AI_MODEL_SPEECH2TEXT, PROVIDE_AI_MODEL_TEXT_EMBEDDING, PROVIDE_AI_MODEL_TTS, PluginJobProcessor, PluginWebhookAuth, PluginWebhookAuthGuard, RETRIEVER_STRATEGY, RequestContext, RequestContextMiddleware, RequirePermissionOperation, RerankModel, RetrieverRegistry, RetrieverStrategy, SANDBOX_PROVIDER, SANDBOX_RUNTIME_PROVIDER, SANDBOX_SHELL_TIMEOUT_LIMITS_SEC, SANDBOX_WORKSPACE_MAPPER, SKILL_SOURCE_PROVIDER, SPEECH_TO_TEXT_PERMISSION_SERVICE_TOKEN, SPEECH_TO_TEXT_SERVICE_TOKEN, SSOProviderRegistry, SSOProviderStrategyKey, SSO_BINDING_PERMISSION_SERVICE_TOKEN, SSO_PROVIDER, STRATEGY_META_KEY, SYSTEM_GLOBAL_SCOPE, SandboxProviderRegistry, SandboxProviderStrategy, SandboxRuntimeProviderRegistry, SandboxRuntimeProviderStrategy, SandboxWorkspaceMapperRegistry, SandboxWorkspaceMapperStrategy, SkillSourceProviderRegistry, SkillSourceProviderStrategy, Speech2TextChatModel, SpeechToTextModel, StrategyBus, TENANT_GLOBAL_SCOPE_PREFIX, TENANT_GLOBAL_SCOPE_SUFFIX, TEXT_SPLITTER_STRATEGY, TOOLSET_STRATEGY, TextEmbeddingModelManager, TextSplitterRegistry, TextSplitterStrategy, TextToSpeechModel, ToolsetRegistry, ToolsetStrategy, USER_PERMISSION_SERVICE_TOKEN, VECTOR_STORE_STRATEGY, VIDEO_GENERATION_PERMISSION_SERVICE_TOKEN, VIDEO_GENERATION_SERVICE_TOKEN, VIDEO_GENERATOR_FAMILIES, VIEW_EXTENSION_CACHE_SERVICE_TOKEN, VIEW_EXTENSION_PROVIDER, VectorStoreRegistry, VectorStoreStrategy, VideoGenerationModel, ViewExtensionProvider, ViewExtensionProviderRegistry, WORKFLOW_NODE_STRATEGY, WORKFLOW_TRIGGER_STRATEGY, WorkflowNodeRegistry, WorkflowNodeStrategy, WorkflowTriggerRegistry, WorkflowTriggerStrategy, WorkspaceFilesRuntimeCapability, WrapWorkflowNodeExecutionCommand, XPERT_AGENT_MIDDLEWARE_RUNTIME_TOKEN, XPERT_TOOL_METHOD_METADATA, XPERT_TOOL_PROVIDER, XPERT_TOOL_PROVIDER_METADATA, XPERT_TOOL_RESULT_FORMAT_VERSION, XpFileSystem, XpertPrompt, XpertResource, XpertResourceTemplate, XpertServerPlugin, XpertTool, XpertToolProvider, XpertToolProviderCoordinator, XpertToolProviderRegistry, als, appendSandboxMessage, assertConnectorDefinition, buildSandboxTimeoutMessage, calcTokenUsage, calculateLLMUsagePrice, calculateModelPrice, calculateModelUsageCharge, chunkText, countTextTokens, countTokensSafe, createI18nInstance, createPluginLogger, createRuntimeCapability, defineAgentMessageType, defineChannelMessageType, defineMcpApp, defineMcpCapabilities, defineMcpPrompt, defineMcpResource, defineMcpResourceTemplate, defineXpertTool, derivePluginArtifactNamespace, describeXpertToolProvider, downloadRemoteFile, formatSandboxTimeout, getDefaultTenantId, getErrorMessage, getModelContextSize, getPermissionOperationMetadata, getPositionList, getPositionMap, getRequestContext, getRequiredPermissionOperation, getTenantGlobalScopeKey, getXpertToolMethod, isDevelopmentSandboxRuntimeEnvironment, isModelOutputValidationError, isModelUsagePricingConfig, isRemoteFile, isSandboxBackend, isSandboxManagedServiceAdapter, isSandboxServiceProxyAdapter, isSandboxTerminalAdapter, isStrategyInstance, isStructuredMessageType, isTenantGlobalScopeKey, loadYamlFile, mergeCredentials, mergeParentChildChunks, normalizeContextSize, pluginArtifactTableName, prepareToolResult, processAsyncAIGCManagedJob, renderRemoteModuleIframeHtml, renderRemoteReactIframeHtml, renderRemoteVueIframeHtml, resolveModelUsagePricingSnapshot, resolveSandboxBackend, resolveSandboxExecutionOptions, resolveSandboxManagedServiceAdapter, resolveSandboxServiceProxyAdapter, resolveSandboxTerminalAdapter, resolveStrategyMetadataTarget, resolveTenantGlobalScopeKey, resolveTokenUsage, resolveXpertMcpExtensions, runWithRequestContext, runtimeToolProviderComponent, secondsToMilliseconds, setDefaultTenantId, sumTokenUsage, textTokenBoundaries };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpert-ai/plugin-sdk",
3
- "version": "3.18.6",
3
+ "version": "3.18.7",
4
4
  "license": "AGPL-3.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,6 +11,9 @@ export type StrategyBusEvent<S = any> = {
11
11
  cause?: 'refresh' | 'uninstall';
12
12
  };
13
13
  export declare class StrategyBus {
14
+ private readonly removalGuards;
15
+ registerRemovalGuard(guard: (scopeKey: string, pluginNames: readonly string[]) => Promise<void>): () => void;
16
+ assertCanRemove(scopeKey: string, pluginNames: readonly string[]): Promise<void>;
14
17
  private readonly subject;
15
18
  readonly events$: import("rxjs").Observable<StrategyBusEvent<any>>;
16
19
  upsert<S>(strategyType: string, entry: StrategyEntry<S>): void;
@@ -0,0 +1,6 @@
1
+ import type { DatabaseValue } from './workbench';
2
+ /** Apply a page to a MySQL result without turning its (possibly duplicate) output names into table columns. */
3
+ export declare function paginateMysqlQuery(sql: string, parameters: DatabaseValue[], limit: number, offset: number): {
4
+ sql: string;
5
+ parameters: DatabaseValue[];
6
+ };
@@ -0,0 +1,11 @@
1
+ export interface SqlToken {
2
+ text: string;
3
+ start: number;
4
+ end: number;
5
+ kind: 'word' | 'number' | 'symbol' | 'identifier' | 'literal';
6
+ }
7
+ /** Mask comments and quoted content without changing source offsets. Shared by guards and pagination. */
8
+ export declare function lexWorkbenchSql(source: string): {
9
+ masked: string;
10
+ tokens: SqlToken[];
11
+ };
@@ -0,0 +1,3 @@
1
+ import type { SqlToken } from './workbench-sql-lexer';
2
+ /** Positions where name(...) declares columns or a built-in type, rather than invoking a function. */
3
+ export declare function sqlDeclarationPositions(tokens: SqlToken[]): Set<number>;
@@ -0,0 +1,3 @@
1
+ export * from './strategy.interface';
2
+ export * from './strategy.decorator';
3
+ export * from './strategy.registry';
@@ -0,0 +1,2 @@
1
+ export declare const KEYWORD_ANALYZER_STRATEGY = "KEYWORD_ANALYZER_STRATEGY";
2
+ export declare const KeywordAnalyzerStrategy: (provider: string) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
@@ -0,0 +1,11 @@
1
+ export interface IKeywordAnalyzerStrategy {
2
+ readonly meta: {
3
+ id: string;
4
+ label: string;
5
+ languages: string[];
6
+ /** Change whenever index/query compatibility changes, including dictionary updates. */
7
+ revision: string;
8
+ };
9
+ /** Return ordered lexemes. The host encodes them as data, never as SQL or tsquery syntax. */
10
+ analyze(text: string, purpose: 'index' | 'query'): Promise<readonly string[]>;
11
+ }
@@ -0,0 +1,8 @@
1
+ import { DiscoveryService, Reflector } from '@nestjs/core';
2
+ import { BaseStrategyRegistry, StrategySource } from '../../strategy';
3
+ import { IKeywordAnalyzerStrategy } from './strategy.interface';
4
+ export declare class KeywordAnalyzerRegistry extends BaseStrategyRegistry<IKeywordAnalyzerStrategy> {
5
+ constructor(discoveryService: DiscoveryService, reflector: Reflector);
6
+ /** Resolve a pinned source without allowing a higher-priority scope to shadow it. */
7
+ getBySource(provider: string, source: StrategySource, organizationId?: string): IKeywordAnalyzerStrategy | undefined;
8
+ }
@@ -5,3 +5,4 @@ export * from './retriever/index';
5
5
  export * from './types';
6
6
  export * from './image/index';
7
7
  export * from './knowledge/index';
8
+ export * from './analyzer/index';