Skip to main content
Glama
validateDomainsField.ts4.49 kB
import * as yaml from 'yaml'; import { ValidationContext, OverlappingDomains } from '../types.js'; import { isValidDomainFormat } from './domainUtils.js'; /** * Validates domains field according to LAML specification */ export function validateDomainsField(context: ValidationContext, metaMap: yaml.YAMLMap): void { const { session } = context; const domainsItem = metaMap.items.find(item => yaml.isScalar(item.key) && item.key.value === 'domains' ); if (!domainsItem) { session.logger.addError({ code: 'LAML_META_DOMAINS_MISSING', message: 'domains field is required in $meta section', context: { missingField: 'domains' } }); return; } if (!yaml.isSeq(domainsItem.value)) { session.logger.addError({ code: 'LAML_META_DOMAINS_INVALID_TYPE', message: '$meta.domains must be an array', context: { actualType: typeof domainsItem?.value || 'undefined' } }); return; } const domainsSeq = domainsItem.value; // Check if domains array is empty if (domainsSeq.items.length === 0) { session.logger.addError({ code: 'LAML_DOMAINS_EMPTY', message: '$meta.domains array cannot be empty - at least one domain is required', context: { domainsCount: 0 } }); return; } const domains: string[] = []; // Validate each domain for (const item of domainsSeq.items) { if (!yaml.isScalar(item) || typeof item.value !== 'string') { session.logger.addError({ code: 'LAML_DOMAIN_INVALID_TYPE', message: 'All domain values must be strings', context: { actualType: typeof item } }); continue; } const domain = item.value; domains.push(domain); // Validate domain format (dot notation, camelCase, max 4 levels) if (!isValidDomainFormat(domain)) { session.logger.addError({ code: 'LAML_DOMAIN_INVALID_FORMAT', message: `Invalid domain format - must be dot notation with camelCase (maximum of 4 levels): ${domain}`, context: { domain, expected: 'level1.level2.level3.level4 (maximum of 4 levels, camelCase)' } }); } } // Auto-fix duplicate domains by removing duplicates const uniqueDomains = new Set(domains); if (uniqueDomains.size !== domains.length) { const duplicates = domains.filter((domain, index) => domains.indexOf(domain) !== index); // Remove duplicate items from the YAML sequence, keeping only the first occurrence const indicesToRemove: number[] = []; for (let i = domains.length - 1; i >= 0; i--) { const domain = domains[i]; if (domains.indexOf(domain) !== i) { indicesToRemove.push(i); } } // Remove duplicates from the YAML sequence for (const index of indicesToRemove) { domainsSeq.items.splice(index, 1); } // Update domains array to reflect the changes domains.splice(0, domains.length, ...Array.from(uniqueDomains)); // Track the auto-fix context.autoFixManager.add(`Removed duplicate domains: ${duplicates.join(', ')}`); } // Validate maximum 3 domains if (domains.length > 3) { session.logger.addError({ code: 'LAML_DOMAINS_COUNT_EXCEEDED', message: `Maximum 3 domains allowed, found ${domains.length}`, context: { domainsCount: domains.length, maxAllowed: 3, domains } }); } // Validate non-overlapping domains const overlappingPairs = findOverlappingDomains(domains); for (const pair of overlappingPairs) { session.logger.addError({ code: 'LAML_DOMAINS_OVERLAPPING', message: `Found overlapping domains: ${pair.domain1} and ${pair.domain2}`, context: { domain1: pair.domain1, domain2: pair.domain2, explanation: 'Domain paths must not be subsets of each other' } }); } } /** * Finds overlapping domains (where one is a subset of another) */ function findOverlappingDomains(domains: string[]): OverlappingDomains[] { const overlapping: OverlappingDomains[] = []; for (let i = 0; i < domains.length; i++) { for (let j = i + 1; j < domains.length; j++) { const domain1 = domains[i]; const domain2 = domains[j]; // Check if one domain is a prefix of another if (domain1.startsWith(domain2 + '.') || domain2.startsWith(domain1 + '.')) { overlapping.push({ domain1, domain2 }); } } } return overlapping; }

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/EgorKluch/mcp-laml'

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