Skip to main content
Glama
field-dependencies.js9.72 kB
/** * Dependency Tracker - Scans and analyzes field dependencies * Detects usage in conditional logic, calculations, merge tags, and dynamic population */ export class DependencyTracker { /** * Scan form for all dependencies of a specific field * @param {object} form - Complete form object * @param {number} fieldId - Field ID to check dependencies for * @returns {object} Dependencies categorized by type */ scanFormDependencies(form, fieldId) { const dependencies = { conditionalLogic: [], calculations: [], mergeTags: [], dynamicPopulation: [] }; // 1. Check conditional logic dependencies in all fields this.scanConditionalLogic(form, fieldId, dependencies); // 2. Check calculation formula dependencies this.scanCalculations(form, fieldId, dependencies); // 3. Check merge tags in notifications and confirmations this.scanMergeTags(form, fieldId, dependencies); // 4. Check dynamic population references this.scanDynamicPopulation(form, fieldId, dependencies); return dependencies; } /** * Scan conditional logic rules for field references */ scanConditionalLogic(form, fieldId, dependencies) { form.fields?.forEach(field => { if (field.conditionalLogic?.enabled && field.conditionalLogic?.rules) { const affectedRules = field.conditionalLogic.rules.filter( rule => rule.fieldId == fieldId ); if (affectedRules.length > 0) { dependencies.conditionalLogic.push({ field_id: field.id, field_label: field.label || `Field ${field.id}`, field_type: field.type, rule_count: affectedRules.length, rules: affectedRules.map(rule => ({ operator: rule.operator, value: rule.value })) }); } } }); } /** * Scan calculation formulas for field references */ scanCalculations(form, fieldId, dependencies) { form.fields?.forEach(field => { if (field.enableCalculation && field.calculationFormula) { // Check for merge tags with this field ID // Patterns: {fieldLabel:fieldId}, {:fieldId}, {fieldId} const patterns = [ new RegExp(`\\{[^}]*:${fieldId}\\}`, 'g'), // {Label:ID} new RegExp(`\\{:${fieldId}\\}`, 'g'), // {:ID} new RegExp(`\\{${fieldId}\\}`, 'g'), // {ID} new RegExp(`\\{[^}]*:${fieldId}\\.[0-9]+\\}`, 'g'), // {Label:ID.subId} ]; let hasMatch = false; let matches = []; patterns.forEach(pattern => { const formulaMatches = field.calculationFormula.match(pattern); if (formulaMatches) { hasMatch = true; matches = matches.concat(formulaMatches); } }); if (hasMatch) { dependencies.calculations.push({ field_id: field.id, field_label: field.label || `Field ${field.id}`, field_type: field.type, formula: field.calculationFormula, matches: [...new Set(matches)] // Unique matches }); } } }); } /** * Scan merge tags in notifications and confirmations */ scanMergeTags(form, fieldId, dependencies) { // Patterns for merge tag detection const mergeTagPatterns = [ new RegExp(`\\{[^}]*:${fieldId}\\}`, 'g'), // {Label:ID} new RegExp(`\\{:${fieldId}\\}`, 'g'), // {:ID} new RegExp(`\\{${fieldId}\\}`, 'g'), // {ID} new RegExp(`\\{[^}]*:${fieldId}\\.[0-9]+\\}`, 'g'), // {Label:ID.subId} new RegExp(`\\{[^}]*:${fieldId}:[^}]*\\}`, 'g'), // {Label:ID:modifier} ]; // Check notifications if (form.notifications) { Object.entries(form.notifications).forEach(([notificationId, notification]) => { const fieldsToCheck = [ 'subject', 'message', 'from', 'fromName', 'replyTo', 'to', 'cc', 'bcc' ]; fieldsToCheck.forEach(fieldName => { if (notification[fieldName]) { const content = String(notification[fieldName]); let hasMatch = false; let matches = []; mergeTagPatterns.forEach(pattern => { const fieldMatches = content.match(pattern); if (fieldMatches) { hasMatch = true; matches = matches.concat(fieldMatches); } }); if (hasMatch) { dependencies.mergeTags.push({ location: 'notification', id: notificationId, name: notification.name || 'Unnamed Notification', field: fieldName, context: `${notification.name} - ${fieldName}`, matches: [...new Set(matches)] }); } } }); }); } // Check confirmations if (form.confirmations) { Object.entries(form.confirmations).forEach(([confirmationId, confirmation]) => { const fieldsToCheck = ['message', 'url', 'pageId', 'queryString']; fieldsToCheck.forEach(fieldName => { if (confirmation[fieldName]) { const content = String(confirmation[fieldName]); let hasMatch = false; let matches = []; mergeTagPatterns.forEach(pattern => { const fieldMatches = content.match(pattern); if (fieldMatches) { hasMatch = true; matches = matches.concat(fieldMatches); } }); if (hasMatch) { dependencies.mergeTags.push({ location: 'confirmation', id: confirmationId, name: confirmation.name || 'Default Confirmation', type: confirmation.type, field: fieldName, context: `${confirmation.name} - ${fieldName}`, matches: [...new Set(matches)] }); } } }); }); } // Check field default values and descriptions for merge tags form.fields?.forEach(field => { const fieldsToCheck = ['defaultValue', 'description', 'content']; fieldsToCheck.forEach(fieldName => { if (field[fieldName]) { const content = String(field[fieldName]); let hasMatch = false; let matches = []; mergeTagPatterns.forEach(pattern => { const fieldMatches = content.match(pattern); if (fieldMatches) { hasMatch = true; matches = matches.concat(fieldMatches); } }); if (hasMatch) { dependencies.mergeTags.push({ location: 'field', field_id: field.id, field_label: field.label || `Field ${field.id}`, property: fieldName, context: `Field ${field.id} - ${fieldName}`, matches: [...new Set(matches)] }); } } }); }); } /** * Scan for dynamic population dependencies */ scanDynamicPopulation(form, fieldId, dependencies) { // Check if the target field has dynamic population enabled const targetField = form.fields?.find(f => f.id == fieldId); if (targetField?.allowsPrepopulate && targetField?.inputName) { // Look for other fields that might reference this parameter form.fields?.forEach(field => { // Check if any field's default value references this parameter if (field.defaultValue) { const paramPattern = new RegExp(`{${targetField.inputName}}`, 'g'); if (paramPattern.test(field.defaultValue)) { dependencies.dynamicPopulation.push({ field_id: field.id, field_label: field.label || `Field ${field.id}`, parameter: targetField.inputName, usage: 'default_value' }); } } }); // Note that this field accepts dynamic population dependencies.dynamicPopulation.push({ field_id: targetField.id, field_label: targetField.label || `Field ${targetField.id}`, parameter: targetField.inputName, usage: 'accepts_population' }); } } /** * Check if dependencies would break form functionality */ hasBreakingDependencies(dependencies) { return ( dependencies.conditionalLogic.length > 0 || dependencies.calculations.length > 0 || dependencies.mergeTags.length > 0 ); } /** * Generate human-readable dependency summary */ generateDependencySummary(dependencies) { const summary = []; if (dependencies.conditionalLogic.length > 0) { summary.push(`${dependencies.conditionalLogic.length} conditional logic rule(s)`); } if (dependencies.calculations.length > 0) { summary.push(`${dependencies.calculations.length} calculation formula(s)`); } if (dependencies.mergeTags.length > 0) { summary.push(`${dependencies.mergeTags.length} merge tag reference(s)`); } if (dependencies.dynamicPopulation.length > 0) { summary.push(`${dependencies.dynamicPopulation.length} dynamic population reference(s)`); } return summary.length > 0 ? `Field has dependencies: ${summary.join(', ')}` : 'No dependencies found'; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GravityKit/gravity-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server