1import { CreateCartV2Mutation } from './graphql-queries.js'; 2 3export async function makeGraphQLRequest(body) { 4 const currentURL = window.location.href; 5 const isStaging = 6 currentURL.includes('staging.mijndomein.nl') || 7 currentURL.includes('dev.mijndomein.nl'); 8 const currentEnv = isStaging ? 'staging.mijndomein.nl' : 'mijndomein.nl'; 9 const subdomain = 'main-graphql.'; 10 const fetchUrl = `https://${subdomain}${currentEnv}/graphql`; 11 12 return await fetch(fetchUrl, { 13 headers: { 14 accept: '*/*', 15 'accept-language': 'en-US,en;q=0.9', 16 'content-type': 'application/json', 17 priority: 'u=1, i', 18 'sec-fetch-dest': 'empty', 19 'sec-fetch-mode': 'cors', 20 'sec-fetch-site': 'same-site', 21 }, 22 referrer: `https://www.${currentEnv}/`, 23 referrerPolicy: 'strict-origin-when-cross-origin', 24 body: body, 25 method: 'POST', 26 mode: 'cors', 27 credentials: 'include', 28 }); 29} 30 31export function createDomainPurchase(searchDomainText, extension, sku) { 32 return { 33 channel: 'SHOP', 34 product: { 35 currentIdentifier: '', 36 sku: sku, 37 data: { 38 domain: { 39 extension: extension, 40 name: searchDomainText, 41 }, 42 }, 43 }, 44 }; 45} 46 47export function createBundlePurchase(searchDomainText, extension, bundle, additionalProducts = []) { 48 const bundleSku = bundle?.split('-')[1]; 49 50 if (!bundleSku) { 51 throw new Error('No bundle sku found'); 52 } 53 54 const products = [ 55 { 56 sku: bundleSku, 57 data: { 58 domain: { 59 extension: extension, 60 name: searchDomainText, 61 }, 62 }, 63 }, 64 ...additionalProducts.map(product => ({ 65 sku: product.sku, 66 data: { 67 domain: { 68 extension: extension, 69 name: searchDomainText, 70 }, 71 }, 72 })) 73 ]; 74 75 return { 76 channel: 'SHOP', 77 bundle: { 78 rgu: bundle, 79 products: products, 80 }, 81 }; 82} 83 84export function createAddonPurchase(searchDomainText, extension, addon, sku) { 85 return { 86 channel: 'SHOP', 87 addon: { 88 currentIdentifier: '', 89 sku: addon, 90 productSku: sku, 91 data: { 92 domain: { 93 name: searchDomainText, 94 extension: extension, 95 }, 96 }, 97 }, 98 }; 99} 100 101export function buildCartRequest(promoCodes, purchases, options = {}) { 102 const paymentPlanGroup = options.paymentPlanGroup || 'MONTHLY'; 103 const displayPriceAs = options.displayPriceAs || 'YEARLY'; 104 105 return { 106 operationName: 'CreateCartV2', 107 variables: { 108 cartInput: { 109 promoCodes, 110 purchases, 111 context: 'SALE', 112 preferredPaymentPlanGroup: paymentPlanGroup, 113 }, 114 includeBundles: false, 115 includeAddons: false, 116 channel: 'SHOP', 117 displayPriceAs, 118 paymentPlanGroup, 119 }, 120 query: CreateCartV2Mutation, 121 }; 122} 123 124/** 125 * Normalize a customer-entered domain to a single registrable label. 126 * Uses everything before the first dot; keeps Unicode for display and API conversion. 127 */ 128export function normalizeDomainLabel(inputData) { 129 if (typeof inputData !== 'string') { 130 return ''; 131 } 132 133 return inputData 134 .trim() 135 .toLowerCase() 136 .replace(/\s+/g, '') 137 .split('.')[0]; 138} 139 140/** 141 * Convert a Unicode label to ASCII/Punycode for registry and cart APIs. 142 * Returns null when the label cannot be represented as a hostname label. 143 */ 144export function toAsciiDomainLabel(label) { 145 if (!label) { 146 return null; 147 } 148 149 try { 150 const hostname = new URL(`http://${label}.invalid`).hostname; 151 const ascii = hostname.replace(/\.invalid$/i, ''); 152 return ascii || null; 153 } catch { 154 return null; 155 } 156} 157 158/** 159 * Validate a domain label after normalization. 160 * Rejects empty, overlong, leading/trailing hyphen, and invalid punctuation. 161 */ 162export function validateDomainLabel(label) { 163 if (!label) { 164 return { valid: false, message: 'Vul een domeinnaam in.' }; 165 } 166 167 if (label.length > 63) { 168 return { 169 valid: false, 170 message: 'Een domeinnaam mag maximaal 63 tekens bevatten.', 171 }; 172 } 173 174 if (label.startsWith('-') || label.endsWith('-')) { 175 return { 176 valid: false, 177 message: 'Een domeinnaam mag niet beginnen of eindigen met een koppelteken.', 178 }; 179 } 180 181 // Allow Unicode letters/numbers and hyphens; reject punctuation used as separators. 182 if (!/^[\p{L}\p{N}]+(?:-[\p{L}\p{N}]+)*$/u.test(label)) { 183 return { 184 valid: false, 185 message: 'Gebruik alleen letters, cijfers en koppeltekens in de domeinnaam.', 186 }; 187 } 188 189 if (!toAsciiDomainLabel(label)) { 190 return { 191 valid: false, 192 message: 'Deze domeinnaam kan niet worden verwerkt. Probeer een andere naam.', 193 }; 194 } 195 196 return { valid: true, message: '' }; 197} 198 199export function isNonProductionHost(hostname = window.location.hostname) { 200 return ( 201 hostname.includes('staging.mijndomein.nl') || 202 hostname.includes('dev.mijndomein.nl') || 203 hostname.includes('localhost') || 204 hostname === '127.0.0.1' 205 ); 206} 207 208export function getMetaregistrarHost(hostname = window.location.hostname) { 209 return isNonProductionHost(hostname) 210 ? 'check-ote.metaregistrar.com' 211 : 'check.metaregistrar.com'; 212} 213 214export function getMetaregistrarDomainCheckScriptUrl(hostname = window.location.hostname) { 215 return `https://${getMetaregistrarHost(hostname)}/domaincheck/dist/domaincheck.full.min.js`; 216} 217 218export function formatEuroFromCents(cents, locale = 'nl-NL') { 219 return new Intl.NumberFormat(locale, { 220 style: 'currency', 221 currency: 'EUR', 222 }).format((cents || 0) / 100); 223} 224 225const invalidCharacters = [ 226 '!', 227 '@', 228 'Å', 229 'â', 230 "'", 231 '#', 232 '$', 233 '%', 234 '^', 235 '&', 236 '*', 237 '(', 238 ')', 239 '[', 240 ']', 241 '{', 242 '}', 243 ';', 244 ':', 245 ',', 246 '"', 247 '`', 248 '~', 249 `'`, 250 '?', 251 '/', 252 '\\', 253 '=', 254 '+', 255 '<', 256 '>', 257 '_', 258 '|' 259]; 260 261export function filterInputValue(inputData) { 262 let input = inputData.toLowerCase().trim().replace(/\s+/g, '').split('.')[0];
263 invalidCharacters.forEach((character) => { 264 input = input.split(character).join(''); 265 }); 266 return input; 267}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.