is-mahram
Version:
A TypeScript library to determine if a person is a mahram based on Islamic jurisprudence.
267 lines (260 loc) • 6.8 kB
JavaScript
// src/is-mahram.ts
function isMahram(personA, personB) {
if (personA.getId() === personB.getId()) {
return true;
}
if (personA.getSpouses().has(personB)) {
return true;
}
if (isBloodMahram(personA, personB)) {
return true;
}
if (isMarriageMahram(personA, personB)) {
return true;
}
if (isSucklingMahram(personA, personB)) {
return true;
}
return false;
}
function isBloodMahram(personA, personB) {
if (isAncestor(personA, personB) || isAncestor(personB, personA)) {
return true;
}
if (isSibling(personA, personB)) {
return true;
}
if (isAuntOrUncle(personA, personB) || isAuntOrUncle(personB, personA)) {
return true;
}
return false;
}
function isMarriageMahram(personA, personB) {
for (const spouse of personA.getSpouses()) {
if (isAncestor(personB, spouse)) {
return true;
}
}
for (const spouse of personB.getSpouses()) {
if (isAncestor(personA, spouse)) {
return true;
}
}
for (const spouse of personA.getSpouses()) {
if (isDescendant(personB, spouse)) {
return true;
}
}
for (const spouse of personB.getSpouses()) {
if (isDescendant(personA, spouse)) {
return true;
}
}
const { father: fatherA, mother: motherA } = personA.getParents();
const { father: fatherB, mother: motherB } = personB.getParents();
if (fatherA?.getSpouses().has(personB)) {
return true;
}
if (motherA?.getSpouses().has(personB)) {
return true;
}
if (fatherB?.getSpouses().has(personA)) {
return true;
}
if (motherB?.getSpouses().has(personA)) {
return true;
}
const childrenOfA = getChildren(personA);
for (const child of childrenOfA) {
if (child?.getSpouses().has(personB)) {
return true;
}
}
const childrenOfB = getChildren(personB);
for (const child of childrenOfB) {
if (child?.getSpouses().has(personA)) {
return true;
}
}
return false;
}
function isSucklingMahram(personA, personB) {
const sucklingParentsA = getSucklingParents(personA);
const sucklingParentsB = getSucklingParents(personB);
if (!sucklingParentsA && !sucklingParentsB)
return false;
if (sucklingParentsB && sucklingParentsB.mother === personA || sucklingParentsB && sucklingParentsB.father === personA) {
return true;
}
if (sucklingParentsA && sucklingParentsA.mother === personB || sucklingParentsA && sucklingParentsA.father === personB) {
return true;
}
if (sucklingParentsA && sucklingParentsB && sucklingParentsA.mother === sucklingParentsB.mother) {
return true;
}
const sucklingMotherA = personA.getSucklingMother();
const sucklingMotherB = personB.getSucklingMother();
if (sucklingMotherA && isBloodMahram(sucklingMotherA, personB)) {
return true;
}
if (sucklingMotherB && isBloodMahram(sucklingMotherB, personA)) {
return true;
}
return false;
}
function isAncestor(potentialAncestor, person) {
const { father, mother } = person.getParents();
if (!father && !mother) {
return false;
}
if (father === potentialAncestor || mother === potentialAncestor) {
return true;
}
return !!(father && isAncestor(potentialAncestor, father) || mother && isAncestor(potentialAncestor, mother));
}
function isDescendant(potentialDescendant, person) {
return isAncestor(person, potentialDescendant);
}
function isSibling(personA, personB) {
const { father: fatherA, mother: motherA } = personA.getParents();
const { father: fatherB, mother: motherB } = personB.getParents();
if (fatherA && motherA && fatherB && motherB) {
return fatherA === fatherB || motherA === motherB;
}
return false;
}
function isAuntOrUncle(potentialAuntOrUncle, person) {
const { father, mother } = person.getParents();
if (father && isSibling(potentialAuntOrUncle, father)) {
return true;
}
if (mother && isSibling(potentialAuntOrUncle, mother)) {
return true;
}
return false;
}
function getChildren(person) {
return [];
}
function getSucklingParents(person) {
const sucklingMother = person.getSucklingMother();
if (!sucklingMother) {
return null;
}
return {
mother: sucklingMother,
father: Array.from(sucklingMother.getSpouses())[0] || null
};
}
// src/errors.ts
class FatherMustBeMaleError extends Error {
constructor() {
super("Father must be male");
this.name = "FatherMustBeMaleError";
}
}
class MotherMustBeFemaleError extends Error {
constructor() {
super("Mother must be female");
this.name = "MotherMustBeFemaleError";
}
}
class SucklingMotherMustBeFemaleError extends Error {
constructor() {
super("Suckling mother must be femalre");
this.name = "SucklingMotherMustBeFemaleError";
}
}
class SpouseMustBeOppositeGenderError extends Error {
constructor() {
super("Spouse must be of opposite gender");
this.name = "SpouseMustBeOppositeGenderError";
}
}
// src/util.ts
var generateId = () => {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
};
// src/person.ts
class Person {
name;
gender;
id;
parents = {
father: null,
mother: null
};
spouses = new Set;
sucklingMother = null;
constructor(name, gender) {
this.name = name;
this.gender = gender;
this.id = generateId();
}
setName(name) {
this.name = name;
}
setFather(father) {
if (father.gender !== "Male")
throw new FatherMustBeMaleError;
this.parents.father = father;
if (this.parents.mother && !this.parents.mother.getSpouses().has(father)) {
this.parents.mother.addSpouse(father);
}
}
setMother(mother) {
if (mother.gender !== "Female")
throw new MotherMustBeFemaleError;
this.parents.mother = mother;
if (this.parents.father && !this.parents.father.getSpouses().has(mother)) {
this.parents.father.addSpouse(mother);
}
}
addSpouse(spouse) {
if (this.gender === spouse.gender) {
throw new SpouseMustBeOppositeGenderError;
}
if (!this.spouses.has(spouse)) {
this.spouses.add(spouse);
spouse.spouses.add(this);
}
}
setSucklingMother(wetNurse) {
if (wetNurse.gender !== "Female") {
throw new SucklingMotherMustBeFemaleError;
}
this.sucklingMother = wetNurse;
}
getId() {
return this.id;
}
getName() {
return this.name;
}
getGender() {
return this.gender;
}
getParents() {
return this.parents;
}
getFather() {
return this.parents.father;
}
getMother() {
return this.parents.mother;
}
getSpouses() {
return this.spouses;
}
getSucklingMother() {
return this.sucklingMother;
}
}
export {
isMahram,
SucklingMotherMustBeFemaleError,
SpouseMustBeOppositeGenderError,
Person,
MotherMustBeFemaleError,
FatherMustBeMaleError
};