@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
146 lines • 5.01 kB
JavaScript
import { FIELDS } from '../generated/fields.generated';
import { getLogger } from '../logging/Logger.js';
/**
* Field Serialization Helper
*
* Utilities for handling object/array field serialization before SQLite storage
* to prevent "SQLite3 can only bind numbers, strings, bigints, buffers, and null" errors
*/
// Types that require JSON serialization before database storage
const SERIALIZABLE_TYPES = ['object', 'array', 'any'];
// Types that can be stored directly in SQLite
const DIRECT_TYPES = ['string', 'integer', 'boolean', 'number'];
/**
* High-risk field name patterns that commonly cause serialization issues
*/
const HIGH_RISK_PATTERNS = [
'conditions', // Audience/page targeting conditions
'audience_conditions', // Experiment audience logic
'url_targeting', // URL matching rules
'environments', // Per-environment configurations
'variable_values', // Feature flag variable assignments
'actions', // DOM manipulation actions
'metrics', // Conversion tracking definitions
'variations', // Experiment variation configurations
'rules', // Targeting rule definitions
'traffic_allocation' // Traffic distribution settings
];
/**
* Check if a field requires serialization for a specific entity
*/
export function requiresSerialization(entityType, fieldName) {
// Type guard to check if entityType is valid
if (!(entityType in FIELDS))
return false;
const entitySchema = FIELDS[entityType];
const fieldType = entitySchema.fieldTypes[fieldName];
return fieldType ? SERIALIZABLE_TYPES.includes(fieldType) : false;
}
/**
* Serialize a value for database storage if needed
*/
export function serializeForDatabase(entityType, fieldName, value) {
if (value === null || value === undefined) {
return null;
}
if (requiresSerialization(entityType, fieldName)) {
try {
return JSON.stringify(value);
}
catch (error) {
getLogger().warn({ entityType, fieldName, error: String(error) }, `Failed to serialize field`);
return value;
}
}
return value;
}
/**
* Deserialize a value from database if needed
*/
export function deserializeFromDatabase(entityType, fieldName, value) {
if (value === null || value === undefined) {
return null;
}
if (requiresSerialization(entityType, fieldName) && typeof value === 'string') {
try {
return JSON.parse(value);
}
catch (error) {
// If parsing fails, return the original value
getLogger().warn({ entityType, fieldName, error: String(error) }, `Failed to deserialize field`);
return value;
}
}
return value;
}
/**
* Get all complex fields for an entity type
*/
export function getComplexFieldsForEntity(entityType) {
const entitySchema = FIELDS[entityType];
if (!entitySchema)
return [];
return Object.entries(entitySchema.fieldTypes)
.filter(([_, fieldType]) => SERIALIZABLE_TYPES.includes(fieldType))
.map(([fieldName, _]) => fieldName);
}
/**
* Serialize an entire entity object for database storage
*/
export function serializeEntityForDatabase(entityType, entityData) {
const serialized = {};
for (const [fieldName, value] of Object.entries(entityData)) {
serialized[fieldName] = serializeForDatabase(entityType, fieldName, value);
}
return serialized;
}
/**
* Deserialize an entire entity object from database storage
*/
export function deserializeEntityFromDatabase(entityType, entityData) {
const deserialized = {};
for (const [fieldName, value] of Object.entries(entityData)) {
deserialized[fieldName] = deserializeFromDatabase(entityType, fieldName, value);
}
return deserialized;
}
/**
* Check if a field is high-risk for serialization issues
*/
export function isHighRiskField(fieldName) {
return HIGH_RISK_PATTERNS.some(pattern => fieldName.includes(pattern));
}
/**
* Generate serialization matrix for debugging
*/
export function generateSerializationMatrix() {
const matrix = {};
for (const [entityName, entitySchema] of Object.entries(FIELDS)) {
const complexFields = {};
const criticalFields = [];
for (const [fieldName, fieldType] of Object.entries(entitySchema.fieldTypes)) {
if (SERIALIZABLE_TYPES.includes(fieldType)) {
complexFields[fieldName] = fieldType;
if (isHighRiskField(fieldName)) {
criticalFields.push(fieldName);
}
}
}
if (Object.keys(complexFields).length > 0) {
matrix[entityName] = {
complexFields,
criticalFields
};
}
}
return matrix;
}
/**
* Export constants for external use
*/
export const SERIALIZATION_CONSTANTS = {
SERIALIZABLE_TYPES,
DIRECT_TYPES,
HIGH_RISK_PATTERNS
};
//# sourceMappingURL=FieldSerializationHelper.js.map