Blueprint

System Data Flow

Entry Point
Process Node
Data Store
Output
Productionv1.0.0

License Hub

Developer

Makes regulatory complexity navigable: users self-resolve eligibility in seconds.

Logic Breakdown

Normalizes rules into a per-state matrix and filters by credential, reciprocity, and residency. Content model designed for safe updates; changes propagate without redeploying UI logic.

Architecture Decisions

  • 01Makes regulatory complexity navigable: users self-resolve eligibility in seconds.
  • 02Normalizes rules into a per-state matrix and filters by credential, reciprocity, and residency.
  • 03Content model designed for safe updates; changes propagate without redeploying UI logic.

Code Snippet

typescript
license-hub.ts
/** License Hub — 50-State Practitioner Filter Engine */
export type StateRule = {
  abbr: string;
  state: string;
  requiresResidency: boolean;
  compactGroup: "UNIFORM" | "WESTERN" | null;
  requiredCredentials: string[];
  ceHoursRequired: number;
  renewalYears: number;
  jurisprudenceExam: boolean;
};

export type LicenseQuery = {
  homeState: string;
  credentials: string[];
  completedCeHours: number;
  jurisprudenceWilling: boolean;
  targetStates?: string[];
};

export type LicenseResult = {
  abbr: string;
  eligible: boolean;
  pathway: "direct" | "compact-reciprocity" | "exam-required" | "ineligible";
  ceGap: number;
  notes: string[];
};

const UNIFORM_COMPACT = new Set([
  "IA","KS","MN","MO","NE","ND","OK","SD","UT","WI",
]);
const WESTERN_COMPACT = new Set([
  "AZ","CO","ID","MT","NV","OR","WA","WY",
]);

function getCompact(abbr: string) {
  if (UNIFORM_COMPACT.has(abbr)) return "UNIFORM";
  if (WESTERN_COMPACT.has(abbr)) return "WESTERN";
  return null;
}

export function evaluateLicensure(
  rules: StateRule[],
  q: LicenseQuery,
): LicenseResult[] {
  const creds = new Set(q.credentials.map((c) => c.toLowerCase()));
  const scope = q.targetStates?.length
    ? rules.filter((r) => q.targetStates!.includes(r.abbr))
    : rules;
  const homeCompact = getCompact(q.homeState);

  return scope.map((r): LicenseResult => {
    const notes: string[] = [];
    const hasAllCreds = r.requiredCredentials.every((c) =>
      creds.has(c.toLowerCase()),
    );
    const ceGap = Math.max(0, r.ceHoursRequired - q.completedCeHours);

    if (!hasAllCreds)
      return { abbr: r.abbr, eligible: false, pathway: "ineligible",
               ceGap, notes: [`Missing: ${r.requiredCredentials.join(", ")}`] };

    if (r.jurisprudenceExam && !q.jurisprudenceWilling)
      return { abbr: r.abbr, eligible: false, pathway: "exam-required",
               ceGap, notes: ["Jurisprudence exam required"] };

    if (r.jurisprudenceExam) notes.push("Jurisprudence exam required");
    if (ceGap > 0) notes.push(`${ceGap} CE hours required`);

    const targetCompact = getCompact(r.abbr);
    if (homeCompact && homeCompact === targetCompact)
      return { abbr: r.abbr, eligible: true,
               pathway: "compact-reciprocity", ceGap, notes };

    if (r.requiresResidency && r.abbr !== q.homeState)
      return { abbr: r.abbr, eligible: false, pathway: "ineligible",
               ceGap, notes: ["In-state residency required"] };

    return { abbr: r.abbr, eligible: true, pathway: "direct", ceGap, notes };
  });
}

Key Dependencies

TypeScriptCore stack component
SearchCore stack component
UX StateCore stack component

Technical Spec

Pillar
Developer
States
50
Mode
Filter

Tags

TypeScriptSearchUX State

Live Sandbox

Interactive runtime environment — License Hub v1.0.0

Production
license-hub-sandbox

$ npm run sandbox

> Initialising License Hub v1.0.0

> Status: Production

// Live iframe mounted once sandboxUrl is configured.