UNPKG

@eristack/person

Version:

Structured person name and gender identity — normalize and display, not HRIS

1 lines • 5.95 kB
{"version":3,"sources":["../src/index.ts","../src/core/errors.ts","../src/core/types.ts","../src/core/person.ts"],"sourcesContent":["export {\n formatPersonDisplay,\n formatPersonSortable,\n GENDER_IDENTITIES,\n isGenderIdentity,\n normalizeGenderIdentity,\n normalizePerson,\n normalizePersonName,\n PERSON_PARSE_CODE,\n PersonParseError,\n} from \"./core/index.js\";\nexport type {\n GenderIdentity,\n Person,\n PersonFormatOptions,\n PersonName,\n} from \"./core/index.js\";\n","export const PERSON_PARSE_CODE = \"PERSON_PARSE\" as const;\n\nexport class PersonParseError extends Error {\n readonly code = PERSON_PARSE_CODE;\n\n constructor(message: string) {\n super(message);\n this.name = \"PersonParseError\";\n }\n}\n","export type GenderIdentity =\n | \"unknown\"\n | \"woman\"\n | \"man\"\n | \"non_binary\"\n | \"prefer_not_to_say\"\n | \"other\";\n\nexport const GENDER_IDENTITIES: readonly GenderIdentity[] = [\n \"unknown\",\n \"woman\",\n \"man\",\n \"non_binary\",\n \"prefer_not_to_say\",\n \"other\",\n] as const;\n\nexport type PersonName = {\n given: string;\n family: string;\n middle?: string;\n prefix?: string;\n suffix?: string;\n};\n\nexport type Person = {\n name: PersonName;\n gender?: GenderIdentity;\n /** Free text when `gender` is `other`. */\n genderOther?: string;\n};\n\nexport type PersonFormatOptions = {\n /** List/sort as \"Family, Given\" (default false = display order). */\n familyFirst?: boolean;\n};\n","import { PersonParseError } from \"./errors.js\";\nimport type {\n GenderIdentity,\n Person,\n PersonFormatOptions,\n PersonName,\n} from \"./types.js\";\nimport { GENDER_IDENTITIES } from \"./types.js\";\n\nfunction trimOptional(value: string | undefined): string | undefined {\n const t = value?.trim();\n return t ? t : undefined;\n}\n\nfunction trimRequired(value: string, field: string): string {\n const t = value.trim();\n if (!t) {\n throw new PersonParseError(`${field} is required`);\n }\n return t;\n}\n\nexport function normalizePersonName(input: PersonName): PersonName {\n return {\n given: trimRequired(input.given, \"name.given\"),\n family: trimRequired(input.family, \"name.family\"),\n middle: trimOptional(input.middle),\n prefix: trimOptional(input.prefix),\n suffix: trimOptional(input.suffix),\n };\n}\n\nexport function isGenderIdentity(value: string): value is GenderIdentity {\n return (GENDER_IDENTITIES as readonly string[]).includes(value);\n}\n\nexport function normalizeGenderIdentity(value: string): GenderIdentity {\n const normalized = value.trim().toLowerCase().replace(/-/g, \"_\") as GenderIdentity;\n if (!isGenderIdentity(normalized)) {\n throw new PersonParseError(`Invalid gender identity \"${value}\"`);\n }\n return normalized;\n}\n\nexport function normalizePerson(input: Person): Person {\n const name = normalizePersonName(input.name);\n const gender = input.gender ? normalizeGenderIdentity(input.gender) : undefined;\n const genderOther = trimOptional(input.genderOther);\n\n if (gender === \"other\" && !genderOther) {\n throw new PersonParseError(\"genderOther is required when gender is other\");\n }\n if (gender !== \"other\" && genderOther) {\n throw new PersonParseError(\"genderOther is only allowed when gender is other\");\n }\n\n return { name, gender, genderOther };\n}\n\nexport function formatPersonDisplay(\n person: Person,\n options: PersonFormatOptions = {},\n): string {\n const p = normalizePerson(person);\n if (options.familyFirst) {\n return formatPersonSortable(p);\n }\n const parts = [\n p.name.prefix,\n p.name.given,\n p.name.middle,\n p.name.family,\n p.name.suffix,\n ].filter(Boolean);\n return parts.join(\" \");\n}\n\nexport function formatPersonSortable(person: Person): string {\n const p = normalizePerson(person);\n const givenParts = [p.name.given, p.name.middle].filter(Boolean).join(\" \");\n const family = p.name.family;\n const suffix = p.name.suffix ? ` ${p.name.suffix}` : \"\";\n return `${family}${suffix}, ${givenParts}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,oBAAoB;AAE1B,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACjC,OAAO;AAAA,EAEhB,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACDO,IAAM,oBAA+C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACNA,SAAS,aAAa,OAA+C;AACnE,QAAM,IAAI,OAAO,KAAK;AACtB,SAAO,IAAI,IAAI;AACjB;AAEA,SAAS,aAAa,OAAe,OAAuB;AAC1D,QAAM,IAAI,MAAM,KAAK;AACrB,MAAI,CAAC,GAAG;AACN,UAAM,IAAI,iBAAiB,GAAG,KAAK,cAAc;AAAA,EACnD;AACA,SAAO;AACT;AAEO,SAAS,oBAAoB,OAA+B;AACjE,SAAO;AAAA,IACL,OAAO,aAAa,MAAM,OAAO,YAAY;AAAA,IAC7C,QAAQ,aAAa,MAAM,QAAQ,aAAa;AAAA,IAChD,QAAQ,aAAa,MAAM,MAAM;AAAA,IACjC,QAAQ,aAAa,MAAM,MAAM;AAAA,IACjC,QAAQ,aAAa,MAAM,MAAM;AAAA,EACnC;AACF;AAEO,SAAS,iBAAiB,OAAwC;AACvE,SAAQ,kBAAwC,SAAS,KAAK;AAChE;AAEO,SAAS,wBAAwB,OAA+B;AACrE,QAAM,aAAa,MAAM,KAAK,EAAE,YAAY,EAAE,QAAQ,MAAM,GAAG;AAC/D,MAAI,CAAC,iBAAiB,UAAU,GAAG;AACjC,UAAM,IAAI,iBAAiB,4BAA4B,KAAK,GAAG;AAAA,EACjE;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,OAAuB;AACrD,QAAM,OAAO,oBAAoB,MAAM,IAAI;AAC3C,QAAM,SAAS,MAAM,SAAS,wBAAwB,MAAM,MAAM,IAAI;AACtE,QAAM,cAAc,aAAa,MAAM,WAAW;AAElD,MAAI,WAAW,WAAW,CAAC,aAAa;AACtC,UAAM,IAAI,iBAAiB,8CAA8C;AAAA,EAC3E;AACA,MAAI,WAAW,WAAW,aAAa;AACrC,UAAM,IAAI,iBAAiB,kDAAkD;AAAA,EAC/E;AAEA,SAAO,EAAE,MAAM,QAAQ,YAAY;AACrC;AAEO,SAAS,oBACd,QACA,UAA+B,CAAC,GACxB;AACR,QAAM,IAAI,gBAAgB,MAAM;AAChC,MAAI,QAAQ,aAAa;AACvB,WAAO,qBAAqB,CAAC;AAAA,EAC/B;AACA,QAAM,QAAQ;AAAA,IACZ,EAAE,KAAK;AAAA,IACP,EAAE,KAAK;AAAA,IACP,EAAE,KAAK;AAAA,IACP,EAAE,KAAK;AAAA,IACP,EAAE,KAAK;AAAA,EACT,EAAE,OAAO,OAAO;AAChB,SAAO,MAAM,KAAK,GAAG;AACvB;AAEO,SAAS,qBAAqB,QAAwB;AAC3D,QAAM,IAAI,gBAAgB,MAAM;AAChC,QAAM,aAAa,CAAC,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AACzE,QAAM,SAAS,EAAE,KAAK;AACtB,QAAM,SAAS,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,MAAM,KAAK;AACrD,SAAO,GAAG,MAAM,GAAG,MAAM,KAAK,UAAU;AAC1C;","names":[]}