Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { isGs1CheckDigitValid } from "./validation_gs1_check_digit.ts"; ← from validation.gs1-check-digit ^1.0.0 · built alongside by fune
import { type GtinCheck } from "./validation_gtin_types.ts";
/** The four GTIN lengths, and what each is called. */
const KINDS: { readonly [length: number]: string } = { 8: "ean-8", 12: "upc-a", 13: "ean-13", 14: "gtin-14" };
function invalid(reason: string): GtinCheck {
return { valid: false, normalised: null, kind: null, gtin14: null, reason };
}
/**
* Check an EAN-8, UPC-A, EAN-13 or GTIN-14 against the GS1 mod-10 check digit.
*
* A pass means the number was typed correctly, not that GS1 issued it.
* Validators answer rather than throw: a malformed value is the answer "no".
*/
export function validateGtin(value: string): GtinCheck {
if (typeof value !== "string") return invalid("empty");
// Barcodes are printed in groups, so spaces and hyphens are ignored anywhere.
const chars: string[] = [];
for (const ch of value) {
if (ch !== " " && ch !== "-") chars.push(ch);
}
if (chars.length === 0) return invalid("empty");
for (const ch of chars) {
if (ch < "0" || ch > "9") return invalid("bad-character");
}
const digits = chars.join("");
const kind = KINDS[digits.length];
if (kind === undefined) return invalid("bad-length");
// Zero everywhere satisfies the arithmetic and has never been allocated.
if (/^0+$/.test(digits)) return invalid("all-zero");
// The GS1 mod-10 check, weighted from the right, which is what makes one
// rule serve all four lengths (EAN-13's first digit gets 1, EAN-8's and
// UPC-A's get 3).
if (!isGs1CheckDigitValid(digits)) return invalid("bad-check-digit");
return { valid: true, normalised: digits, kind, gtin14: digits.padStart(14, "0"), reason: null };
}