1 import type { Item, MaybeNull } from '@proton/pass/types';
2 import { parseUrl } from '@proton/pass/utils/url/parser';
3 import { resolveDomain } from '@proton/pass/utils/url/utils';
5 export enum ItemUrlMatch {
11 /* This utility will give a score for a given login item :
12 * - priority = -1 : no match
13 * - priority = 0 : non-top level domain match
14 * - priority = 1 : direct top-level domain match */
15 export const getItemPriorityForUrl =
16 (item: Item<'login'>) =>
19 options: { protocol: MaybeNull<string>; port: MaybeNull<string>; isPrivate: boolean; strict?: boolean }
21 item.content.urls.reduce<number>((priority, url) => {
22 const parsedUrl = parseUrl(url);
24 /* if an item's domain is parsed as null then
25 * we're dealing with a corrupted url */
26 if (parsedUrl.domain === null) return priority;
27 if (options.port && parsedUrl.port !== options.port) return priority;
28 if (options.protocol && parsedUrl.protocol !== options.protocol) return priority;
31 * - If `match` is a top-level domain: only matches URLs without a subdomain
32 * - If `match` is a sub-domain: only matches on exact URL match */
33 const itemDomain = resolveDomain(parsedUrl);
34 if (options.strict && itemDomain !== match) return priority;
36 /* Check for strict domain match - this leverages
37 * the public suffix list from `tldts`. If dealing
38 * with a private domain, this will exclude private
39 * top-level domains when trying to match a private
41 const domainMatch = parsedUrl.domain === match;
43 /* If the URL we are trying to match is a non-private
44 * subdomain: allow resolving deeper subdomains for this
45 * specific subdomain. */
46 const subdomainMatch = !options?.isPrivate
47 ? parsedUrl.subdomain && parsedUrl.subdomain.endsWith(match) && match.includes(parsedUrl.domain)
48 : parsedUrl.subdomain === match;
50 /* no match -> skip */
51 if (!(domainMatch || subdomainMatch)) return priority;
55 parsedUrl.isTopLevelDomain && domainMatch ? ItemUrlMatch.TOP_MATCH : ItemUrlMatch.SUB_MATCH
57 }, ItemUrlMatch.NO_MATCH);