// The HTML autofill vocabulary — the basis for WCAG 1.3.5 "Identify Input Purpose".
//
// 1.3.5 asks that a field collecting information ABOUT THE USER declare its purpose with a
// token from the WCAG "Input Purposes for User Interface Components" list, which is a
// subset of HTML's autofill field names. A misspelled or invented token is worse than no
// token at all: it looks compliant, silently autofills nothing and helps nobody.
//
// Source: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill
// https://www.w3.org/TR/WCAG22/#input-purposes
/** Autofill field names that identify information about the user (WCAG §7). */
export const AUTOFILL_FIELD_NAMES = new Set([
"name",
"honorific-prefix",
"given-name",
"additional-name",
"family-name",
"honorific-suffix",
"nickname",
"organization-title",
"username",
"new-password",
"current-password",
"one-time-code",
"organization",
"street-address",
"address-line1",
"address-line2",
"address-line3",
"address-level4",
"address-level3",
"address-level2",
"address-level1",
"country",
"country-name",
"postal-code",
"cc-name",
"cc-given-name",
"cc-additional-name",
"cc-family-name",
"cc-number",
"cc-exp",
"cc-exp-month",
"cc-exp-year",
"cc-csc",
"cc-type",
"transaction-currency",
"transaction-amount",
"language",
"bday",
"bday-day",
"bday-month",
"bday-year",
"sex",
"url",
"photo",
"tel",
"tel-country-code",
"tel-national",
"tel-area-code",
"tel-local",
"tel-local-prefix",
"tel-local-suffix",
"tel-extension",
"email",
"impp",
]);
// Contact-type qualifiers, legal only in front of tel/email/impp-family field names.
const CONTACT_TOKENS = new Set(["home", "work", "mobile", "fax", "pager"]);
// Address-purpose qualifiers, legal in front of any field name.
const PURPOSE_TOKENS = new Set(["shipping", "billing"]);
// Field names that accept a contact qualifier.
const CONTACTABLE = /^(tel|email|impp)(-|$)/;
/** Is `value` a well-formed autocomplete attribute? Follows the HTML grammar:
* `[section-*] [shipping|billing] [home|work|mobile|fax|pager] [webauthn]`,
* plus the standalone `on` / `off` switches. */
export function isValidAutocomplete(value: string): boolean {
const tokens = value.trim().toLowerCase().split(/\s+/).filter(Boolean);
if (!tokens.length) return false;
if (tokens.length === 1 && (tokens[0] === "on" || tokens[0] === "off")) return true;
let i = 0;
if (tokens[i]?.startsWith("section-") && tokens[i]!.length > "section-".length) i++;
if (tokens[i] && PURPOSE_TOKENS.has(tokens[i]!)) i++;
const contactAt = i;
if (tokens[i] && CONTACT_TOKENS.has(tokens[i]!)) i++;
const field = tokens[i];
if (!field || !AUTOFILL_FIELD_NAMES.has(field)) return false;
// A contact qualifier only makes sense on the tel/email/impp family.
if (contactAt !== i && !CONTACTABLE.test(field)) return false;
i++;
if (tokens[i] === "webauthn") i++;
return i === tokens.length;
}
/** Credential fields whose autofill must NOT be suppressed: blocking the password manager
* is what WCAG 2.2's 3.3.8 Accessible Authentication is about. */
export const CREDENTIAL_FIELDS = new Set(["username", "current-password", "new-password", "one-time-code", "email"]);