@syntropylog/adapters
Version:
External adapters for SyntropyLog framework
1,376 lines (1,370 loc) • 58.3 kB
JavaScript
class PrismaSerializer {
constructor() {
this.name = 'prisma';
this.priority = 75;
}
canSerialize(data) {
return (this.isPrismaQuery(data) ||
this.isPrismaError(data) ||
this.isPrismaClient(data));
}
getComplexity(data) {
if (this.isPrismaQuery(data)) {
return this.assessQueryComplexity(data);
}
if (this.isPrismaError(data)) {
return 'low';
}
if (this.isPrismaClient(data)) {
return 'low';
}
return 'low';
}
async serialize(data, context) {
const startTime = Date.now();
try {
let result;
if (this.isPrismaQuery(data)) {
result = this.serializeQuery(data);
}
else if (this.isPrismaError(data)) {
result = this.serializeError(data);
}
else if (this.isPrismaClient(data)) {
result = this.serializeClient(data);
}
else {
throw new Error('Tipo de dato Prisma no reconocido');
}
const duration = Date.now() - startTime;
// ✅ Verificar que la serialización respeta el timeout del contexto
const timeout = context.timeout || 50;
if (duration > timeout) {
throw new Error(`Serialización lenta: ${duration}ms (máximo ${timeout}ms)`);
}
return {
success: true,
data: result,
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
catch (error) {
const duration = Date.now() - startTime;
return {
success: false,
error: error instanceof Error ? error.message : 'Error desconocido en serialización Prisma',
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
}
isPrismaQuery(data) {
return (data &&
typeof data === 'object' &&
typeof data.model === 'string' &&
typeof data.action === 'string');
}
isPrismaError(data) {
return (data &&
typeof data === 'object' &&
typeof data.code === 'string' &&
typeof data.message === 'string');
}
isPrismaClient(data) {
return (data &&
typeof data === 'object' &&
typeof data.$connect === 'function' &&
typeof data.$disconnect === 'function' &&
typeof data.$queryRaw === 'function');
}
assessQueryComplexity(query) {
let complexity = 0;
// Basado en la acción
if (query.action === 'findMany')
complexity += 1;
else if (query.action === 'findFirst')
complexity += 1;
else if (query.action === 'findUnique')
complexity += 1;
else if (query.action === 'create')
complexity += 2;
else if (query.action === 'update')
complexity += 2;
else if (query.action === 'updateMany')
complexity += 3;
else if (query.action === 'delete')
complexity += 2;
else if (query.action === 'deleteMany')
complexity += 3;
else if (query.action === 'upsert')
complexity += 3;
else if (query.action === 'aggregate')
complexity += 4;
else if (query.action === 'groupBy')
complexity += 4;
else if (query.action === 'count')
complexity += 1;
// Basado en argumentos complejos
if (query.args) {
if (query.args.include)
complexity += 1;
if (query.args.select)
complexity += 1;
if (query.args.where && typeof query.args.where === 'object') {
const whereKeys = Object.keys(query.args.where);
complexity += Math.min(whereKeys.length, 2);
}
if (query.args.orderBy)
complexity += 1;
if (query.args.take)
complexity += 1;
if (query.args.skip)
complexity += 1;
if (query.args.distinct)
complexity += 1;
}
if (complexity >= 7)
return 'high';
if (complexity >= 4)
return 'medium';
return 'low';
}
serializeQuery(query) {
return {
type: 'PrismaQuery',
model: query.model,
action: query.action,
args: query.args, // Datos originales, sin sanitizar
duration: query.duration,
timestamp: query.timestamp,
complexity: this.assessQueryComplexity(query)
};
}
serializeError(error) {
return {
type: 'PrismaError',
code: error.code,
message: error.message,
meta: error.meta, // Datos originales, sin sanitizar
clientVersion: error.clientVersion,
stack: error.stack
};
}
serializeClient(client) {
return {
type: 'PrismaClient',
hasConnect: typeof client.$connect === 'function',
hasDisconnect: typeof client.$disconnect === 'function',
hasQueryRaw: typeof client.$queryRaw === 'function',
hasExecuteRaw: typeof client.$executeRaw === 'function',
hasTransaction: typeof client.$transaction === 'function',
hasUse: typeof client.$use === 'function',
hasOn: typeof client.$on === 'function'
};
}
}
class TypeORMSerializer {
constructor() {
this.name = 'typeorm';
this.priority = 80;
}
canSerialize(data) {
return (this.isTypeORMQuery(data) ||
this.isTypeORMError(data) ||
this.isTypeORMEntity(data) ||
this.isTypeORMRepository(data) ||
this.isTypeORMConnection(data));
}
getComplexity(data) {
if (this.isTypeORMQuery(data)) {
return this.assessQueryComplexity(data);
}
if (this.isTypeORMError(data)) {
return 'low';
}
if (this.isTypeORMEntity(data)) {
return this.assessEntityComplexity(data);
}
if (this.isTypeORMRepository(data)) {
return 'medium';
}
if (this.isTypeORMConnection(data)) {
return 'low';
}
return 'low';
}
async serialize(data, context) {
const startTime = Date.now();
try {
let result;
if (this.isTypeORMQuery(data)) {
result = this.serializeQuery(data);
}
else if (this.isTypeORMError(data)) {
result = this.serializeError(data);
}
else if (this.isTypeORMEntity(data)) {
result = this.serializeEntity(data);
}
else if (this.isTypeORMRepository(data)) {
result = this.serializeRepository(data);
}
else if (this.isTypeORMConnection(data)) {
result = this.serializeConnection(data);
}
else {
throw new Error('Tipo de dato TypeORM no reconocido');
}
const duration = Date.now() - startTime;
// ✅ Verificar que la serialización respeta el timeout del contexto
const timeout = context.timeout || 50;
if (duration > timeout) {
throw new Error(`Serialización lenta: ${duration}ms (máximo ${timeout}ms)`);
}
return {
success: true,
data: result,
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
catch (error) {
const duration = Date.now() - startTime;
return {
success: false,
error: error instanceof Error ? error.message : 'Error desconocido en serialización TypeORM',
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
}
isTypeORMQuery(data) {
return (data &&
typeof data === 'object' &&
typeof data.sql === 'string' &&
(data.parameters === undefined || Array.isArray(data.parameters)));
}
isTypeORMError(data) {
return (data &&
typeof data === 'object' &&
typeof data.message === 'string' &&
(data.code === undefined || typeof data.code === 'string'));
}
isTypeORMEntity(data) {
return (data &&
typeof data === 'object' &&
data.constructor &&
data.constructor.name &&
(data.constructor.name.includes('Entity') ||
data.constructor.name.includes('Model') ||
data.id !== undefined));
}
isTypeORMRepository(data) {
return (data &&
typeof data === 'object' &&
typeof data.find === 'function' &&
typeof data.findOne === 'function' &&
typeof data.save === 'function');
}
isTypeORMConnection(data) {
return (data &&
typeof data === 'object' &&
typeof data.isConnected === 'function' &&
typeof data.close === 'function');
}
assessQueryComplexity(query) {
let complexity = 0;
// Basado en el tipo de query
if (query.queryType === 'SELECT')
complexity += 1;
else if (query.queryType === 'INSERT')
complexity += 2;
else if (query.queryType === 'UPDATE')
complexity += 3;
else if (query.queryType === 'DELETE')
complexity += 3;
// Basado en joins
if (query.joins && query.joins.length > 0) {
complexity += query.joins.length * 2;
}
// Basado en la longitud del SQL
if (query.sql.length > 500)
complexity += 2;
else if (query.sql.length > 200)
complexity += 1;
// Basado en parámetros
if (query.parameters && query.parameters.length > 10)
complexity += 2;
else if (query.parameters && query.parameters.length > 5)
complexity += 1;
if (complexity >= 6)
return 'high';
if (complexity >= 3)
return 'medium';
return 'low';
}
assessEntityComplexity(entity) {
const keys = Object.keys(entity);
if (keys.length > 20)
return 'high';
if (keys.length > 10)
return 'medium';
return 'low';
}
serializeQuery(query) {
return {
type: 'TypeORMQuery',
queryType: query.queryType || 'UNKNOWN',
sql: query.sql, // SQL original, sin sanitizar
parameters: query.parameters, // Parámetros originales, sin sanitizar
table: query.table,
alias: query.alias,
joins: query.joins, // Datos originales, sin sanitizar
where: query.where, // Datos originales, sin sanitizar
orderBy: query.orderBy,
limit: query.limit,
offset: query.offset,
complexity: this.assessQueryComplexity(query)
};
}
serializeError(error) {
return {
type: 'TypeORMError',
code: error.code,
message: error.message,
query: error.query, // SQL original, sin sanitizar
parameters: error.parameters, // Parámetros originales, sin sanitizar
table: error.table,
constraint: error.constraint,
detail: error.detail,
hint: error.hint,
position: error.position,
internalPosition: error.internalPosition,
internalQuery: error.internalQuery, // SQL original, sin sanitizar
where: error.where, // SQL original, sin sanitizar
schema: error.schema,
column: error.column,
dataType: error.dataType
};
}
serializeEntity(entity) {
const serialized = {
type: 'TypeORMEntity',
entityName: entity.constructor?.name || 'UnknownEntity',
id: entity.id,
fields: {}
};
// Serializar campos del entity (datos originales, sin sanitizar)
for (const [key, value] of Object.entries(entity)) {
if (key !== 'constructor' && typeof value !== 'function') {
serialized.fields[key] = value; // Valor original, sin sanitizar
}
}
return serialized;
}
serializeRepository(repo) {
return {
type: 'TypeORMRepository',
repositoryName: repo.constructor?.name || 'UnknownRepository',
target: repo.target?.name || 'UnknownTarget',
metadata: repo.metadata ? {
tableName: repo.metadata.tableName,
columns: repo.metadata.columns?.map((col) => col.propertyName) || [],
relations: repo.metadata.relations?.map((rel) => rel.propertyName) || []
} : undefined
};
}
serializeConnection(connection) {
return {
type: 'TypeORMConnection',
name: connection.name || 'default',
isConnected: connection.isConnected ? connection.isConnected() : undefined,
driver: connection.driver?.constructor?.name || 'UnknownDriver',
options: connection.options ? {
type: connection.options.type,
host: connection.options.host,
port: connection.options.port,
database: connection.options.database,
username: connection.options.username // Usuario original, sin sanitizar
} : undefined
};
}
}
class MySQLSerializer {
constructor() {
this.name = 'mysql';
this.priority = 85;
}
canSerialize(data) {
return (this.isMySQLQuery(data) ||
this.isMySQLError(data) ||
this.isMySQLConnection(data) ||
this.isMySQLPool(data));
}
getComplexity(data) {
if (this.isMySQLQuery(data)) {
return this.assessQueryComplexity(data);
}
if (this.isMySQLError(data)) {
return 'low';
}
if (this.isMySQLConnection(data)) {
return 'low';
}
if (this.isMySQLPool(data)) {
return 'medium';
}
return 'low';
}
async serialize(data, context) {
const startTime = Date.now();
try {
let result;
if (this.isMySQLQuery(data)) {
result = this.serializeQuery(data);
}
else if (this.isMySQLError(data)) {
result = this.serializeError(data);
}
else if (this.isMySQLConnection(data)) {
result = this.serializeConnection(data);
}
else if (this.isMySQLPool(data)) {
result = this.serializePool(data);
}
else {
throw new Error('Tipo de dato MySQL no reconocido');
}
const duration = Date.now() - startTime;
// ✅ Verificar que la serialización respeta el timeout del contexto
const timeout = context.timeout || 50;
if (duration > timeout) {
throw new Error(`Serialización lenta: ${duration}ms (máximo ${timeout}ms)`);
}
return {
success: true,
data: result,
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
catch (error) {
const duration = Date.now() - startTime;
return {
success: false,
error: error instanceof Error ? error.message : 'Error desconocido en serialización MySQL',
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
}
isMySQLQuery(data) {
return (data &&
typeof data === 'object' &&
typeof data.sql === 'string' &&
(data.values === undefined || Array.isArray(data.values)));
}
isMySQLError(data) {
return (data &&
typeof data === 'object' &&
typeof data.code === 'string' &&
typeof data.errno === 'number' &&
typeof data.sqlMessage === 'string');
}
isMySQLConnection(data) {
return (data &&
typeof data === 'object' &&
typeof data.query === 'function' &&
typeof data.connect === 'function' &&
typeof data.end === 'function');
}
isMySQLPool(data) {
return (data &&
typeof data === 'object' &&
typeof data.getConnection === 'function' &&
typeof data.query === 'function' &&
typeof data.end === 'function');
}
assessQueryComplexity(query) {
let complexity = 0;
const sql = query.sql.toLowerCase();
// Basado en el tipo de operación
if (sql.includes('select') && !sql.includes('join'))
complexity += 1;
else if (sql.includes('insert'))
complexity += 2;
else if (sql.includes('update'))
complexity += 3;
else if (sql.includes('delete'))
complexity += 3;
else if (sql.includes('create') || sql.includes('alter') || sql.includes('drop')) {
complexity += 4; // DDL operations
}
// Basado en joins
if (sql.includes('join')) {
const joinCount = (sql.match(/join/g) || []).length;
complexity += joinCount * 2;
}
// Basado en subqueries
if (sql.includes('(select') || sql.includes('( select')) {
const subqueryCount = (sql.match(/\(select/g) || []).length;
complexity += subqueryCount * 3;
}
// Basado en funciones complejas
if (sql.includes('group_concat') || sql.includes('json_'))
complexity += 2;
if (sql.includes('window') || sql.includes('over('))
complexity += 3;
// Basado en la longitud del SQL
if (sql.length > 1000)
complexity += 3;
else if (sql.length > 500)
complexity += 2;
else if (sql.length > 200)
complexity += 1;
// Basado en parámetros
if (query.values && query.values.length > 20)
complexity += 2;
else if (query.values && query.values.length > 10)
complexity += 1;
if (complexity >= 8)
return 'high';
if (complexity >= 4)
return 'medium';
return 'low';
}
serializeQuery(query) {
return {
type: 'MySQLQuery',
sql: query.sql, // SQL original, sin sanitizar
values: query.values, // Valores originales, sin sanitizar
timeout: query.timeout,
connectionConfig: query.connectionConfig ? {
host: query.connectionConfig.host,
port: query.connectionConfig.port,
database: query.connectionConfig.database,
user: query.connectionConfig.user,
password: query.connectionConfig.password // Contraseña original, sin sanitizar
} : undefined,
complexity: this.assessQueryComplexity(query)
};
}
serializeError(error) {
return {
type: 'MySQLError',
code: error.code,
errno: error.errno,
sqlMessage: error.sqlMessage,
sqlState: error.sqlState,
index: error.index,
sql: error.sql, // SQL original, sin sanitizar
fatal: error.fatal
};
}
serializeConnection(connection) {
return {
type: 'MySQLConnection',
threadId: connection.threadId,
state: connection.state,
config: connection.config ? {
host: connection.config.host,
port: connection.config.port,
database: connection.config.database,
user: connection.config.user,
password: connection.config.password // Contraseña original, sin sanitizar
} : undefined,
hasQuery: typeof connection.query === 'function',
hasConnect: typeof connection.connect === 'function',
hasEnd: typeof connection.end === 'function'
};
}
serializePool(pool) {
return {
type: 'MySQLPool',
config: pool.config ? {
host: pool.config.host,
port: pool.config.port,
database: pool.config.database,
user: pool.config.user,
password: pool.config.password, // Contraseña original, sin sanitizar
connectionLimit: pool.config.connectionLimit,
acquireTimeout: pool.config.acquireTimeout,
timeout: pool.config.timeout
} : undefined,
hasGetConnection: typeof pool.getConnection === 'function',
hasQuery: typeof pool.query === 'function',
hasEnd: typeof pool.end === 'function'
};
}
}
class PostgreSQLSerializer {
constructor() {
this.name = 'postgresql';
this.priority = 90;
}
canSerialize(data) {
return (this.isPostgreSQLQuery(data) ||
this.isPostgreSQLError(data) ||
this.isPostgreSQLClient(data) ||
this.isPostgreSQLPool(data));
}
getComplexity(data) {
if (this.isPostgreSQLQuery(data)) {
return this.assessQueryComplexity(data);
}
if (this.isPostgreSQLError(data)) {
return 'low';
}
if (this.isPostgreSQLClient(data)) {
return 'low';
}
if (this.isPostgreSQLPool(data)) {
return 'medium';
}
return 'low';
}
async serialize(data, context) {
const startTime = Date.now();
try {
let result;
if (this.isPostgreSQLQuery(data)) {
result = this.serializeQuery(data);
}
else if (this.isPostgreSQLError(data)) {
result = this.serializeError(data);
}
else if (this.isPostgreSQLClient(data)) {
result = this.serializeClient(data);
}
else if (this.isPostgreSQLPool(data)) {
result = this.serializePool(data);
}
else {
throw new Error('Tipo de dato PostgreSQL no reconocido');
}
const duration = Date.now() - startTime;
// ✅ Verificar que la serialización respeta el timeout del contexto
const timeout = context.timeout || 50;
if (duration > timeout) {
throw new Error(`Serialización lenta: ${duration}ms (máximo ${timeout}ms)`);
}
return {
success: true,
data: result,
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
catch (error) {
const duration = Date.now() - startTime;
return {
success: false,
error: error instanceof Error ? error.message : 'Error desconocido en serialización PostgreSQL',
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
}
isPostgreSQLQuery(data) {
return (data &&
typeof data === 'object' &&
typeof data.text === 'string' &&
(data.values === undefined || Array.isArray(data.values)));
}
isPostgreSQLError(data) {
return (data &&
typeof data === 'object' &&
typeof data.code === 'string' &&
typeof data.message === 'string');
}
isPostgreSQLClient(data) {
return (data &&
typeof data === 'object' &&
typeof data.query === 'function' &&
typeof data.connect === 'function' &&
typeof data.end === 'function');
}
isPostgreSQLPool(data) {
return (data &&
typeof data === 'object' &&
typeof data.connect === 'function' &&
typeof data.query === 'function' &&
typeof data.end === 'function');
}
assessQueryComplexity(query) {
let complexity = 0;
const sql = query.text.toLowerCase();
// Basado en el tipo de operación
if (sql.includes('select') && !sql.includes('join'))
complexity += 1;
else if (sql.includes('insert'))
complexity += 2;
else if (sql.includes('update'))
complexity += 3;
else if (sql.includes('delete'))
complexity += 3;
else if (sql.includes('create') || sql.includes('alter') || sql.includes('drop')) {
complexity += 4; // DDL operations
}
// Basado en CTEs (Common Table Expressions)
if (sql.includes('with')) {
const cteCount = (sql.match(/with\s+\w+\s+as/gi) || []).length;
complexity += cteCount * 3;
}
// Basado en window functions
if (sql.includes('over(')) {
const windowCount = (sql.match(/over\s*\(/gi) || []).length;
complexity += windowCount * 2;
}
// Basado en joins
if (sql.includes('join')) {
const joinCount = (sql.match(/join/g) || []).length;
complexity += joinCount * 2;
}
// Basado en subqueries
if (sql.includes('(select') || sql.includes('( select')) {
const subqueryCount = (sql.match(/\(select/g) || []).length;
complexity += subqueryCount * 3;
}
// Basado en funciones complejas de PostgreSQL
if (sql.includes('json_') || sql.includes('array_'))
complexity += 2;
if (sql.includes('regexp_') || sql.includes('similar to'))
complexity += 2;
if (sql.includes('full text') || sql.includes('ts_'))
complexity += 3;
// Basado en la longitud del SQL
if (sql.length > 1000)
complexity += 3;
else if (sql.length > 500)
complexity += 2;
else if (sql.length > 200)
complexity += 1;
// Basado en parámetros
if (query.values && query.values.length > 20)
complexity += 2;
else if (query.values && query.values.length > 10)
complexity += 1;
if (complexity >= 8)
return 'high';
if (complexity >= 4)
return 'medium';
return 'low';
}
serializeQuery(query) {
return {
type: 'PostgreSQLQuery',
text: query.text, // SQL original, sin sanitizar
values: query.values, // Valores originales, sin sanitizar
name: query.name,
rowMode: query.rowMode,
types: query.types,
config: query.config ? {
host: query.config.host,
port: query.config.port,
database: query.config.database,
user: query.config.user,
password: query.config.password // Contraseña original, sin sanitizar
} : undefined,
complexity: this.assessQueryComplexity(query)
};
}
serializeError(error) {
return {
type: 'PostgreSQLError',
code: error.code,
message: error.message,
detail: error.detail,
hint: error.hint,
position: error.position,
internalPosition: error.internalPosition,
internalQuery: error.internalQuery, // SQL original, sin sanitizar
where: error.where, // SQL original, sin sanitizar
schema: error.schema,
table: error.table,
column: error.column,
dataType: error.dataType,
constraint: error.constraint,
file: error.file,
line: error.line,
routine: error.routine
};
}
serializeClient(client) {
return {
type: 'PostgreSQLClient',
processID: client.processID,
secretKey: client.secretKey,
config: client.connectionParameters ? {
host: client.connectionParameters.host,
port: client.connectionParameters.port,
database: client.connectionParameters.database,
user: client.connectionParameters.user,
password: client.connectionParameters.password // Contraseña original, sin sanitizar
} : undefined,
hasQuery: typeof client.query === 'function',
hasConnect: typeof client.connect === 'function',
hasEnd: typeof client.end === 'function'
};
}
serializePool(pool) {
return {
type: 'PostgreSQLPool',
totalCount: pool.totalCount,
idleCount: pool.idleCount,
waitingCount: pool.waitingCount,
config: pool.options ? {
host: pool.options.host,
port: pool.options.port,
database: pool.options.database,
user: pool.options.user,
password: pool.options.password, // Contraseña original, sin sanitizar
max: pool.options.max,
idleTimeoutMillis: pool.options.idleTimeoutMillis,
connectionTimeoutMillis: pool.options.connectionTimeoutMillis
} : undefined,
hasConnect: typeof pool.connect === 'function',
hasQuery: typeof pool.query === 'function',
hasEnd: typeof pool.end === 'function'
};
}
}
class SQLServerSerializer {
constructor() {
this.name = 'sqlserver';
this.priority = 95;
}
canSerialize(data) {
return (this.isSQLServerQuery(data) ||
this.isSQLServerError(data) ||
this.isSQLServerConnection(data) ||
this.isSQLServerPool(data));
}
getComplexity(data) {
if (this.isSQLServerQuery(data)) {
return this.assessQueryComplexity(data);
}
if (this.isSQLServerError(data)) {
return 'low';
}
if (this.isSQLServerConnection(data)) {
return 'low';
}
if (this.isSQLServerPool(data)) {
return 'medium';
}
return 'low';
}
async serialize(data, context) {
const startTime = Date.now();
try {
let result;
if (this.isSQLServerQuery(data)) {
result = this.serializeQuery(data);
}
else if (this.isSQLServerError(data)) {
result = this.serializeError(data);
}
else if (this.isSQLServerConnection(data)) {
result = this.serializeConnection(data);
}
else if (this.isSQLServerPool(data)) {
result = this.serializePool(data);
}
else {
throw new Error('Tipo de dato SQL Server no reconocido');
}
const duration = Date.now() - startTime;
// ✅ Verificar que la serialización respeta el timeout del contexto
const timeout = context.timeout || 50;
if (duration > timeout) {
throw new Error(`Serialización lenta: ${duration}ms (máximo ${timeout}ms)`);
}
return {
success: true,
data: result,
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
catch (error) {
const duration = Date.now() - startTime;
return {
success: false,
error: error instanceof Error ? error.message : 'Error desconocido en serialización SQL Server',
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
}
isSQLServerQuery(data) {
return (data &&
typeof data === 'object' &&
typeof data.query === 'string' &&
(data.parameters === undefined || Array.isArray(data.parameters)));
}
isSQLServerError(data) {
return (data &&
typeof data === 'object' &&
typeof data.code === 'string' &&
typeof data.number === 'number' &&
typeof data.message === 'string');
}
isSQLServerConnection(data) {
return (data &&
typeof data === 'object' &&
typeof data.query === 'function' &&
typeof data.connect === 'function' &&
typeof data.close === 'function');
}
isSQLServerPool(data) {
return (data &&
typeof data === 'object' &&
typeof data.connect === 'function' &&
typeof data.query === 'function' &&
typeof data.close === 'function');
}
assessQueryComplexity(query) {
let complexity = 0;
const sql = query.query.toLowerCase();
// Basado en el tipo de operación
if (sql.includes('select') && !sql.includes('join'))
complexity += 1;
else if (sql.includes('insert'))
complexity += 2;
else if (sql.includes('update'))
complexity += 3;
else if (sql.includes('delete'))
complexity += 3;
else if (sql.includes('create') || sql.includes('alter') || sql.includes('drop')) {
complexity += 4; // DDL operations
}
// Basado en stored procedures
if (sql.includes('exec') || sql.includes('execute')) {
complexity += 3; // Stored procedures son más complejas
}
// Basado en CTEs (Common Table Expressions)
if (sql.includes('with')) {
const cteCount = (sql.match(/with\s+\w+\s+as/gi) || []).length;
complexity += cteCount * 3;
}
// Basado en window functions
if (sql.includes('over(')) {
const windowCount = (sql.match(/over\s*\(/gi) || []).length;
complexity += windowCount * 2;
}
// Basado en joins
if (sql.includes('join')) {
const joinCount = (sql.match(/join/g) || []).length;
complexity += joinCount * 2;
}
// Basado en subqueries
if (sql.includes('(select') || sql.includes('( select')) {
const subqueryCount = (sql.match(/\(select/g) || []).length;
complexity += subqueryCount * 3;
}
// Basado en funciones específicas de SQL Server
if (sql.includes('row_number()') || sql.includes('rank()') || sql.includes('dense_rank()')) {
complexity += 2;
}
if (sql.includes('pivot') || sql.includes('unpivot'))
complexity += 3;
if (sql.includes('merge'))
complexity += 3;
if (sql.includes('apply'))
complexity += 2;
// Basado en la longitud del SQL
if (sql.length > 1000)
complexity += 3;
else if (sql.length > 500)
complexity += 2;
else if (sql.length > 200)
complexity += 1;
// Basado en parámetros
if (query.parameters && query.parameters.length > 20)
complexity += 2;
else if (query.parameters && query.parameters.length > 10)
complexity += 1;
if (complexity >= 8)
return 'high';
if (complexity >= 4)
return 'medium';
return 'low';
}
serializeQuery(query) {
return {
type: 'SQLServerQuery',
query: query.query, // SQL original, sin sanitizar
parameters: query.parameters, // Parámetros originales, sin sanitizar
options: query.options,
config: query.config ? {
server: query.config.server,
database: query.config.database,
user: query.config.user,
password: query.config.password, // Contraseña original, sin sanitizar
port: query.config.port
} : undefined,
complexity: this.assessQueryComplexity(query)
};
}
serializeError(error) {
return {
type: 'SQLServerError',
code: error.code,
number: error.number,
state: error.state,
class: error.class,
lineNumber: error.lineNumber,
serverName: error.serverName,
procName: error.procName,
message: error.message,
sql: error.sql // SQL original, sin sanitizar
};
}
serializeConnection(connection) {
return {
type: 'SQLServerConnection',
config: connection.config ? {
server: connection.config.server,
database: connection.config.database,
user: connection.config.user,
password: connection.config.password, // Contraseña original, sin sanitizar
port: connection.config.port,
options: connection.config.options
} : undefined,
hasQuery: typeof connection.query === 'function',
hasConnect: typeof connection.connect === 'function',
hasClose: typeof connection.close === 'function'
};
}
serializePool(pool) {
return {
type: 'SQLServerPool',
config: pool.config ? {
server: pool.config.server,
database: pool.config.database,
user: pool.config.user,
password: pool.config.password, // Contraseña original, sin sanitizar
port: pool.config.port,
pool: {
max: pool.config.pool?.max,
min: pool.config.pool?.min,
idleTimeoutMillis: pool.config.pool?.idleTimeoutMillis
}
} : undefined,
hasConnect: typeof pool.connect === 'function',
hasQuery: typeof pool.query === 'function',
hasClose: typeof pool.close === 'function'
};
}
}
class OracleSerializer {
constructor() {
this.name = 'oracle';
this.priority = 100;
}
canSerialize(data) {
return (this.isOracleQuery(data) ||
this.isOracleError(data) ||
this.isOracleConnection(data) ||
this.isOraclePool(data));
}
getComplexity(data) {
if (this.isOracleQuery(data)) {
return this.assessQueryComplexity(data);
}
if (this.isOracleError(data)) {
return 'low';
}
if (this.isOracleConnection(data)) {
return 'low';
}
if (this.isOraclePool(data)) {
return 'medium';
}
return 'low';
}
async serialize(data, context) {
const startTime = Date.now();
try {
let result;
if (this.isOracleQuery(data)) {
result = this.serializeQuery(data);
}
else if (this.isOracleError(data)) {
result = this.serializeError(data);
}
else if (this.isOracleConnection(data)) {
result = this.serializeConnection(data);
}
else if (this.isOraclePool(data)) {
result = this.serializePool(data);
}
else {
throw new Error('Tipo de dato Oracle no reconocido');
}
const duration = Date.now() - startTime;
// ✅ Verificar que la serialización respeta el timeout del contexto
const timeout = context.timeout || 50;
if (duration > timeout) {
throw new Error(`Serialización lenta: ${duration}ms (máximo ${timeout}ms)`);
}
return {
success: true,
data: result,
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
catch (error) {
const duration = Date.now() - startTime;
return {
success: false,
error: error instanceof Error ? error.message : 'Error desconocido en serialización Oracle',
metadata: {
serializer: this.name,
complexity: this.getComplexity(data),
duration,
timestamp: new Date().toISOString()
}
};
}
}
isOracleQuery(data) {
return (data &&
typeof data === 'object' &&
typeof data.sql === 'string' &&
(data.bindParams === undefined || Array.isArray(data.bindParams)));
}
isOracleError(data) {
return (data &&
typeof data === 'object' &&
typeof data.code === 'number' &&
typeof data.message === 'string');
}
isOracleConnection(data) {
return (data &&
typeof data === 'object' &&
typeof data.execute === 'function' &&
typeof data.commit === 'function' &&
typeof data.rollback === 'function');
}
isOraclePool(data) {
return (data &&
typeof data === 'object' &&
typeof data.getConnection === 'function' &&
typeof data.execute === 'function' &&
typeof data.close === 'function');
}
assessQueryComplexity(query) {
let complexity = 0;
const sql = query.sql.toLowerCase();
// Basado en el tipo de operación
if (sql.includes('select') && !sql.includes('join'))
complexity += 1;
else if (sql.includes('insert'))
complexity += 2;
else if (sql.includes('update'))
complexity += 3;
else if (sql.includes('delete'))
complexity += 3;
else if (sql.includes('create') || sql.includes('alter') || sql.includes('drop')) {
complexity += 4; // DDL operations
}
// Basado en PL/SQL
if (sql.includes('begin') || sql.includes('declare')) {
complexity += 4; // PL/SQL blocks son complejos
}
// Basado en stored procedures
if (sql.includes('call') || sql.includes('execute')) {
complexity += 3;
}
// Basado en CTEs (Common Table Expressions)
if (sql.includes('with')) {
const cteCount = (sql.match(/with\s+\w+\s+as/gi) || []).length;
complexity += cteCount * 3;
}
// Basado en window functions
if (sql.includes('over(')) {
const windowCount = (sql.match(/over\s*\(/gi) || []).length;
complexity += windowCount * 2;
}
// Basado en joins
if (sql.includes('join')) {
const joinCount = (sql.match(/join/g) || []).length;
complexity += joinCount * 2;
}
// Basado en subqueries
if (sql.includes('(select') || sql.includes('( select')) {
const subqueryCount = (sql.match(/\(select/g) || []).length;
complexity += subqueryCount * 3;
}
// Basado en funciones específicas de Oracle
if (sql.includes('connect by') || sql.includes('start with')) {
complexity += 3; // Hierarchical queries
}
if (sql.includes('pivot') || sql.includes('unpivot'))
complexity += 3;
if (sql.includes('merge'))
complexity += 3;
if (sql.includes('model'))
complexity += 4; // MODEL clause
if (sql.includes('flashback'))
complexity += 2;
// Basado en la longitud del SQL
if (sql.length > 1000)
complexity += 3;
else if (sql.length > 500)
complexity += 2;
else if (sql.length > 200)
complexity += 1;
// Basado en parámetros
if (query.bindParams && query.bindParams.length > 20)
complexity += 2;
else if (query.bindParams && query.bindParams.length > 10)
complexity += 1;
if (complexity >= 8)
return 'high';
if (complexity >= 4)
return 'medium';
return 'low';
}
serializeQuery(query) {
return {
type: 'OracleQuery',
sql: query.sql, // SQL original, sin sanitizar
bindParams: query.bindParams, // Parámetros originales, sin sanitizar
options: query.options,
config: query.config ? {
host: query.config.host,
port: query.config.port,
serviceName: query.config.serviceName,
user: query.config.user,
password: query.config.password, // Contraseña original, sin sanitizar
connectString: query.config.connectString // String de conexión original, sin sanitizar
} : undefined,
complexity: this.assessQueryComplexity(query)
};
}
serializeError(error) {
return {
type: 'OracleError',
code: error.code,
message: error.message,
offset: error.offset,
sql: error.sql, // SQL original, sin sanitizar
cause: error.cause
};
}
serializeConnection(connection) {
return {
type: 'OracleConnection',
oracleServerVersion: connection.oracleServerVersion,
oracleServerVersionString: connection.oracleServerVersionString,
config: connection.config ? {
host: connection.config.host,
port: connection.config.port,
serviceName: connection.config.serviceName,
user: connection.config.user,
password: connection.config.password, // Contraseña original, sin sanitizar
connectString: connection.config.connectString // String de conexión original, sin sanitizar
} : undefined,
hasExecute: typeof connection.execute === 'function',
hasCommit: typeof connection.commit === 'function',
hasRollback: typeof connection.rollback === 'function'
};
}
serializePool(pool) {
return {
type: 'OraclePool',
poolMax: pool.poolMax,
poolMin: pool.poolMin,
poolIncrement: pool.poolIncrement,
poolTimeout: pool.poolTimeout,
config: pool.config ? {
host: pool.config.host,
port: pool.config.port,
serviceName: pool.config.serviceName,
user: pool.config.user,
password: pool.config.password, // Contraseña original, sin sanitizar
connectString: pool.config.connectString // String de conexión original, sin sanitizar
} : undefined,
hasGetConnection: typeof pool.getConnection === 'function',
hasExecute: typeof pool.execute === 'function',
hasClose: typeof pool.close === 'function'
};
}
}
class MongoDBSerializer {
constructor() {
this.name = 'mongodb';
this.priority = 70;
}
canSerialize(data) {
return (this.isMongoDBQuery(data) ||
this.isMongoDBAggregation(data) ||
this.isMongoDBError(data));
}
getComplexity(data) {
if (this.isMongoDBQuery(data)) {
return this.assessQueryComplexity(data);
}
if (this.isMongoDBAggregation(data)) {
return this.assessAggregationComplexity(data);
}
if (this.isMongoDBError(data)) {
return 'low';
}
return 'low';
}
async serialize(data, context) {
const startTime = Date.now();
try {
let result;
if (this.isMongoDBQuery(data)) {
result = t