@teleporthq/teleport-plugin-next-data-source 0.44.7 → 0.44.12
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/__tests__/items-path.test.ts +3 -2
- package/__tests__/search-relevance.test.ts +260 -0
- package/dist/cjs/fetchers/csv-file.d.ts.map +1 -1
- package/dist/cjs/fetchers/csv-file.js +4 -3
- package/dist/cjs/fetchers/csv-file.js.map +1 -1
- package/dist/cjs/fetchers/firestore.js +1 -1
- package/dist/cjs/fetchers/firestore.js.map +1 -1
- package/dist/cjs/fetchers/google-sheets.d.ts.map +1 -1
- package/dist/cjs/fetchers/google-sheets.js +2 -1
- package/dist/cjs/fetchers/google-sheets.js.map +1 -1
- package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
- package/dist/cjs/fetchers/javascript.js +2 -1
- package/dist/cjs/fetchers/javascript.js.map +1 -1
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mariadb.js +4 -3
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
- package/dist/cjs/fetchers/mysql.js +3 -2
- package/dist/cjs/fetchers/mysql.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +3 -2
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +3 -2
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +3 -2
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
- package/dist/cjs/fetchers/supabase.js +3 -2
- package/dist/cjs/fetchers/supabase.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +4 -3
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/turso.d.ts.map +1 -1
- package/dist/cjs/fetchers/turso.js +2 -1
- package/dist/cjs/fetchers/turso.js.map +1 -1
- package/dist/cjs/search-relevance.d.ts +40 -0
- package/dist/cjs/search-relevance.d.ts.map +1 -0
- package/dist/cjs/search-relevance.js +46 -0
- package/dist/cjs/search-relevance.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/fetchers/csv-file.d.ts.map +1 -1
- package/dist/esm/fetchers/csv-file.js +4 -3
- package/dist/esm/fetchers/csv-file.js.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/google-sheets.d.ts.map +1 -1
- package/dist/esm/fetchers/google-sheets.js +2 -1
- package/dist/esm/fetchers/google-sheets.js.map +1 -1
- package/dist/esm/fetchers/javascript.d.ts.map +1 -1
- package/dist/esm/fetchers/javascript.js +2 -1
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
- package/dist/esm/fetchers/mariadb.js +4 -3
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mysql.d.ts.map +1 -1
- package/dist/esm/fetchers/mysql.js +3 -2
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +3 -2
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +3 -2
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
- package/dist/esm/fetchers/rest-api.js +3 -2
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/supabase.d.ts.map +1 -1
- package/dist/esm/fetchers/supabase.js +3 -2
- package/dist/esm/fetchers/supabase.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +4 -3
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +2 -1
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/search-relevance.d.ts +40 -0
- package/dist/esm/search-relevance.d.ts.map +1 -0
- package/dist/esm/search-relevance.js +40 -0
- package/dist/esm/search-relevance.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/fetchers/csv-file.ts +16 -5
- package/src/fetchers/firestore.ts +2 -2
- package/src/fetchers/google-sheets.ts +14 -5
- package/src/fetchers/javascript.ts +12 -5
- package/src/fetchers/mariadb.ts +21 -26
- package/src/fetchers/mysql.ts +21 -26
- package/src/fetchers/postgresql.ts +24 -24
- package/src/fetchers/redshift.ts +18 -17
- package/src/fetchers/rest-api.ts +12 -5
- package/src/fetchers/supabase.ts +27 -20
- package/src/fetchers/teleport.ts +24 -24
- package/src/fetchers/turso.ts +20 -19
- package/src/search-relevance.ts +96 -0
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateMariaDBCountFetcher = exports.generateMariaDBFetcher = void 0;
|
|
4
4
|
var utils_1 = require("../utils");
|
|
5
|
+
var search_relevance_1 = require("../search-relevance");
|
|
5
6
|
var generateMariaDBFetcher = function (config, tableName) {
|
|
6
7
|
var mariaConfig = config;
|
|
7
8
|
var database = mariaConfig.database;
|
|
8
|
-
return "import mariadb from 'mariadb'\n\n".concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n\n// Helper function to process filters and build conditions\nconst processFilters = (filters, conditions, queryParams) => {\n if (!filters) return\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return\n \n const buildCondition = (condition) => {\n const field = `\\`${condition.source}\\``\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n const placeholders = value.map(() => '?').join(', ')\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders})`\n : `${field} IN (${placeholders})`\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n // Validate operator to prevent SQL injection\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n queryParams.push(value)\n return `${field} ${sqlOperator} ?`\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n let connection = null\n try {\n connection = await mariadb.createConnection({\n host: ").concat(JSON.stringify(mariaConfig.host), ",\n port: ").concat(mariaConfig.port || 3306, ",\n user: ").concat(JSON.stringify(mariaConfig.user), ",\n password: ").concat((0, utils_1.replaceSecretReference)(mariaConfig.password), ",\n database: ").concat(JSON.stringify(mariaConfig.database), ",\n ssl: ").concat(mariaConfig.ssl || false).concat(mariaConfig.sslConfig
|
|
9
|
+
return "import mariadb from 'mariadb'\n\n".concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, search_relevance_1.generateSearchSqlHelpersCode)(search_relevance_1.MARIADB_SEARCH_MATCH_SQL), "\n\n// Helper function to process filters and build conditions\nconst processFilters = (filters, conditions, queryParams) => {\n if (!filters) return\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return\n \n const buildCondition = (condition) => {\n const field = `\\`${condition.source}\\``\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n const placeholders = value.map(() => '?').join(', ')\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders})`\n : `${field} IN (${placeholders})`\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n // Validate operator to prevent SQL injection\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n queryParams.push(value)\n return `${field} ${sqlOperator} ?`\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n let connection = null\n try {\n connection = await mariadb.createConnection({\n host: ").concat(JSON.stringify(mariaConfig.host), ",\n port: ").concat(mariaConfig.port || 3306, ",\n user: ").concat(JSON.stringify(mariaConfig.user), ",\n password: ").concat((0, utils_1.replaceSecretReference)(mariaConfig.password), ",\n database: ").concat(JSON.stringify(mariaConfig.database), ",\n ssl: ").concat(mariaConfig.ssl || false).concat(mariaConfig.sslConfig
|
|
9
10
|
? ",\n sslConfig: {\n ".concat(mariaConfig.sslConfig.ca ? "ca: ".concat((0, utils_1.replaceSecretReference)(mariaConfig.sslConfig.ca), ",") : '', "\n ").concat(mariaConfig.sslConfig.cert
|
|
10
11
|
? "cert: ".concat((0, utils_1.replaceSecretReference)(mariaConfig.sslConfig.cert), ",")
|
|
11
12
|
: '', "\n ").concat(mariaConfig.sslConfig.key
|
|
12
13
|
? "key: ".concat((0, utils_1.replaceSecretReference)(mariaConfig.sslConfig.key), ",")
|
|
13
14
|
: '', "\n rejectUnauthorized: ").concat(mariaConfig.sslConfig.rejectUnauthorized !== false, "\n }")
|
|
14
|
-
: '', "\n })\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so a single column\n // passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaRows = await connection.query(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [").concat(JSON.stringify(database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n
|
|
15
|
+
: '', "\n })\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n let search = null\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so a single column\n // passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaRows = await connection.query(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [").concat(JSON.stringify(database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n search = buildSearchSql(query, columns)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n }\n }\n\n // Apply filters using helper function\n processFilters(filters, conditions, queryParams)\n \n let sql = `SELECT * FROM \\`").concat(tableName, "\\``\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. A search orders its matches by where\n // the words are first; the sort orders the rows within each of those groups.\n const orderClauses = search && search.rank ? [search.rank] : []\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n orderClauses.push(...parsedSorts.map((sort) => {\n if (!sort.field) return null\n const order = (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n return `\\`${sort.field}\\` ${order}`\n }).filter(Boolean))\n }\n } else if (sortBy) {\n orderClauses.push(`\\`${sortBy}\\` ${(sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'}`)\n }\n if (orderClauses.length > 0) {\n sql += ` ORDER BY ${orderClauses.join(', ')}`\n if (search && search.rank) queryParams.push(...search.rankParams)\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n if (limitValue) {\n sql += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sql += ` OFFSET ${offsetValue}`\n }\n \n const rows = await connection.query(sql, queryParams)\n const rowArray = Array.isArray(rows) ? rows : []\n const plainRows = rowArray.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n \n return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('MariaDB fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (connection) {\n try {\n await connection.end()\n } catch (error) {\n console.error('Error closing MariaDB connection:', error)\n }\n }\n }\n}\n");
|
|
15
16
|
};
|
|
16
17
|
exports.generateMariaDBFetcher = generateMariaDBFetcher;
|
|
17
18
|
var generateMariaDBCountFetcher = function (config, tableName) {
|
|
@@ -23,7 +24,7 @@ var generateMariaDBCountFetcher = function (config, tableName) {
|
|
|
23
24
|
: '', "\n ").concat(mariaConfig.sslConfig.key
|
|
24
25
|
? "key: ".concat((0, utils_1.replaceSecretReference)(mariaConfig.sslConfig.key), ",")
|
|
25
26
|
: '', "\n rejectUnauthorized: ").concat(mariaConfig.sslConfig.rejectUnauthorized !== false, "\n }")
|
|
26
|
-
: '', "\n })\n \n const { query, queryColumns, filters } = req.query\n const conditions = []\n const queryParams = []\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaRows = await connection.query(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [").concat(JSON.stringify(database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n
|
|
27
|
+
: '', "\n })\n \n const { query, queryColumns, filters } = req.query\n const conditions = []\n const queryParams = []\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaRows = await connection.query(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [").concat(JSON.stringify(database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n const search = buildSearchSql(query, columns)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n }\n }\n\n // Apply filters using helper function\n processFilters(filters, conditions, queryParams)\n\n let countSql = `SELECT COUNT(*) as count FROM ").concat(tableName, "`\n if (conditions.length > 0) {\n countSql += ` WHERE ${conditions.join(' AND ')}`\n }\n\n const rows = await connection.query(countSql, queryParams)\n const count = rows[0].count\n\n return res.status(200).json({\n success: true,\n count: count,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to get count',\n timestamp: Date.now()\n })\n } finally {\n if (connection) {\n try {\n await connection.end()\n } catch (error) {\n console.error('Error closing MariaDB connection:', error)\n }\n }\n }\n}\n");
|
|
27
28
|
};
|
|
28
29
|
exports.generateMariaDBCountFetcher = generateMariaDBCountFetcher;
|
|
29
30
|
//# sourceMappingURL=mariadb.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb.js","sourceRoot":"","sources":["../../../src/fetchers/mariadb.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;
|
|
1
|
+
{"version":3,"file":"mariadb.js","sourceRoot":"","sources":["../../../src/fetchers/mariadb.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../src/fetchers/mysql.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../src/fetchers/mysql.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,oBAAoB,WACvB,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA2MF,CAAA;AAED,eAAO,MAAM,yBAAyB,WAC5B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA4EF,CAAA"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateMySQLCountFetcher = exports.generateMySQLFetcher = void 0;
|
|
4
4
|
var utils_1 = require("../utils");
|
|
5
|
+
var search_relevance_1 = require("../search-relevance");
|
|
5
6
|
var generateMySQLFetcher = function (config, tableName) {
|
|
6
7
|
var mysqlConfig = config;
|
|
7
8
|
var resolvedUser = mysqlConfig.user || mysqlConfig.username || null;
|
|
@@ -19,12 +20,12 @@ var generateMySQLFetcher = function (config, tableName) {
|
|
|
19
20
|
: defaultSSLEnabled
|
|
20
21
|
? "{ rejectUnauthorized: true }"
|
|
21
22
|
: 'false';
|
|
22
|
-
return "import mysql from 'mysql2/promise'\n\nconst getConnection = () => {\n return mysql.createConnection({\n host: ".concat(JSON.stringify(mysqlConfig.host), ",\n port: ").concat(mysqlConfig.port || 3306, ",\n user: ").concat(resolvedUser !== null ? JSON.stringify(resolvedUser) : 'undefined', ",\n password: ").concat((0, utils_1.replaceSecretReference)(mysqlConfig.password), ",\n database: ").concat(JSON.stringify(mysqlConfig.database), ",\n ssl: ").concat(sslConfigString, "\n })\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n\n// Helper function to process filters and build conditions\nconst processFilters = (filters, conditions, queryParams) => {\n if (!filters) return\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return\n \n const buildCondition = (condition) => {\n const field = mysql.escapeId(condition.source)\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n const placeholders = value.map(() => '?').join(', ')\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders})`\n : `${field} IN (${placeholders})`\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n // Validate operator to prevent SQL injection\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n queryParams.push(value)\n return `${field} ${sqlOperator} ?`\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n const connection = await getConnection()\n \n try {\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so a single column\n // passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const [schemaRows] = await connection.query(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [").concat(JSON.stringify(database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n
|
|
23
|
+
return "import mysql from 'mysql2/promise'\n\nconst getConnection = () => {\n return mysql.createConnection({\n host: ".concat(JSON.stringify(mysqlConfig.host), ",\n port: ").concat(mysqlConfig.port || 3306, ",\n user: ").concat(resolvedUser !== null ? JSON.stringify(resolvedUser) : 'undefined', ",\n password: ").concat((0, utils_1.replaceSecretReference)(mysqlConfig.password), ",\n database: ").concat(JSON.stringify(mysqlConfig.database), ",\n ssl: ").concat(sslConfigString, "\n })\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, search_relevance_1.generateSearchSqlHelpersCode)(search_relevance_1.MYSQL_SEARCH_MATCH_SQL), "\n\n// Helper function to process filters and build conditions\nconst processFilters = (filters, conditions, queryParams) => {\n if (!filters) return\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return\n \n const buildCondition = (condition) => {\n const field = mysql.escapeId(condition.source)\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n const placeholders = value.map(() => '?').join(', ')\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders})`\n : `${field} IN (${placeholders})`\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n // Validate operator to prevent SQL injection\n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n queryParams.push(value)\n return `${field} ${sqlOperator} ?`\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n const connection = await getConnection()\n \n try {\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n let search = null\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so a single column\n // passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const [schemaRows] = await connection.query(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [").concat(JSON.stringify(database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n search = buildSearchSql(query, columns)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n }\n }\n\n // Apply filters using helper function\n processFilters(filters, conditions, queryParams)\n \n let sql = `SELECT * FROM ${mysql.escapeId('").concat(tableName, "')}`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. A search orders its matches by where\n // the words are first; the sort orders the rows within each of those groups.\n const orderClauses = search && search.rank ? [search.rank] : []\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n orderClauses.push(...parsedSorts.map((sort) => {\n if (!sort.field) return null\n const order = (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n return `${mysql.escapeId(sort.field)} ${order}`\n }).filter(Boolean))\n }\n } else if (sortBy) {\n orderClauses.push(`${mysql.escapeId(sortBy)} ${(sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'}`)\n }\n if (orderClauses.length > 0) {\n sql += ` ORDER BY ${orderClauses.join(', ')}`\n if (search && search.rank) queryParams.push(...search.rankParams)\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n if (limitValue) {\n sql += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sql += ` OFFSET ${offsetValue}`\n }\n \n const [rows] = await connection.query(sql, queryParams)\n const rowArray = Array.isArray(rows) ? rows : []\n const plainRows = rowArray.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n\n return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('MySQL fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (connection) {\n try {\n await connection.end()\n } catch (error) {\n console.error('Error closing MySQL connection:', error)\n }\n }\n }\n}\n");
|
|
23
24
|
};
|
|
24
25
|
exports.generateMySQLFetcher = generateMySQLFetcher;
|
|
25
26
|
var generateMySQLCountFetcher = function (config, tableName) {
|
|
26
27
|
var mysqlConfig = config;
|
|
27
|
-
return "\nasync function getCount(req, res) {\n const connection = await getConnection()\n\n try {\n const { query, queryColumns, filters } = req.query\n const conditions = []\n const queryParams = []\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const [schemaRows] = await connection.execute(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [".concat(JSON.stringify(mysqlConfig.database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n
|
|
28
|
+
return "\nasync function getCount(req, res) {\n const connection = await getConnection()\n\n try {\n const { query, queryColumns, filters } = req.query\n const conditions = []\n const queryParams = []\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const [schemaRows] = await connection.execute(\n `SELECT COLUMN_NAME FROM information_schema.COLUMNS \n WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? \n ORDER BY ORDINAL_POSITION`,\n [".concat(JSON.stringify(mysqlConfig.database), ", ").concat(JSON.stringify(tableName), "]\n )\n columns = schemaRows.map(row => row.COLUMN_NAME)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n const search = buildSearchSql(query, columns)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n }\n }\n\n // Apply filters using helper function\n processFilters(filters, conditions, queryParams)\n\n let countSql = `SELECT COUNT(*) as count FROM ").concat(tableName, "`\n if (conditions.length > 0) {\n countSql += ` WHERE ${conditions.join(' AND ')}`\n }\n\n const [rows] = await connection.query(countSql, queryParams)\n const count = rows[0].count\n\n return res.status(200).json({\n success: true,\n count: count,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to get count',\n timestamp: Date.now()\n })\n } finally {\n if (connection) {\n try {\n await connection.end()\n } catch (error) {\n console.error('Error closing MySQL connection:', error)\n }\n }\n }\n}\n");
|
|
28
29
|
};
|
|
29
30
|
exports.generateMySQLCountFetcher = generateMySQLCountFetcher;
|
|
30
31
|
//# sourceMappingURL=mysql.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../../src/fetchers/mysql.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;
|
|
1
|
+
{"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../../src/fetchers/mysql.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;AACjB,wDAA0F;AAanF,IAAM,oBAAoB,GAAG,UAClC,MAA+B,EAC/B,SAAiB;IAEjB,IAAM,WAAW,GAAG,MAAqB,CAAA;IACzC,IAAM,YAAY,GAAG,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,QAAQ,IAAI,IAAI,CAAA;IACrE,IAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,SAAS,CAAA;IAClD,IAAM,iBAAiB,GAAG,WAAW,CAAC,GAAG,KAAK,KAAK,CAAA;IACnD,IAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAA;IAErC,IAAM,eAAe,GAAG,kBAAkB;QACxC,CAAC,CAAC,mBACE,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,cAAO,IAAA,8BAAsB,EAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,qBAE1F,WAAW,CAAC,SAAS,CAAC,IAAI;YACxB,CAAC,CAAC,gBAAS,IAAA,8BAAsB,EAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,MAAG;YAChE,CAAC,CAAC,EAAE,qBAGN,WAAW,CAAC,SAAS,CAAC,GAAG;YACvB,CAAC,CAAC,eAAQ,IAAA,8BAAsB,EAAC,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,MAAG;YAC9D,CAAC,CAAC,EAAE,yCAGN,WAAW,CAAC,SAAS,CAAC,kBAAkB,KAAK,SAAS;YACpD,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,kBAAkB;YAC1C,CAAC,CAAC,IAAI,YAEV;QACF,CAAC,CAAC,iBAAiB;YACnB,CAAC,CAAC,8BAA8B;YAChC,CAAC,CAAC,OAAO,CAAA;IAEX,OAAO,4HAIG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,0BAChC,WAAW,CAAC,IAAI,IAAI,IAAI,0BACxB,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,WAAW,8BAC9D,IAAA,8BAAsB,EAAC,WAAW,CAAC,QAAQ,CAAC,8BAC5C,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,yBACzC,eAAe,0BAIxB,IAAA,iCAAyB,GAAE,iBAE3B,IAAA,qCAA6B,GAAE,iBAE/B,IAAA,uCAA+B,GAAE,eACjC,IAAA,+CAA4B,EAAC,yCAAsB,CAAC,u6CA0CpD,IAAA,iCAAyB,GAAE,q+BA2Bd,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,eAAK,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,0nBAmBlB,SAAS,svEAkE3D,CAAA;AACD,CAAC,CAAA;AA9MY,QAAA,oBAAoB,wBA8MhC;AAEM,IAAM,yBAAyB,GAAG,UACvC,MAA+B,EAC/B,SAAiB;IAEjB,IAAM,WAAW,GAAG,MAAqB,CAAA;IAEzC,OAAO,kwBAuBM,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,eAAK,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,+nBAmB5B,SAAS,ouBA8B7D,CAAA;AACD,CAAC,CAAA;AA/EY,QAAA,yBAAyB,6BA+ErC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresql.d.ts","sourceRoot":"","sources":["../../../src/fetchers/postgresql.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"postgresql.d.ts","sourceRoot":"","sources":["../../../src/fetchers/postgresql.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,yBAAyB,WAC5B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,uBACG,cAAc,MAAM,CAAC,KACxC,MA2SF,CAAA;AAED,eAAO,MAAM,8BAA8B,WACjC,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA4FF,CAAA"}
|
|
@@ -7,6 +7,7 @@ var list_sort_order_1 = require("../list-sort-order");
|
|
|
7
7
|
var row_order_1 = require("../row-order");
|
|
8
8
|
var product_filter_fields_1 = require("../product-filter-fields");
|
|
9
9
|
var table_access_guard_1 = require("./utils/table-access-guard");
|
|
10
|
+
var search_relevance_1 = require("../search-relevance");
|
|
10
11
|
var request_sql_guards_1 = require("./utils/request-sql-guards");
|
|
11
12
|
var array_overlap_sql_1 = require("../array-overlap-sql");
|
|
12
13
|
var pg_client_import_1 = require("./pg-client-import");
|
|
@@ -22,7 +23,7 @@ var generatePostgreSQLFetcher = function (config, tableName, trustedReaderRoles)
|
|
|
22
23
|
: pgConfig.sslConfig
|
|
23
24
|
? "{\n ".concat(pgConfig.sslConfig.ca ? "ca: ".concat((0, utils_1.replaceSecretReference)(pgConfig.sslConfig.ca), ",") : '', "\n ").concat(pgConfig.sslConfig.cert ? "cert: ".concat((0, utils_1.replaceSecretReference)(pgConfig.sslConfig.cert), ",") : '', "\n ").concat(pgConfig.sslConfig.key ? "key: ".concat((0, utils_1.replaceSecretReference)(pgConfig.sslConfig.key), ",") : '', "\n rejectUnauthorized: false\n }")
|
|
24
25
|
: '{ rejectUnauthorized: false }', "\n }");
|
|
25
|
-
return "".concat(pg_client_import_1.PG_CLIENT_IMPORT, "\n\n").concat((0, table_access_guard_1.generateTableAccessPreamble)(trustedReaderRoles), "\n\nconst getClient = () => {\n return new Client(").concat(clientConfig, ")\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, array_overlap_sql_1.generateArrayOverlapSqlCode)(), "\n\n// Builds one SQL clause for the whole filter tree, so an OR group nested in\n// the root AND keeps its meaning instead of being flattened into ANDs.\n").concat((0, request_sql_guards_1.generateRequestSqlGuardsCode)(), "\nconst processFilters = (filters, conditions, queryParams, paramIndex) => {\n if (!filters) return paramIndex\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return paramIndex\n \n const buildCondition = (condition) => {\n const field = condition.source\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n // The products table's virtual fields (effective price, rating bucket,\n // on-sale flag, variant axes) are whole clauses, not columns - so they are\n // answered before any branch below can interpolate the field name raw.\n const productClause = productFilterClause(field, operand, value, (param) => {\n queryParams.push(param)\n return '$' + paramIndex++\n })\n if (productClause !== null) return productClause\n assertRequestColumn(field, 'filter field')\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n if (operand === 'array_overlap') {\n // The row's list (see array-overlap-sql.ts) shares any value with the set.\n const clause = arrayOverlapSql([field], '$' + paramIndex)\n queryParams.push(value.map((entry) => String(entry)))\n paramIndex++\n return clause\n }\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders.join(', ')})`\n : `${field} IN (${placeholders.join(', ')})`\n }\n \n if (operand === 'array_overlap') {\n if (value === '' || value === null || value === undefined) return null\n // A single comma-joined string (the multi-select Category Filter's\n // ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.\n const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)\n if (overlapValues.length === 0) return null\n const clause = arrayOverlapSql([field], '$' + paramIndex)\n queryParams.push(overlapValues)\n paramIndex++\n return clause\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n const clause = `${field} ${sqlOperator} $${paramIndex}`\n queryParams.push(value)\n paramIndex++\n return clause\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n \n return paramIndex\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\n").concat((0, product_price_sort_1.generateSortFieldSqlHelper)(tableName), "\n\n").concat((0, product_price_sort_1.generateSortFallbackFieldHelper)(tableName), "\n\n").concat((0, row_order_1.generateRowOrderTiebreakHelper)((0, row_order_1.rowIdentityColumn)(config, tableName)), "\n\n").concat((0, product_filter_fields_1.generateProductFilterClauseHelper)(tableName), "\n\nexport default async function handler(req, res) {\n").concat((0, table_access_guard_1.generateReadGuardCall)(tableName, null), "\n const client = getClient()\n \n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n // A visitor never filters, sorts or searches by a column it is not served.\n if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so that a single\n // column passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaQuery = `\n SELECT column_name\n FROM information_schema.columns\n WHERE table_name = $1\n ").concat(schema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = schema \n ? [").concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(schema), "]\n : [").concat(JSON.stringify(tableName), "]\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n if (columns.length > 0) {\n const pattern = '%' + escapeLikePattern(query) + '%'\n const placeholder = '$' + paramIndex\n paramIndex++\n queryParams.push(pattern)\n const searchConditions = columns.map(\n (col) => '\"' + sanitizeSearchIdentifier(col) + '\"::text ILIKE ' + placeholder + \" ESCAPE '|'\"\n )\n conditions.push('(' + searchConditions.join(' OR ') + ')')\n }\n }\n\n // Apply filters using helper function\n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')\n \n let sql = `SELECT * FROM \"").concat(tableName, "\"`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. Two ORDER BY clauses are built: the one\n // actually used (the products table's price rewrites, rows without a value\n // last), and a plain-column twin kept only as the fallback below.\n let orderBySql = ''\n let plainOrderBySql = ''\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const valid = parsedSorts.filter((sort) => sort && sort.field)\n valid.forEach((sort) => assertRequestColumn(sort.field, 'sort field'))\n const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const orderClauses = valid.map((sort) => `${sortFieldSql(sort.field)} ${orderOf(sort)}").concat(list_sort_order_1.SORT_NULLS_LAST_SQL, "`)\n const plainClauses = valid.map((sort) => `${sortFallbackField(sort.field)} ${orderOf(sort)}`)\n\n if (orderClauses.length > 0) {\n // The row's own key last, so equal rows can't repeat across pages\n // or vanish between them.\n const tiebreak = rowOrderTiebreak(valid.map((sort) => sort.field), orderOf(valid[valid.length - 1]))\n orderBySql = ` ORDER BY ${orderClauses.join(', ')}${tiebreak}`\n plainOrderBySql = ` ORDER BY ${plainClauses.join(', ')}${tiebreak}`\n }\n }\n } else if (sortBy) {\n assertRequestColumn(sortBy, 'sort field')\n const sortByDirection = (sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const tiebreak = rowOrderTiebreak([sortBy], sortByDirection)\n orderBySql = ` ORDER BY ${sortBy} ${sortByDirection}").concat(list_sort_order_1.SORT_NULLS_LAST_SQL, "${tiebreak}`\n plainOrderBySql = ` ORDER BY ${sortBy} ${sortByDirection}${tiebreak}`\n }\n const usedRewrittenSort = orderBySql !== plainOrderBySql\n\n const limitValue = requestRowCount(limit || perPage)\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n\n let sqlTail = ''\n if (limitValue !== undefined) {\n sqlTail += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sqlTail += ` OFFSET ${offsetValue}`\n }\n\n const baseSql = sql\n sql = baseSql + orderBySql + sqlTail\n \n // The discount-aware price ordering is an inline sub-select over a JSON\n // column. It is written to be unraisable, but it runs inside ORDER BY for\n // the whole table \u2014 so if a database ever rejects it, fall back to ordering\n // by the stored list price rather than serving an empty products page. The\n // empty-values-last ordering takes the same exit, for a Postgres-compatible\n // database that does not know NULLS LAST.\n let result\n try {\n result = await client.query(sql, queryParams)\n } catch (sortError) {\n if (!usedRewrittenSort) throw sortError\n console.warn(\n 'Rewritten sort failed; falling back to the plain column:',\n sortError && sortError.message\n )\n result = await client.query(baseSql + plainOrderBySql + sqlTail, queryParams)\n }\n const rows = Array.isArray(result?.rows) ? result.rows : []\n const plainRows = rows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n\n return res.status(200).json({\n success: true,\n data: __taWithoutCredentials(\n __rowPolicy ? __brpStripHidden(__rowPolicy, safeData) : safeData,\n ").concat(JSON.stringify(tableName), "\n ),\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('PostgreSQL fetch error:', error)\n // A database error names tables, columns and values: it stays in the server\n // log. Only an error this route raised for the caller (it carries a status)\n // is repeated to it.\n return res.status(error.status || 500).json({\n success: false,\n error: error.status ? error.message : 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing PostgreSQL client:', error)\n }\n }\n }\n}\n");
|
|
26
|
+
return "".concat(pg_client_import_1.PG_CLIENT_IMPORT, "\n\n").concat((0, table_access_guard_1.generateTableAccessPreamble)(trustedReaderRoles), "\n\nconst getClient = () => {\n return new Client(").concat(clientConfig, ")\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, search_relevance_1.generateSearchSqlHelpersCode)(search_relevance_1.PG_SEARCH_MATCH_SQL), "\n").concat((0, array_overlap_sql_1.generateArrayOverlapSqlCode)(), "\n\n// Builds one SQL clause for the whole filter tree, so an OR group nested in\n// the root AND keeps its meaning instead of being flattened into ANDs.\n").concat((0, request_sql_guards_1.generateRequestSqlGuardsCode)(), "\nconst processFilters = (filters, conditions, queryParams, paramIndex) => {\n if (!filters) return paramIndex\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return paramIndex\n \n const buildCondition = (condition) => {\n const field = condition.source\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n // The products table's virtual fields (effective price, rating bucket,\n // on-sale flag, variant axes) are whole clauses, not columns - so they are\n // answered before any branch below can interpolate the field name raw.\n const productClause = productFilterClause(field, operand, value, (param) => {\n queryParams.push(param)\n return '$' + paramIndex++\n })\n if (productClause !== null) return productClause\n assertRequestColumn(field, 'filter field')\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n if (operand === 'array_overlap') {\n // The row's list (see array-overlap-sql.ts) shares any value with the set.\n const clause = arrayOverlapSql([field], '$' + paramIndex)\n queryParams.push(value.map((entry) => String(entry)))\n paramIndex++\n return clause\n }\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders.join(', ')})`\n : `${field} IN (${placeholders.join(', ')})`\n }\n \n if (operand === 'array_overlap') {\n if (value === '' || value === null || value === undefined) return null\n // A single comma-joined string (the multi-select Category Filter's\n // ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.\n const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)\n if (overlapValues.length === 0) return null\n const clause = arrayOverlapSql([field], '$' + paramIndex)\n queryParams.push(overlapValues)\n paramIndex++\n return clause\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n const clause = `${field} ${sqlOperator} $${paramIndex}`\n queryParams.push(value)\n paramIndex++\n return clause\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n \n return paramIndex\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\n").concat((0, product_price_sort_1.generateSortFieldSqlHelper)(tableName), "\n\n").concat((0, product_price_sort_1.generateSortFallbackFieldHelper)(tableName), "\n\n").concat((0, row_order_1.generateRowOrderTiebreakHelper)((0, row_order_1.rowIdentityColumn)(config, tableName)), "\n\n").concat((0, product_filter_fields_1.generateProductFilterClauseHelper)(tableName), "\n\nexport default async function handler(req, res) {\n").concat((0, table_access_guard_1.generateReadGuardCall)(tableName, null), "\n const client = getClient()\n \n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n // A visitor never filters, sorts or searches by a column it is not served.\n if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n let searchRank = ''\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so that a single\n // column passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaQuery = `\n SELECT column_name\n FROM information_schema.columns\n WHERE table_name = $1\n ").concat(schema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = schema \n ? [").concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(schema), "]\n : [").concat(JSON.stringify(tableName), "]\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n const search = buildSearchSql(query, columns, paramIndex)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n paramIndex = search.nextIndex\n searchRank = search.rank\n }\n }\n\n // Apply filters using helper function\n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')\n \n let sql = `SELECT * FROM \"").concat(tableName, "\"`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. Two ORDER BY clauses are built: the one\n // actually used (the products table's price rewrites, rows without a value\n // last), and a plain-column twin kept only as the fallback below.\n // A search orders its matches by where the words are first; the sort\n // orders the rows within each of those groups.\n let orderBySql = ''\n let plainOrderBySql = ''\n const searchOrder = searchRank ? searchRank + ', ' : ''\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const valid = parsedSorts.filter((sort) => sort && sort.field)\n valid.forEach((sort) => assertRequestColumn(sort.field, 'sort field'))\n const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const orderClauses = valid.map((sort) => `${sortFieldSql(sort.field)} ${orderOf(sort)}").concat(list_sort_order_1.SORT_NULLS_LAST_SQL, "`)\n const plainClauses = valid.map((sort) => `${sortFallbackField(sort.field)} ${orderOf(sort)}`)\n\n if (orderClauses.length > 0) {\n // The row's own key last, so equal rows can't repeat across pages\n // or vanish between them.\n const tiebreak = rowOrderTiebreak(valid.map((sort) => sort.field), orderOf(valid[valid.length - 1]))\n orderBySql = ` ORDER BY ${searchOrder}${orderClauses.join(', ')}${tiebreak}`\n plainOrderBySql = ` ORDER BY ${searchOrder}${plainClauses.join(', ')}${tiebreak}`\n }\n }\n } else if (sortBy) {\n assertRequestColumn(sortBy, 'sort field')\n const sortByDirection = (sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const tiebreak = rowOrderTiebreak([sortBy], sortByDirection)\n orderBySql = ` ORDER BY ${searchOrder}${sortBy} ${sortByDirection}").concat(list_sort_order_1.SORT_NULLS_LAST_SQL, "${tiebreak}`\n plainOrderBySql = ` ORDER BY ${searchOrder}${sortBy} ${sortByDirection}${tiebreak}`\n }\n if (!orderBySql && searchRank) {\n orderBySql = plainOrderBySql = ' ORDER BY ' + searchRank + rowOrderTiebreak([], 'ASC')\n }\n const usedRewrittenSort = orderBySql !== plainOrderBySql\n\n const limitValue = requestRowCount(limit || perPage)\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n\n let sqlTail = ''\n if (limitValue !== undefined) {\n sqlTail += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sqlTail += ` OFFSET ${offsetValue}`\n }\n\n const baseSql = sql\n sql = baseSql + orderBySql + sqlTail\n \n // The discount-aware price ordering is an inline sub-select over a JSON\n // column. It is written to be unraisable, but it runs inside ORDER BY for\n // the whole table \u2014 so if a database ever rejects it, fall back to ordering\n // by the stored list price rather than serving an empty products page. The\n // empty-values-last ordering takes the same exit, for a Postgres-compatible\n // database that does not know NULLS LAST.\n let result\n try {\n result = await client.query(sql, queryParams)\n } catch (sortError) {\n if (!usedRewrittenSort) throw sortError\n console.warn(\n 'Rewritten sort failed; falling back to the plain column:',\n sortError && sortError.message\n )\n result = await client.query(baseSql + plainOrderBySql + sqlTail, queryParams)\n }\n const rows = Array.isArray(result?.rows) ? result.rows : []\n const plainRows = rows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n\n return res.status(200).json({\n success: true,\n data: __taWithoutCredentials(\n __rowPolicy ? __brpStripHidden(__rowPolicy, safeData) : safeData,\n ").concat(JSON.stringify(tableName), "\n ),\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('PostgreSQL fetch error:', error)\n // A database error names tables, columns and values: it stays in the server\n // log. Only an error this route raised for the caller (it carries a status)\n // is repeated to it.\n return res.status(error.status || 500).json({\n success: false,\n error: error.status ? error.message : 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing PostgreSQL client:', error)\n }\n }\n }\n}\n");
|
|
26
27
|
};
|
|
27
28
|
exports.generatePostgreSQLFetcher = generatePostgreSQLFetcher;
|
|
28
29
|
var generatePostgreSQLCountFetcher = function (config, tableName) {
|
|
@@ -31,7 +32,7 @@ var generatePostgreSQLCountFetcher = function (config, tableName) {
|
|
|
31
32
|
var hasSchema = !!((_a = pgConfig.options) === null || _a === void 0 ? void 0 : _a.schema);
|
|
32
33
|
return "\nasync function getCount(req, res) {\n".concat((0, table_access_guard_1.generateReadGuardCall)(tableName, null), "\n const client = getClient()\n\n try {\n await client.connect()\n const { query, queryColumns, filters } = req.query\n if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)\n const conditions = []\n const queryParams = []\n let paramIndex = 1\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ").concat(hasSchema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(hasSchema
|
|
33
34
|
? "[".concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(pgConfig.options.schema), "]")
|
|
34
|
-
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n
|
|
35
|
+
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n const search = buildSearchSql(query, columns, paramIndex)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n paramIndex = search.nextIndex\n }\n }\n\n // Apply filters using helper function\n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')\n\n let countSql = `SELECT COUNT(*) FROM \"").concat(tableName, "\"`\n if (conditions.length > 0) {\n countSql += ` WHERE ${conditions.join(' AND ')}`\n }\n\n const result = await client.query(countSql, queryParams)\n const count = parseInt(result.rows[0].count, 10)\n\n return res.status(200).json({\n success: true,\n count: count,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(error.status || 500).json({\n success: false,\n error: error.status ? error.message : 'Failed to get count',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing PostgreSQL client:', error)\n }\n }\n }\n}\n");
|
|
35
36
|
};
|
|
36
37
|
exports.generatePostgreSQLCountFetcher = generatePostgreSQLCountFetcher;
|
|
37
38
|
//# sourceMappingURL=postgresql.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresql.js","sourceRoot":"","sources":["../../../src/fetchers/postgresql.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;AACjB,4DAAmG;AACnG,sDAAwD;AACxD,0CAAgF;AAChF,kEAA4E;AAC5E,iEAA+F;AAC/F,iEAAyE;AACzE,0DAAkE;AAClE,uDAAqD;AAe9C,IAAM,yBAAyB,GAAG,UACvC,MAA+B,EAC/B,SAAiB,EACjB,kBAA8C;;IAA9C,mCAAA,EAAA,uBAA8C;IAE9C,IAAM,QAAQ,GAAG,MAA0B,CAAA;IAC3C,IAAM,MAAM,GAAG,MAAA,QAAQ,CAAC,OAAO,0CAAE,MAAM,CAAA;IAEvC,IAAM,YAAY,GAAG,QAAQ,CAAC,gBAAgB;QAC5C,CAAC,CAAC,mCACkB,IAAA,8BAAsB,EAAC,QAAQ,CAAC,gBAAgB,CAAC,WACrE;QACA,CAAC,CAAC,uBACM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,0BAC7B,QAAQ,CAAC,IAAI,IAAI,IAAI,0BACrB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,QAAQ,CAAC,8BAC9C,IAAA,8BAAsB,EAAC,QAAQ,CAAC,QAAQ,CAAC,8BACzC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,yBAE3C,QAAQ,CAAC,GAAG,KAAK,KAAK;YACpB,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,QAAQ,CAAC,SAAS;gBACpB,CAAC,CAAC,mBACF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,cAAO,IAAA,8BAAsB,EAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,qBACpF,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAS,IAAA,8BAAsB,EAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,qBAC1F,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,eAAQ,IAAA,8BAAsB,EAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,6CAEzF;gBACE,CAAC,CAAC,+BAA+B,UAErC,CAAA;IAEF,OAAO,UAAG,mCAAgB,iBAE1B,IAAA,gDAA2B,EAAC,kBAAkB,CAAC,gEAG3B,YAAY,qBAGhC,IAAA,iCAAyB,GAAE,iBAE3B,IAAA,qCAA6B,GAAE,iBAE/B,IAAA,uCAA+B,GAAE,eACjC,IAAA,+CAA2B,GAAE,wKAI7B,IAAA,iDAA4B,GAAE,+xFAwE9B,IAAA,iCAAyB,GAAE,iBAE3B,IAAA,+CAA0B,EAAC,SAAS,CAAC,iBAErC,IAAA,oDAA+B,EAAC,SAAS,CAAC,iBAE1C,IAAA,0CAA8B,EAAC,IAAA,6BAAiB,EAAC,MAAM,EAAE,SAAS,CAAC,CAAC,iBAEpE,IAAA,yDAAiC,EAAC,SAAS,CAAC,oEAG5C,IAAA,0CAAqB,EAAC,SAAS,EAAE,IAAI,CAAC,0FAKlC,MAAM,CAAC,CAAC,CAAC,iDAA0C,MAAM,OAAI,CAAC,CAAC,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"postgresql.js","sourceRoot":"","sources":["../../../src/fetchers/postgresql.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redshift.d.ts","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redshift.d.ts","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,uBAAuB,WAC1B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA6MF,CAAA"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateRedshiftFetcher = void 0;
|
|
4
4
|
var utils_1 = require("../utils");
|
|
5
|
+
var search_relevance_1 = require("../search-relevance");
|
|
5
6
|
var generateRedshiftFetcher = function (config, tableName) {
|
|
6
7
|
var _a;
|
|
7
8
|
var redshiftConfig = config;
|
|
@@ -18,9 +19,9 @@ var generateRedshiftFetcher = function (config, tableName) {
|
|
|
18
19
|
: sslConfig
|
|
19
20
|
? "{\n ".concat(sslConfig.ca ? "ca: ".concat((0, utils_1.replaceSecretReference)(sslConfig.ca), ",") : '', "\n ").concat(sslConfig.cert ? "cert: ".concat((0, utils_1.replaceSecretReference)(sslConfig.cert), ",") : '', "\n ").concat(sslConfig.key ? "key: ".concat((0, utils_1.replaceSecretReference)(sslConfig.key), ",") : '', "\n rejectUnauthorized: ").concat(sslConfig.rejectUnauthorized !== false, "\n }")
|
|
20
21
|
: '{ rejectUnauthorized: false }' // Default to SSL with no cert verification for Redshift
|
|
21
|
-
, "\n })\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n const client = getClient()\n \n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaQuery = 'SELECT column_name FROM information_schema.columns WHERE table_name = $1' + \n ").concat(schema ? "' AND table_schema = $2'" : "''", " + \n ' ORDER BY ordinal_position'\n const schemaParams = ").concat(schema
|
|
22
|
+
, "\n })\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, search_relevance_1.generateSearchSqlHelpersCode)(search_relevance_1.PG_SEARCH_MATCH_SQL), "\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n const client = getClient()\n \n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n \n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n let search = null\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get all columns from information_schema\n try {\n const schemaQuery = 'SELECT column_name FROM information_schema.columns WHERE table_name = $1' + \n ").concat(schema ? "' AND table_schema = $2'" : "''", " + \n ' ORDER BY ordinal_position'\n const schemaParams = ").concat(schema
|
|
22
23
|
? "[".concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(schema), "]")
|
|
23
|
-
: "[".concat(JSON.stringify(tableName), "]"), "\n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n \n
|
|
24
|
+
: "[".concat(JSON.stringify(tableName), "]"), "\n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n \n search = buildSearchSql(query, columns, paramIndex)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n paramIndex = search.nextIndex\n }\n }\n \n // Helper to sanitize identifier (prevent SQL injection in column names)\n const sanitizeIdentifier = (name) => {\n // Only allow alphanumeric, underscore, and dot (for schema.table)\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(`Invalid identifier: ${name}`)\n }\n return `\"${name}\"`\n }\n \n if (filters) {\n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n \n if (filterTree) {\n const buildCondition = (condition) => {\n const field = sanitizeIdentifier(condition.source)\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders.join(', ')})`\n : `${field} IN (${placeholders.join(', ')})`\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n const clause = `${field} ${sqlOperator} $${paramIndex}`\n queryParams.push(value)\n paramIndex++\n return clause\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n }\n }\n \n let sql = `SELECT * FROM ").concat(tableName, "`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. A search orders its matches by where\n // the words are first; the sort orders the rows within each of those groups.\n const orderClauses = search && search.rank ? [search.rank] : []\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n orderClauses.push(...parsedSorts.map((sort) => {\n if (!sort.field) return null\n const order = (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n return `${sanitizeIdentifier(sort.field)} ${order}`\n }).filter(Boolean))\n }\n } else if (sortBy) {\n orderClauses.push(`${sanitizeIdentifier(sortBy)} ${(sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'}`)\n }\n if (orderClauses.length > 0) {\n sql += ` ORDER BY ${orderClauses.join(', ')}`\n if (search && search.rank) queryParams.push(...search.rankParams)\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n if (limitValue) {\n sql += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sql += ` OFFSET ${offsetValue}`\n }\n \n const result = await client.query(sql, queryParams)\n const rows = Array.isArray(result?.rows) ? result.rows : []\n const plainRows = rows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n\n return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Redshift fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing Redshift client:', error)\n }\n }\n }\n}\n");
|
|
24
25
|
};
|
|
25
26
|
exports.generateRedshiftFetcher = generateRedshiftFetcher;
|
|
26
27
|
//# sourceMappingURL=redshift.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redshift.js","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;
|
|
1
|
+
{"version":3,"file":"redshift.js","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;AACjB,wDAAuF;AAahF,IAAM,uBAAuB,GAAG,UACrC,MAA+B,EAC/B,SAAiB;;IAEjB,IAAM,cAAc,GAAG,MAAwB,CAAA;IAC/C,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAA;IACxC,IAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAA;IACxC,IAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAA;IAC9B,IAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAA;IAC1C,IAAM,MAAM,GAAG,MAAA,cAAc,CAAC,OAAO,0CAAE,MAAM,CAAA;IAE7C,OAAO,qGAIG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,0BACpB,IAAI,IAAI,IAAI,0BACZ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,8BAChB,IAAA,8BAAsB,EAAC,QAAQ,CAAC,8BAChC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,yBAElC,GAAG,KAAK,KAAK;QACX,CAAC,CAAC,+BAA+B;QACjC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,mBACF,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,cAAO,IAAA,8BAAsB,EAAC,SAAS,CAAC,EAAE,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,qBAClE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAS,IAAA,8BAAsB,EAAC,SAAS,CAAC,IAAI,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,qBACxE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,eAAQ,IAAA,8BAAsB,EAAC,SAAS,CAAC,GAAG,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,yCACjD,SAAS,CAAC,kBAAkB,KAAK,KAAK,YAC5D;YACE,CAAC,CAAC,+BAA+B,CAAC,wDAAwD;8BAKhG,IAAA,iCAAyB,GAAE,iBAE3B,IAAA,qCAA6B,GAAE,iBAE/B,IAAA,uCAA+B,GAAE,eACjC,IAAA,+CAA4B,EAAC,sCAAmB,CAAC,iBAEjD,IAAA,iCAAyB,GAAE,+IAOvB,MAAM,CAAC,CAAC,CAAC,iDAA0C,MAAM,OAAI,CAAC,CAAC,CAAC,EAAE,2oBAmB1D,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,2FAG5C,MAAM;QACJ,CAAC,CAAC,WAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,eAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAG;QAC7D,CAAC,CAAC,WAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,MAAG,i7EAiEhB,SAAS,wvEAkExC,CAAA;AACD,CAAC,CAAA;AAhNY,QAAA,uBAAuB,2BAgNnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../../src/fetchers/rest-api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../../src/fetchers/rest-api.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,qBAAqB,WACxB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CA0BpC,CAAA;AAyED,eAAO,MAAM,sBAAsB,WAAY,OAAO,MAAM,EAAE,OAAO,CAAC,KAAG,MA+OxE,CAAA"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateRESTAPIFetcher = exports.validateRESTAPIConfig = void 0;
|
|
4
4
|
var utils_1 = require("../utils");
|
|
5
|
+
var search_relevance_1 = require("../search-relevance");
|
|
5
6
|
var rest_api_response_cache_1 = require("./utils/rest-api-response-cache");
|
|
6
7
|
var validateRESTAPIConfig = function (config) {
|
|
7
8
|
if (!config || typeof config !== 'object') {
|
|
@@ -80,7 +81,7 @@ var generateRESTAPIFetcher = function (config) {
|
|
|
80
81
|
// uncached data source keeps generating byte-identical output.
|
|
81
82
|
var cacheTTLSeconds = (0, rest_api_response_cache_1.resolveRESTAPICacheTTLSeconds)(restConfig.cache);
|
|
82
83
|
var cached = cacheTTLSeconds !== undefined;
|
|
83
|
-
return "import fetch from 'node-fetch'\n\n".concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\n").concat((0, utils_1.generateSortFilterHelperCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateCollectionPathHelperCode)()).concat(cached ? "\n\n".concat((0, rest_api_response_cache_1.generateRESTAPIResponseCacheCode)(cacheTTLSeconds)) : '', "\n\nexport default async function handler(req, res) {\n try {\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset, collectionPath, itemsPath } = req.query\n \n const url = ").concat(JSON.stringify(buildRequestURL(restConfig.url, restConfig.queryParams)), "\n const method = ").concat(JSON.stringify(restConfig.method || 'GET'), "\n \n const headers = ").concat(JSON.stringify(restConfig.headers || {}), "\n ").concat(authCode, "\n \n const options = {\n method,\n headers\n }\n \n ").concat(restConfig.method === 'POST' || restConfig.method === 'PUT' || restConfig.method === 'PATCH'
|
|
84
|
+
return "import fetch from 'node-fetch'\n\n".concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, search_relevance_1.generateSearchRankHelperCode)(), "\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\n").concat((0, utils_1.generateSortFilterHelperCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateCollectionPathHelperCode)()).concat(cached ? "\n\n".concat((0, rest_api_response_cache_1.generateRESTAPIResponseCacheCode)(cacheTTLSeconds)) : '', "\n\nexport default async function handler(req, res) {\n try {\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset, collectionPath, itemsPath } = req.query\n \n const url = ").concat(JSON.stringify(buildRequestURL(restConfig.url, restConfig.queryParams)), "\n const method = ").concat(JSON.stringify(restConfig.method || 'GET'), "\n \n const headers = ").concat(JSON.stringify(restConfig.headers || {}), "\n ").concat(authCode, "\n \n const options = {\n method,\n headers\n }\n \n ").concat(restConfig.method === 'POST' || restConfig.method === 'PUT' || restConfig.method === 'PATCH'
|
|
84
85
|
? "\n if (req.body) {\n options.body = ".concat(restConfig.bodyType === 'json' || !restConfig.bodyType
|
|
85
86
|
? 'JSON.stringify(req.body)'
|
|
86
87
|
: 'req.body', "\n }").concat(
|
|
@@ -92,7 +93,7 @@ var generateRESTAPIFetcher = function (config) {
|
|
|
92
93
|
restConfig.body
|
|
93
94
|
? " else {\n options.body = ".concat(JSON.stringify(restConfig.body), "\n }")
|
|
94
95
|
: '', "\n ")
|
|
95
|
-
: '', "\n \n const response = await ").concat(cached ? 'fetchRestApiWithCache' : 'fetch', "(url, options)\n \n if (!response.ok) {\n return res.status(response.status).json({\n success: false,\n error: `HTTP ${response.status}: ${response.statusText}`,\n timestamp: Date.now()\n })\n }\n \n let data = ").concat(cached ? 'JSON.parse(response.bodyText)' : 'await response.json()', "\n \n data = resolveCollectionPath(data, collectionPath)\n const itemsEnvelope = openItemsEnvelope(data, itemsPath)\n if (itemsEnvelope) {\n data = itemsEnvelope.items\n }\n \n if (Array.isArray(data)) {\n if (query && query.trim()) {\n const
|
|
96
|
+
: '', "\n \n const response = await ").concat(cached ? 'fetchRestApiWithCache' : 'fetch', "(url, options)\n \n if (!response.ok) {\n return res.status(response.status).json({\n success: false,\n error: `HTTP ${response.status}: ${response.statusText}`,\n timestamp: Date.now()\n })\n }\n \n let data = ").concat(cached ? 'JSON.parse(response.bodyText)' : 'await response.json()', "\n \n data = resolveCollectionPath(data, collectionPath)\n const itemsEnvelope = openItemsEnvelope(data, itemsPath)\n if (itemsEnvelope) {\n data = itemsEnvelope.items\n }\n \n if (Array.isArray(data)) {\n if (query && query.trim()) {\n const searchTerms = query.toLowerCase().split(' ').filter(Boolean)\n \n if (queryColumns) {\n try {\n const parsed = safeJSONParse(queryColumns)\n const columns = Array.isArray(parsed) ? parsed : [parsed]\n data = data.filter((item) => {\n return searchTerms.every((term) => columns.some((col) => {\n const value = getNestedValue(item, col)\n if (value === null || value === undefined) return false\n return String(value).toLowerCase().includes(term)\n }))\n })\n } catch (err) {\n console.error('Error parsing queryColumns:', err)\n }\n } else {\n data = data.filter((item) => {\n try {\n const stringified = JSON.stringify(item).toLowerCase()\n return searchTerms.every((term) => stringified.includes(term))\n } catch {\n return false\n }\n })\n }\n }\n \n if (filters) {\n try {\n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n \n if (filterTree) {\n data = data.filter((item) =>\n evaluateFilterTree(filterTree, (condition) => {\n const value = getNestedValue(item, condition.source)\n const target = condition.destination\n const operand = condition.operand\n \n if (Array.isArray(target)) {\n if (operand === '!=') {\n return !target.includes(value)\n }\n return target.includes(value)\n }\n \n return compareValues(value, target, operand)\n })\n )\n }\n } catch (err) {\n console.error('Error parsing filters:', err)\n }\n }\n \n if (sorts) {\n try {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n data.sort((a, b) => {\n for (const sort of parsedSorts) {\n if (!sort.field) continue\n const aVal = getNestedValue(a, sort.field)\n const bVal = getNestedValue(b, sort.field)\n const sortOrderValue = (sort.order || '').toLowerCase().startsWith('desc') ? -1 : 1\n \n let comparison = 0\n if (aVal === null || aVal === undefined) {\n comparison = bVal === null || bVal === undefined ? 0 : -1\n } else if (bVal === null || bVal === undefined) {\n comparison = 1\n } else if (typeof aVal === 'number' && typeof bVal === 'number') {\n comparison = aVal - bVal\n } else if (aVal instanceof Date && bVal instanceof Date) {\n comparison = aVal.getTime() - bVal.getTime()\n } else {\n const aStr = String(aVal)\n const bStr = String(bVal)\n if (aStr < bStr) comparison = -1\n else if (aStr > bStr) comparison = 1\n }\n \n if (comparison !== 0) return comparison * sortOrderValue\n }\n return 0\n })\n }\n } catch (err) {\n console.error('Error parsing sorts:', err)\n }\n } else if (sortBy && sortBy.trim()) {\n data.sort((a, b) => {\n const aVal = getNestedValue(a, sortBy)\n const bVal = getNestedValue(b, sortBy)\n const sortOrderValue = (sortOrder || '').toLowerCase().startsWith('desc') ? -1 : 1\n \n let comparison = 0\n if (aVal === null || aVal === undefined) {\n comparison = bVal === null || bVal === undefined ? 0 : -1\n } else if (bVal === null || bVal === undefined) {\n comparison = 1\n } else if (typeof aVal === 'number' && typeof bVal === 'number') {\n comparison = aVal - bVal\n } else if (aVal instanceof Date && bVal instanceof Date) {\n comparison = aVal.getTime() - bVal.getTime()\n } else {\n const aStr = String(aVal)\n const bStr = String(bVal)\n if (aStr < bStr) comparison = -1\n else if (aStr > bStr) comparison = 1\n }\n \n return comparison * sortOrderValue\n })\n }\n \n if (query && queryColumns) {\n data = rankBySearch(data, query, safeJSONParse(queryColumns), getNestedValue)\n }\n \n const limitValue = limit || perPage\n const pageValue = page ? Math.max(1, parseInt(page)) : undefined\n const offsetValue = offset !== undefined ? Math.max(0, parseInt(offset)) : (pageValue && perPage ? (pageValue - 1) * Math.max(1, parseInt(perPage)) : 0)\n \n if (limitValue) {\n const limitInt = Math.max(1, parseInt(limitValue))\n data = data.slice(offsetValue, offsetValue + limitInt)\n } else if (offsetValue > 0) {\n data = data.slice(offsetValue)\n }\n }\n \n if (itemsEnvelope) {\n data = itemsEnvelope.close(data)\n }\n \n const safeData = JSON.parse(JSON.stringify(data, dateReplacer))\n \n ").concat(cached ? 'applyRestApiCacheHeaders(req, res, response.fetchedAt)\n \n ' : '', "return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('REST API fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n }\n}\n");
|
|
96
97
|
};
|
|
97
98
|
exports.generateRESTAPIFetcher = generateRESTAPIFetcher;
|
|
98
99
|
//# sourceMappingURL=rest-api.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-api.js","sourceRoot":"","sources":["../../../src/fetchers/rest-api.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"rest-api.js","sourceRoot":"","sources":["../../../src/fetchers/rest-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../../src/fetchers/supabase.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../../src/fetchers/supabase.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,sBAAsB,WACzB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CA0BpC,CAAA;AAUD,eAAO,MAAM,uBAAuB,WAC1B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA2XF,CAAA;AAGD,eAAO,MAAM,4BAA4B,YAAa,GAAG,aAAa,MAAM,KAAG,MA0F9E,CAAA"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateSupabaseCountFetcher = exports.generateSupabaseFetcher = exports.validateSupabaseConfig = void 0;
|
|
4
4
|
var utils_1 = require("../utils");
|
|
5
|
+
var search_relevance_1 = require("../search-relevance");
|
|
5
6
|
var validateSupabaseConfig = function (config) {
|
|
6
7
|
if (!config || typeof config !== 'object') {
|
|
7
8
|
return { isValid: false, error: 'Config must be a valid object' };
|
|
@@ -91,12 +92,12 @@ var generateSupabaseFetcher = function (config, tableName) {
|
|
|
91
92
|
END $$;
|
|
92
93
|
|
|
93
94
|
*/
|
|
94
|
-
return "import { createClient } from '@supabase/supabase-js'\n\nlet client = null\n\nconst getClient = () => {\n if (client) return client\n \n client = createClient(\n ".concat(JSON.stringify(supabaseUrl), ",\n ").concat((0, utils_1.replaceSecretReference)(apiKey), "\n )\n \n return client\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n\n// Helper function to process filter values\nconst processFilterValue = (value) => {\n if (typeof value === 'string' && !isNaN(Number(value))) {\n return Number(value)\n }\n return value\n}\n\n// Helper function to apply filters to a query\nconst applyFilters = (queryRef, filters) => {\n if (!filters) return queryRef\n \n const parsedFilters = safeJSONParse(filters)\n \n if (Array.isArray(parsedFilters)) {\n const filterTree = normalizeFilterTree(parsedFilters)\n \n const applyCondition = (condition) => {\n const field = condition.source\n const value = condition.destination\n const operand = condition.operand\n \n if (Array.isArray(value)) {\n const processedValues = value.map(processFilterValue)\n if (operand === '!=') {\n queryRef = queryRef.not(field, 'in', processedValues)\n } else {\n queryRef = queryRef.in(field, processedValues)\n }\n return\n }\n \n const processedValue = processFilterValue(value)\n \n // Handle null values\n if (processedValue === null) {\n if (operand === '=') {\n queryRef = queryRef.is(field, null)\n } else if (operand === '!=') {\n queryRef = queryRef.not(field, 'is', null)\n }\n return\n }\n \n // Map operand to Supabase methods\n switch (operand) {\n case '!=':\n queryRef = queryRef.neq(field, processedValue)\n break\n case '>':\n queryRef = queryRef.gt(field, processedValue)\n break\n case '>=':\n queryRef = queryRef.gte(field, processedValue)\n break\n case '<':\n queryRef = queryRef.lt(field, processedValue)\n break\n case '<=':\n queryRef = queryRef.lte(field, processedValue)\n break\n default:\n queryRef = queryRef.eq(field, processedValue)\n }\n }\n \n // An OR group cannot be expressed by chaining (chained filters are ANDed),\n // so it is emitted as one PostgREST or=(...) expression instead.\n const POSTGREST_OPERANDS = { '=': 'eq', '!=': 'neq', '>': 'gt', '>=': 'gte', '<': 'lt', '<=': 'lte' }\n \n const toExprValue = (value) => {\n if (value === null || value === undefined) return 'null'\n const asString = String(value)\n return /[,.:()\"\\s]/.test(asString) ? '\"' + asString.replace(/\"/g, '\\\\\"') + '\"' : asString\n }\n \n const conditionToExpr = (condition) => {\n const field = condition.source\n const value = condition.destination\n const operand = condition.operand\n \n if (Array.isArray(value)) {\n const list = '(' + value.map((entry) => toExprValue(processFilterValue(entry))).join(',') + ')'\n return operand === '!=' ? field + '.not.in.' + list : field + '.in.' + list\n }\n \n const processedValue = processFilterValue(value)\n if (processedValue === null) {\n return operand === '!=' ? field + '.not.is.null' : field + '.is.null'\n }\n \n return field + '.' + (POSTGREST_OPERANDS[operand] || 'eq') + '.' + toExprValue(processedValue)\n }\n \n const nodeToExpr = (node) => {\n if (node.type === 'group') {\n const parts = node.children.map(nodeToExpr).filter(Boolean)\n if (parts.length === 0) return null\n if (parts.length === 1) return parts[0]\n return (node.operator === 'or' ? 'or' : 'and') + '(' + parts.join(',') + ')'\n }\n return conditionToExpr(node)\n }\n \n const applyNode = (node) => {\n if (node.type !== 'group') {\n applyCondition(node)\n return\n }\n if (node.operator === 'and') {\n node.children.forEach(applyNode)\n return\n }\n if (node.children.length === 1) {\n applyNode(node.children[0])\n return\n }\n const parts = node.children.map(nodeToExpr).filter(Boolean)\n if (parts.length > 0) {\n queryRef = queryRef.or(parts.join(','))\n }\n }\n \n if (filterTree) {\n applyNode(filterTree)\n }\n } else {\n // Old format: object with key-value pairs (backward compatibility)\n Object.entries(parsedFilters).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n const processedValues = value.map(processFilterValue)\n queryRef = queryRef.in(key, processedValues)\n } else if (typeof value === 'object' && value !== null) {\n const operator = Object.keys(value)[0]\n let operatorValue = value[operator]\n if (typeof operatorValue === 'string' && !isNaN(Number(operatorValue))) {\n operatorValue = Number(operatorValue)\n }\n switch (operator) {\n case 'eq': queryRef = queryRef.eq(key, operatorValue); break\n case 'neq': queryRef = queryRef.neq(key, operatorValue); break\n case 'gt': queryRef = queryRef.gt(key, operatorValue); break\n case 'gte': queryRef = queryRef.gte(key, operatorValue); break\n case 'lt': queryRef = queryRef.lt(key, operatorValue); break\n case 'lte': queryRef = queryRef.lte(key, operatorValue); break\n case 'like': queryRef = queryRef.like(key, operatorValue); break\n case 'ilike': queryRef = queryRef.ilike(key, operatorValue); break\n case 'in': queryRef = queryRef.in(key, operatorValue); break\n default: queryRef = queryRef.eq(key, operatorValue)\n }\n } else {\n let processedValue = value\n if (typeof value === 'string' && !isNaN(Number(value))) {\n processedValue = Number(value)\n }\n queryRef = queryRef.eq(key, processedValue)\n }\n })\n }\n \n return queryRef\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n try {\n const client = getClient()\n const { query, queryColumns, select, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n let queryRef = client.from('").concat(tableName, "').select(select || '*')\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so a single column\n // passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get text-searchable columns from a sample row\n try {\n const { data: sampleData, error: sampleError } = await client.from('").concat(tableName, "').select('*').limit(1).single()\n if (sampleError) {\n throw sampleError\n }\n if (sampleData) {\n // Filter out columns that are likely non-text types\n // Note: This is heuristic-based since we don't have schema info\n columns = Object.keys(sampleData).filter(col => {\n const value = sampleData[col]\n const colLower = col.toLowerCase()\n \n // Exclude common timestamp/date column names\n if (colLower.includes('_at') || colLower.includes('date') || colLower === 'timestamp') {\n return false\n }\n \n // Exclude if value is a number, boolean, null, or object (non-string)\n if (value === null || value === undefined) {\n return true // Include null columns, let the query handle it\n }\n \n const type = typeof value\n return type === 'string' // Only include string values\n })\n }\n } catch (schemaError) {\n console.warn('Failed to fetch sample row for column names:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n if (columns.length > 0) {\n // PostgREST .or() DSL separates conditions with comma and uses\n // dot to split field / operator / value, so we wrap the pattern\n // in double quotes and backslash-escape any embedded quotes or\n // backslashes in the user's search term. Columns go through the\n // shared identifier sanitizer to reject injection. Note:\n // PostgREST .ilike. does not expose a LIKE ESCAPE clause, so\n // raw % / _ in the user input still act as wildcards in this\n // backend; the canvas preview path escapes them explicitly.\n const rawPattern = \"%\" + String(query) + \"%\"\n const escapedForPostgrest = rawPattern\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n const searchPattern = '\"' + escapedForPostgrest + '\"'\n const orConditions = columns\n .map((col) => sanitizeSearchIdentifier(col) + \".ilike.\" + searchPattern)\n .join(\",\")\n queryRef = queryRef.or(orConditions)\n }\n }\n\n // Apply filters using helper function\n queryRef = applyFilters(queryRef, filters)\n \n // Handle sorts - new array format\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts)) {\n parsedSorts.forEach((sort) => {\n if (sort.field) {\n queryRef = queryRef.order(sort.field, {\n ascending: !(sort.order || '').toLowerCase().startsWith('desc')\n })\n }\n })\n }\n } else if (sortBy) {\n queryRef = queryRef.order(sortBy, { ascending: !(sortOrder || '').toLowerCase().startsWith('desc') })\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n if (offsetValue !== undefined && limitValue) {\n queryRef = queryRef.range(offsetValue, offsetValue + parseInt(limitValue) - 1)\n } else if (limitValue) {\n queryRef = queryRef.limit(parseInt(limitValue))\n }\n \n const { data, error } = await queryRef\n \n if (error) {\n return res.status(500).json({\n success: false,\n error: error.message,\n timestamp: Date.now()\n })\n }\n \n const safeData = JSON.parse(JSON.stringify(data, dateReplacer))\n \n return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Supabase fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n }\n}\n");
|
|
95
|
+
return "import { createClient } from '@supabase/supabase-js'\n\nlet client = null\n\nconst getClient = () => {\n if (client) return client\n \n client = createClient(\n ".concat(JSON.stringify(supabaseUrl), ",\n ").concat((0, utils_1.replaceSecretReference)(apiKey), "\n )\n \n return client\n}\n\n").concat((0, utils_1.generateSafeJSONParseCode)(), "\n\n").concat((0, utils_1.generateFilterTreeHelpersCode)(), "\n\n").concat((0, utils_1.generateSearchEscapeHelpersCode)(), "\n").concat((0, search_relevance_1.generateSearchWordsCode)(), "\n\n// Helper function to process filter values\nconst processFilterValue = (value) => {\n if (typeof value === 'string' && !isNaN(Number(value))) {\n return Number(value)\n }\n return value\n}\n\n// Helper function to apply filters to a query\nconst applyFilters = (queryRef, filters) => {\n if (!filters) return queryRef\n \n const parsedFilters = safeJSONParse(filters)\n \n if (Array.isArray(parsedFilters)) {\n const filterTree = normalizeFilterTree(parsedFilters)\n \n const applyCondition = (condition) => {\n const field = condition.source\n const value = condition.destination\n const operand = condition.operand\n \n if (Array.isArray(value)) {\n const processedValues = value.map(processFilterValue)\n if (operand === '!=') {\n queryRef = queryRef.not(field, 'in', processedValues)\n } else {\n queryRef = queryRef.in(field, processedValues)\n }\n return\n }\n \n const processedValue = processFilterValue(value)\n \n // Handle null values\n if (processedValue === null) {\n if (operand === '=') {\n queryRef = queryRef.is(field, null)\n } else if (operand === '!=') {\n queryRef = queryRef.not(field, 'is', null)\n }\n return\n }\n \n // Map operand to Supabase methods\n switch (operand) {\n case '!=':\n queryRef = queryRef.neq(field, processedValue)\n break\n case '>':\n queryRef = queryRef.gt(field, processedValue)\n break\n case '>=':\n queryRef = queryRef.gte(field, processedValue)\n break\n case '<':\n queryRef = queryRef.lt(field, processedValue)\n break\n case '<=':\n queryRef = queryRef.lte(field, processedValue)\n break\n default:\n queryRef = queryRef.eq(field, processedValue)\n }\n }\n \n // An OR group cannot be expressed by chaining (chained filters are ANDed),\n // so it is emitted as one PostgREST or=(...) expression instead.\n const POSTGREST_OPERANDS = { '=': 'eq', '!=': 'neq', '>': 'gt', '>=': 'gte', '<': 'lt', '<=': 'lte' }\n \n const toExprValue = (value) => {\n if (value === null || value === undefined) return 'null'\n const asString = String(value)\n return /[,.:()\"\\s]/.test(asString) ? '\"' + asString.replace(/\"/g, '\\\\\"') + '\"' : asString\n }\n \n const conditionToExpr = (condition) => {\n const field = condition.source\n const value = condition.destination\n const operand = condition.operand\n \n if (Array.isArray(value)) {\n const list = '(' + value.map((entry) => toExprValue(processFilterValue(entry))).join(',') + ')'\n return operand === '!=' ? field + '.not.in.' + list : field + '.in.' + list\n }\n \n const processedValue = processFilterValue(value)\n if (processedValue === null) {\n return operand === '!=' ? field + '.not.is.null' : field + '.is.null'\n }\n \n return field + '.' + (POSTGREST_OPERANDS[operand] || 'eq') + '.' + toExprValue(processedValue)\n }\n \n const nodeToExpr = (node) => {\n if (node.type === 'group') {\n const parts = node.children.map(nodeToExpr).filter(Boolean)\n if (parts.length === 0) return null\n if (parts.length === 1) return parts[0]\n return (node.operator === 'or' ? 'or' : 'and') + '(' + parts.join(',') + ')'\n }\n return conditionToExpr(node)\n }\n \n const applyNode = (node) => {\n if (node.type !== 'group') {\n applyCondition(node)\n return\n }\n if (node.operator === 'and') {\n node.children.forEach(applyNode)\n return\n }\n if (node.children.length === 1) {\n applyNode(node.children[0])\n return\n }\n const parts = node.children.map(nodeToExpr).filter(Boolean)\n if (parts.length > 0) {\n queryRef = queryRef.or(parts.join(','))\n }\n }\n \n if (filterTree) {\n applyNode(filterTree)\n }\n } else {\n // Old format: object with key-value pairs (backward compatibility)\n Object.entries(parsedFilters).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n const processedValues = value.map(processFilterValue)\n queryRef = queryRef.in(key, processedValues)\n } else if (typeof value === 'object' && value !== null) {\n const operator = Object.keys(value)[0]\n let operatorValue = value[operator]\n if (typeof operatorValue === 'string' && !isNaN(Number(operatorValue))) {\n operatorValue = Number(operatorValue)\n }\n switch (operator) {\n case 'eq': queryRef = queryRef.eq(key, operatorValue); break\n case 'neq': queryRef = queryRef.neq(key, operatorValue); break\n case 'gt': queryRef = queryRef.gt(key, operatorValue); break\n case 'gte': queryRef = queryRef.gte(key, operatorValue); break\n case 'lt': queryRef = queryRef.lt(key, operatorValue); break\n case 'lte': queryRef = queryRef.lte(key, operatorValue); break\n case 'like': queryRef = queryRef.like(key, operatorValue); break\n case 'ilike': queryRef = queryRef.ilike(key, operatorValue); break\n case 'in': queryRef = queryRef.in(key, operatorValue); break\n default: queryRef = queryRef.eq(key, operatorValue)\n }\n } else {\n let processedValue = value\n if (typeof value === 'string' && !isNaN(Number(value))) {\n processedValue = Number(value)\n }\n queryRef = queryRef.eq(key, processedValue)\n }\n })\n }\n \n return queryRef\n}\n\n").concat((0, utils_1.generateDateFormatterCode)(), "\n\nexport default async function handler(req, res) {\n try {\n const client = getClient()\n const { query, queryColumns, select, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n \n let queryRef = client.from('").concat(tableName, "').select(select || '*')\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns. Wrap non-arrays so a single column\n // passed as a bare string doesn't get iterated as chars.\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n // Fallback: Get text-searchable columns from a sample row\n try {\n const { data: sampleData, error: sampleError } = await client.from('").concat(tableName, "').select('*').limit(1).single()\n if (sampleError) {\n throw sampleError\n }\n if (sampleData) {\n // Filter out columns that are likely non-text types\n // Note: This is heuristic-based since we don't have schema info\n columns = Object.keys(sampleData).filter(col => {\n const value = sampleData[col]\n const colLower = col.toLowerCase()\n \n // Exclude common timestamp/date column names\n if (colLower.includes('_at') || colLower.includes('date') || colLower === 'timestamp') {\n return false\n }\n \n // Exclude if value is a number, boolean, null, or object (non-string)\n if (value === null || value === undefined) {\n return true // Include null columns, let the query handle it\n }\n \n const type = typeof value\n return type === 'string' // Only include string values\n })\n }\n } catch (schemaError) {\n console.warn('Failed to fetch sample row for column names:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n if (columns.length > 0) {\n // PostgREST .or() DSL separates conditions with comma and uses\n // dot to split field / operator / value, so we wrap the pattern\n // in double quotes and backslash-escape any embedded quotes or\n // backslashes in the user's search term. Columns go through the\n // shared identifier sanitizer to reject injection. Note:\n // PostgREST .ilike. does not expose a LIKE ESCAPE clause, so\n // raw % / _ in the user input still act as wildcards in this\n // backend; the canvas preview path escapes them explicitly. Each\n // typed word gets its own .or(), and PostgREST ANDs them.\n searchWords(query).forEach((word) => {\n const rawPattern = \"%\" + word + \"%\"\n const escapedForPostgrest = rawPattern\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n const searchPattern = '\"' + escapedForPostgrest + '\"'\n const orConditions = columns\n .map((col) => sanitizeSearchIdentifier(col) + \".ilike.\" + searchPattern)\n .join(\",\")\n queryRef = queryRef.or(orConditions)\n })\n }\n }\n\n // Apply filters using helper function\n queryRef = applyFilters(queryRef, filters)\n \n // Handle sorts - new array format\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts)) {\n parsedSorts.forEach((sort) => {\n if (sort.field) {\n queryRef = queryRef.order(sort.field, {\n ascending: !(sort.order || '').toLowerCase().startsWith('desc')\n })\n }\n })\n }\n } else if (sortBy) {\n queryRef = queryRef.order(sortBy, { ascending: !(sortOrder || '').toLowerCase().startsWith('desc') })\n }\n \n const limitValue = limit || perPage\n const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)\n \n if (offsetValue !== undefined && limitValue) {\n queryRef = queryRef.range(offsetValue, offsetValue + parseInt(limitValue) - 1)\n } else if (limitValue) {\n queryRef = queryRef.limit(parseInt(limitValue))\n }\n \n const { data, error } = await queryRef\n \n if (error) {\n return res.status(500).json({\n success: false,\n error: error.message,\n timestamp: Date.now()\n })\n }\n \n const safeData = JSON.parse(JSON.stringify(data, dateReplacer))\n \n return res.status(200).json({\n success: true,\n data: safeData,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Supabase fetch error:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to fetch data',\n timestamp: Date.now()\n })\n }\n}\n");
|
|
95
96
|
};
|
|
96
97
|
exports.generateSupabaseFetcher = generateSupabaseFetcher;
|
|
97
98
|
// tslint:disable-next-line:variable-name
|
|
98
99
|
var generateSupabaseCountFetcher = function (_config, tableName) {
|
|
99
|
-
return "\nasync function getCount(req, res) {\n const supabase = getClient()\n\n try {\n const { query, queryColumns, filters } = req.query\n let countQuery = supabase.from('".concat(tableName, "').select('*', { count: 'exact', head: true })\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get text-searchable columns from a sample row\n try {\n const { data: sampleData, error: sampleError } = await supabase.from('").concat(tableName, "').select('*').limit(1).single()\n if (sampleError) {\n throw sampleError\n }\n if (sampleData) {\n // Filter out columns that are likely non-text types\n // Note: This is heuristic-based since we don't have schema info\n columns = Object.keys(sampleData).filter(col => {\n const value = sampleData[col]\n const colLower = col.toLowerCase()\n \n // Exclude common timestamp/date column names\n if (colLower.includes('_at') || colLower.includes('date') || colLower === 'timestamp') {\n return false\n }\n \n // Exclude if value is a number, boolean, null, or object (non-string)\n if (value === null || value === undefined) {\n return true // Include null columns, let the query handle it\n }\n \n const type = typeof value\n return type === 'string' // Only include string values\n })\n }\n } catch (schemaError) {\n console.warn('Failed to fetch sample row for column names:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n if (columns.length > 0) {\n // Mirror the fetch handler: sanitize identifiers, wrap the\n // pattern in double quotes for PostgREST .or() DSL, and escape\n // backslashes / quotes in the user's search term.\n const rawPattern = \"%\" +
|
|
100
|
+
return "\nasync function getCount(req, res) {\n const supabase = getClient()\n\n try {\n const { query, queryColumns, filters } = req.query\n let countQuery = supabase.from('".concat(tableName, "').select('*', { count: 'exact', head: true })\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n // Use specified columns\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n // Fallback: Get text-searchable columns from a sample row\n try {\n const { data: sampleData, error: sampleError } = await supabase.from('").concat(tableName, "').select('*').limit(1).single()\n if (sampleError) {\n throw sampleError\n }\n if (sampleData) {\n // Filter out columns that are likely non-text types\n // Note: This is heuristic-based since we don't have schema info\n columns = Object.keys(sampleData).filter(col => {\n const value = sampleData[col]\n const colLower = col.toLowerCase()\n \n // Exclude common timestamp/date column names\n if (colLower.includes('_at') || colLower.includes('date') || colLower === 'timestamp') {\n return false\n }\n \n // Exclude if value is a number, boolean, null, or object (non-string)\n if (value === null || value === undefined) {\n return true // Include null columns, let the query handle it\n }\n \n const type = typeof value\n return type === 'string' // Only include string values\n })\n }\n } catch (schemaError) {\n console.warn('Failed to fetch sample row for column names:', schemaError.message)\n // Continue without search if we can't get columns\n }\n }\n \n if (columns.length > 0) {\n // Mirror the fetch handler: sanitize identifiers, wrap the\n // pattern in double quotes for PostgREST .or() DSL, and escape\n // backslashes / quotes in the user's search term, one .or() per word.\n searchWords(query).forEach((word) => {\n const rawPattern = \"%\" + word + \"%\"\n const escapedForPostgrest = rawPattern\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n const searchPattern = '\"' + escapedForPostgrest + '\"'\n const orConditions = columns\n .map((col) => sanitizeSearchIdentifier(col) + \".ilike.\" + searchPattern)\n .join(\",\")\n countQuery = countQuery.or(orConditions)\n })\n }\n }\n\n // Apply filters using helper function\n countQuery = applyFilters(countQuery, filters)\n\n const { count, error } = await countQuery\n \n if (error) throw error\n\n return res.status(200).json({\n success: true,\n count: count || 0,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(500).json({\n success: false,\n error: error.message || 'Failed to get count',\n timestamp: Date.now()\n })\n }\n}\n");
|
|
100
101
|
};
|
|
101
102
|
exports.generateSupabaseCountFetcher = generateSupabaseCountFetcher;
|
|
102
103
|
//# sourceMappingURL=supabase.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../../src/fetchers/supabase.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../../src/fetchers/supabase.ts"],"names":[],"mappings":";;;AAAA,kCAMiB;AACjB,wDAA6D;AAEtD,IAAM,sBAAsB,GAAG,UACpC,MAA+B;IAE/B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;QACzC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAA;KAClE;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE;QACjE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAA;KAC7D;IAED,IAAI;QACF,IAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;QACvC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE;YACpF,OAAO,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAA;SACnF;KACF;IAAC,WAAM;QACN,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAA;KAChE;IAED,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE;QAClD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,+DAA+D;SACvE,CAAA;KACF;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC,CAAA;AA5BY,QAAA,sBAAsB,0BA4BlC;AAUM,IAAM,uBAAuB,GAAG,UACrC,MAA+B,EAC/B,SAAiB;IAEjB,IAAM,cAAc,GAAG,MAAwB,CAAA;IAC/C,IAAM,WAAW,GAAG,cAAc,CAAC,WAAW,CAAA;IAC9C,IAAM,MAAM,GAAG,cAAc,CAAC,cAAc,IAAI,cAAc,CAAC,YAAY,CAAA;IAC3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2DA;IACA,OAAO,iLAQH,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,oBAC3B,IAAA,8BAAsB,EAAC,MAAM,CAAC,8CAMlC,IAAA,iCAAyB,GAAE,iBAE3B,IAAA,qCAA6B,GAAE,iBAE/B,IAAA,uCAA+B,GAAE,eACjC,IAAA,0CAAuB,GAAE,irLAqKzB,IAAA,iCAAyB,GAAE,2QAOK,SAAS,qjBAaqC,SAAS,ynIA6GxF,CAAA;AACD,CAAC,CAAA;AA9XY,QAAA,uBAAuB,2BA8XnC;AAED,yCAAyC;AAClC,IAAM,4BAA4B,GAAG,UAAC,OAAY,EAAE,SAAiB;IAC1E,OAAO,wLAM6B,SAAS,kdAYmC,SAAS,6jFAsE1F,CAAA;AACD,CAAC,CAAA;AA1FY,QAAA,4BAA4B,gCA0FxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teleport.d.ts","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"teleport.d.ts","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAA;AA2C3B,eAAO,MAAM,sBAAsB,WACzB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAMpC,CAAA;AAED,eAAO,MAAM,uBAAuB,WAC1B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,qBACC,sBAAsB,KACvC,MAscF,CAAA;AAED,eAAO,MAAM,4BAA4B,WAC/B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MAwFF,CAAA"}
|