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
typescriptlicense-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
TypeScript—Core stack component
Search—Core stack component
UX State—Core stack component
Technical Spec
- Pillar
- Developer
- States
- 50
- Mode
- Filter
Tags
TypeScriptSearchUX State
Live Sandbox
Interactive runtime environment — License Hub v1.0.0
license-hub-sandbox
$ npm run sandbox
> Initialising License Hub v1.0.0…
> Status: Production
// Live iframe mounted once sandboxUrl is configured.