Initial release of Barcode Tools 0.1.0

This commit is contained in:
2026-09-01 02:55:06 +02:00
commit 81677e1134
58 changed files with 9200 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
export type PayloadKind =
"text" | "url" | "email" | "sms" | "wifi" | "vcard" | "geo";
export interface PayloadFields {
primary: string;
secondary?: string;
tertiary?: string;
hidden?: boolean;
}
function escapeWifi(value: string): string {
return value.replace(/[\\;,:"]/gu, (character) => `\\${character}`);
}
function escapeVcard(value: string): string {
return value
.replaceAll("\\", "\\\\")
.replaceAll("\n", "\\n")
.replaceAll(";", "\\;")
.replaceAll(",", "\\,");
}
export function buildStructuredPayload(
kind: PayloadKind,
fields: PayloadFields,
): string {
const primary = fields.primary.trim();
if (!primary) throw new Error("The primary value is required.");
switch (kind) {
case "text":
return fields.primary;
case "url": {
const url = new URL(primary);
if (!["http:", "https:"].includes(url.protocol))
throw new Error("The URL builder accepts HTTP and HTTPS URLs only.");
return url.href;
}
case "email":
return `mailto:${encodeURIComponent(primary)}${fields.secondary ? `?subject=${encodeURIComponent(fields.secondary)}` : ""}`;
case "sms":
return `SMSTO:${primary}:${fields.secondary ?? ""}`;
case "wifi": {
const supplied = (fields.tertiary || "WPA").trim();
if (!/^(?:WPA|WEP|nopass)$/iu.test(supplied))
throw new Error("Wi-Fi security must be WPA, WEP, or nopass.");
const security =
supplied.toLowerCase() === "nopass" ? "nopass" : supplied.toUpperCase();
return `WIFI:T:${security};S:${escapeWifi(primary)};P:${escapeWifi(fields.secondary ?? "")};H:${fields.hidden ? "true" : "false"};;`;
}
case "vcard":
return [
"BEGIN:VCARD",
"VERSION:3.0",
`FN:${escapeVcard(primary)}`,
fields.secondary ? `TEL:${escapeVcard(fields.secondary)}` : "",
fields.tertiary ? `EMAIL:${escapeVcard(fields.tertiary)}` : "",
"END:VCARD",
]
.filter(Boolean)
.join("\r\n");
case "geo": {
const latitude = Number(primary);
const longitude = Number(fields.secondary);
if (
!Number.isFinite(latitude) ||
latitude < -90 ||
latitude > 90 ||
!Number.isFinite(longitude) ||
longitude < -180 ||
longitude > 180
)
throw new Error("Latitude must be 90…90 and longitude 180…180.");
return `geo:${latitude},${longitude}`;
}
}
}
export function gtinCheckDigit(body: string): number {
if (!/^\d{1,17}$/u.test(body))
throw new Error("A GTIN body must contain 117 decimal digits.");
let sum = 0;
for (
let index = body.length - 1, position = 0;
index >= 0;
index -= 1, position += 1
)
sum += Number(body[index]) * (position % 2 === 0 ? 3 : 1);
return (10 - (sum % 10)) % 10;
}
export function validateGtin(value: string): {
valid: boolean;
expected: string;
} {
if (![8, 12, 13, 14].includes(value.length) || !/^\d+$/u.test(value))
return {
valid: false,
expected: "GTIN length must be 8, 12, 13, or 14 digits.",
};
const expected = String(gtinCheckDigit(value.slice(0, -1)));
return { valid: value.endsWith(expected), expected };
}