import { findByPath } from './utils.js'; const USER_DOMAIN = '_'; const WWW_BASE_DOMAIN = 'www._base_domain_'; const DOMAINS = ['wix.com', 'editorx.com']; const WIX_API_DOMAINS = ['42.wixprod.net', 'uw2-edt-1.wixprod.net']; const DEV_WIX_CODE_DOMAIN = 'dev.wix-code.com'; const REGEX_CAPTURE_PROTO_FIELD = /{(.*)}/; const REGEX_CAPTURE_DOMAINS = new RegExp(`\\.(${DOMAINS.join('|')})$`); const REGEX_CAPTURE_API_DOMAINS = new RegExp( `\\.(${WIX_API_DOMAINS.join('|')})$`, ); const REGEX_CAPTURE_DEV_WIX_CODE_DOMAIN = new RegExp( `.*\\.${DEV_WIX_CODE_DOMAIN}$`, ); export type ResolveUrlOpts = { // resource path as defined in proto file, including all wildcards & co. protoPath: string; // domain as defined on FP to its mappings (might be more than one) // www._base_domain_ => [{ srcPath: '/_api/foo', destPath: '/api' }] domainToMappings: DomainToMappings; // actual payload attached to request, needed in order to rersolve path, // in case it contains some dynamic parameters data?: object; // request's host host: string; }; export function resolveUrl(opts: ResolveUrlOpts) { const domain = resolveDomain(opts.host); const mappings = resolveMappingsByDomain(domain, opts.domainToMappings); const path = injectDataIntoProtoPath(opts.protoPath, opts.data || {}); return resolvePath(path, mappings); } function injectDataIntoProtoPath(protoPath: string, data: object) { return protoPath .split('/') .map((path) => maybeProtoPathToData(path, data)) .join('/'); } function maybeProtoPathToData(protoPath: string, data: object) { const protoRegExpMatch = protoPath.match(REGEX_CAPTURE_PROTO_FIELD) || []; const field = protoRegExpMatch[1]; if (field) { const suffix = protoPath.replace(protoRegExpMatch[0]!, ''); return findByPath(data, field, protoPath, suffix); } return protoPath; } function resolveDomain(host: string) { const resolvedHost = fixHostExceptions(host); return resolvedHost .replace(REGEX_CAPTURE_DOMAINS, '._base_domain_') .replace(REGEX_CAPTURE_API_DOMAINS, '._api_base_domain_') .replace(REGEX_CAPTURE_DEV_WIX_CODE_DOMAIN, '*.dev.wix-code.com'); } // hosts which standard string replacing doesn't apply to them, will be fixed here. function fixHostExceptions(host: string) { // https://system-kb.wixanswers.com/kb/en/article/editorx-domains-matching-to-wixcom return host.replace('create.editorx.com', 'editor.editorx.com'); } function resolveMappingsByDomain( domain: string, domainToMappings: DomainToMappings, ) { const mappings = domainToMappings[domain]; if (mappings) { return mappings; } const rootDomainMappings = resolveRootDomain(domain, domainToMappings); if (!rootDomainMappings) { if (isBaseDomain(domain)) { const wwwMappings = domainToMappings[WWW_BASE_DOMAIN]; if (wwwMappings) { return wwwMappings; } } const userMappings = domainToMappings[USER_DOMAIN]; if (userMappings) { return userMappings; } } return rootDomainMappings ?? []; } function resolveRootDomain(domain: string, domainToMappings: DomainToMappings) { return Object.entries(domainToMappings).find(([entryDomain]) => { const [_, ...rooDomainSegments] = domain.split('.'); return rooDomainSegments.join('.') === entryDomain; })?.[1]; } function resolvePath(protoPath: string, mappings: Mapping[]) { const mapping = mappings?.find((m) => protoPath.startsWith(m.destPath)); if (!mapping) { // todo: should we return the path? if no - what should we do in case of testings? return protoPath; } return mapping.srcPath + protoPath.slice(mapping.destPath.length); } type Mapping = { srcPath: string; destPath: string; }; type DomainToMappings = Record; function isBaseDomain(domain: string) { return !!domain.match(/\._base_domain_$/); }