UNPKG

@eristack/person

Version:

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

1 lines • 6.18 kB
{"version":3,"sources":["../../src/zod/index.ts","../../src/zod/schemas.ts","../../src/core/errors.ts","../../src/core/types.ts","../../src/core/person.ts"],"sourcesContent":["export { personSchema } from \"./schemas.js\";\n","import { z } from \"zod\";\n\nimport { normalizePerson } from \"../core/person.js\";\nimport { GENDER_IDENTITIES } from \"../core/types.js\";\n\nconst personNameSchema = z.object({\n given: z.string(),\n family: z.string(),\n middle: z.string().optional(),\n prefix: z.string().optional(),\n suffix: z.string().optional(),\n});\n\nexport const personSchema = z\n .object({\n name: personNameSchema,\n gender: z\n .enum([...GENDER_IDENTITIES] as [\n (typeof GENDER_IDENTITIES)[number],\n ...(typeof GENDER_IDENTITIES)[number][],\n ])\n .optional(),\n genderOther: z.string().optional(),\n })\n .transform((value) => normalizePerson(value));\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;;;ACAA,iBAAkB;;;ACAX,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;;;AHpDA,IAAM,mBAAmB,aAAE,OAAO;AAAA,EAChC,OAAO,aAAE,OAAO;AAAA,EAChB,QAAQ,aAAE,OAAO;AAAA,EACjB,QAAQ,aAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,QAAQ,aAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,QAAQ,aAAE,OAAO,EAAE,SAAS;AAC9B,CAAC;AAEM,IAAM,eAAe,aACzB,OAAO;AAAA,EACN,MAAM;AAAA,EACN,QAAQ,aACL,KAAK,CAAC,GAAG,iBAAiB,CAG1B,EACA,SAAS;AAAA,EACZ,aAAa,aAAE,OAAO,EAAE,SAAS;AACnC,CAAC,EACA,UAAU,CAAC,UAAU,gBAAgB,KAAK,CAAC;","names":[]}