Skip to content

Architecture

packages/
├── core/ @faraid/core — pure math engine
├── domain/ @faraid/domain — types, schemas, presets
├── extensions/ @faraid/extensions — 12 extension instances
├── api/ @faraid/api — user wrappers + BaseSolver
├── server/ @faraid/server — Hono HTTP server
├── client/ @faraid/client — generated RPC client
├── web/ @faraid/web — web application (Astro + Svelte 5)
├── design/ @faraid/design — shared CSS tokens
└── docs/ @faraid/docs — this documentation site (Astro Starlight)

Enforced by eslint-plugin-boundaries (.eslintrc.json):

core ← domain ← extensions ← api ← server
web (imports domain only)

No package may import from a package to its right. This keeps the math engine completely decoupled from HTTP, UI, and schema layers.

The math engine. Implements the 4-axiom system from findings/02:

  • Phase 0 — attribute exclusion (non-Muslim, slave, killer)
  • Phase 1 — ghost-pressure flags (hasDescendant, hasFather, etc.)
  • Phase 2 — person exclusion (α₁ intermediary + α₂ quwwa tiebreak)
  • Phase 3 — share assignment (β — farḍ and ʿaṣaba)
  • Phase 4 — normalization (γ — ʿAwl / Radd / ʿAṣaba absorption)
  • Phase 5 — taṣḥīḥ (integer conversion)

Key invariant: no strings, no HTTP, no UI. Pure TypeScript math with exact BigInt arithmetic.

The alpha2.ts helper provides the shared minQ computation used by both Phase 2 and Phase 4.

All types, Zod schemas, and madhhab presets live here. No other package defines HeirType or validates request bodies — they import from domain.

Key exports:

  • HeirTypeSchema — 27 heir types as a Zod enum
  • MadhhabConfigSchema — 9 fields including D16/D17/D18 toggles
  • SHAFII, HANAFI, HANBALI, MALIKI — preset configs
  • ScenarioFlagSchema, PHENOMENA — scenario generation types
  • All input and output Zod schemas (request validation + response docs)

@faraid/extensions — unified extension primitive

Section titled “@faraid/extensions — unified extension primitive”

All six extension families reduce to one primitive: scenarioExtension(F, S, w, A) where:

  • F — the core computeShares function
  • S — scenario set (e.g. 2ⁿ alive/dead masks for Mafqūd, 6 gender×count for Ḥaml)
  • w — weight function (uniform, estate-ratio, etc.)
  • A — aggregation rule (componentMin, componentMean, singlePick, asymmetricMinMax, weightedSum, caseSplit)

The 12 instances correspond exactly to findings/16 §4:

InstanceMethodAggregation
MunāsakhatChain of deathsweightedSum (path products)
Gharqā JumhūrIndependent estatesNo merge
Gharqā ḤanbalīChain Forest (TILD/ṬARIF)weightedSum
Mafqūd2ⁿ alive/deadcomponentMin
Ḥaml Jumhūr6 scenariosasymmetricMinMax
Ḥaml Ḥanafī2 scenariosasymmetricMinMax
Khunthā Shāfiʿī{m, f}componentMin
Khunthā Mālikī{m, f}componentMean
Khunthā Ḥanafī{m, f}singlePick(argmin)
Khunthā Ḥanbalī{m, f}caseSplit
DhA TanzīlProjection treesub-distribution products
DhA QarābaPriority cascadepriority-weighted split

The API layer translates between string HeirType values and the engine’s 5-tuple vectors.

BaseSolver class hierarchy:

abstract class BaseSolver<TInput, TOutput> {
abstract preProcess(input: TInput): PreparedInput;
abstract runCore(prep: PreparedInput): PipelineResult;
protected postProcess(result, warnings): TOutput; // = _annotatePipelineResult
solve(input: TInput): TOutput;
}
class FlatSolver extends BaseSolver<HeirInput[], SolverResult>
class ChainSolver extends BaseSolver<ChainSolverInput, ChainSolverResult>
class MafqudSolver extends BaseSolver<MafqudSolverInput, MafqudSolverResult>
class DhaSolver extends BaseSolver<DhaSolverInput, SolverResult>

The public functions (solve, solveChain, solveMafqud, solveDha) are thin wrappers: new FlatSolver(config).solve(input).

Hono server targeting Cloudflare Workers. All request validation uses Zod schemas from @faraid/domain. No schema is defined in the server itself.

  • Turborepo — task orchestration, dependency-aware builds
  • tsc -b — composite TypeScript builds with project references
  • pnpm workspaces — package management
  • Vitest — testing (809 total tests across all packages)