PageSourceSearch

https://www.mijndomein.nl/data-assets/dev-only/domain-search-helpers.js

js mijndomein.nl collected 2026-10-09 21:31:59 UTC 6,023 bytes, 267 lines download raw bytes

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.