matchframe
v0.0.1
Published
A general-purpose business decision library for solving allocation, assignment, ranking, and scheduling problems under complex business constraints.
Readme
matchframe
A small, deterministic allocation engine for assigning entities to resources under hard constraints and configurable scoring rules.
Install
npm install matchframe
# or
yarn add matchframeCore concepts
- Entity (
E) — the thing being allocated (a job, student, pallet, ...). - Resource (
R) — the thing receiving the allocation (a technician, major, warehouse zone, ...). - Problem — a collection of entities, wrapped resources, optional global constraints, and optional global scoring rules.
- Solver — a function that turns a
Probleminto aResult.
The default solver is a deterministic greedy allocator:
- Sort entities by global priority (higher score first).
- For each entity, find the best feasible resource using combined global + per-resource scores.
- Enforce hard constraints and capacity.
- Record every assignment, rejection, and final resource usage.
Quick example
import { createResource, solve, scoring } from "matchframe";
type Job = { id: string; priority: number; zone: string };
type Tech = { id: string; zone: string; maxJobs: number };
const technicians: Resource<Job, Tech>[] = [
createResource(
{ id: "T1", zone: "north", maxJobs: 2 },
{
capacity: 2,
constraints: [
createResource.compatible({
predicate: (job, tech) => job.zone === tech.zone,
reason: "zone mismatch",
}),
],
scoring: [
scoring.preference({
score: (job, tech) => (job.zone === tech.zone ? 10 : 0),
}),
],
}
),
];
const result = solve({
entities: [
{ id: "J1", priority: 1, zone: "north" },
{ id: "J2", priority: 2, zone: "north" },
],
resources: technicians,
scoring: [
scoring.preference({
name: "priority",
score: (job) => job.priority,
}),
],
});
console.log(result.summary);
// { totalEntities: 2, assigned: 2, unassigned: 0, resourcesUsed: 1 }API
createResource<E, R>(raw, options)
Wrap a raw resource with capacity, constraints, and scoring rules.
createResource(raw, {
capacity: 5,
constraints: [...],
scoring: [...],
});Built-in constraints
Attach to a resource or pass globally in Problem.constraints.
| Helper | Purpose |
| --- | --- |
| createResource.hard({ predicate, reason }) | Generic boolean constraint. |
| createResource.capacity({ limit }) | Enforce a fixed capacity limit. |
| createResource.quota({ groupBy, maxPct, minPct }) | Enforce percentage caps/floors for groups. |
| createResource.exclusive({ category, allowedCategories }) | Only allow listed categories. |
| createResource.compatible({ predicate, reason }) | Generic compatibility predicate. |
| createResource.dependency({ key, dependsOn, sameResource? }) | Require another entity to be assigned first. |
| createResource.mutualExclusion({ conflicts, scope? }) | Block conflicting entities from sharing a resource (or globally). |
| createResource.timeWindow({ entityWindow, resourceWindows, allowOverlap? }) | Enforce schedule fit and optional no-overlap policy. |
| createResource.crossResourceQuota({ groupBy, maxAllowed }) | Enforce group limits across all resources. |
Notes:
createResource.capacity(...)also accepts{ predicate, reason? }for custom context-aware capacity rules.- Backward-compatible signatures are still supported (
hard(predicate, options)andcapacity(number | predicate)).
Built-in scoring rules
| Helper | Purpose |
| --- | --- |
| scoring.preference({ score, direction }) | User-defined numeric preference. |
| scoring.loadBalance({ weight }) | Prefer resources with lower utilization. |
| scoring.orderedFallback({ order }) | Prefer resources in a fixed fallback order. |
| scoring.tieBreak({ by, direction }) | Deterministic tie-break key when other scores are equal. |
| scoring.waitlist({ score, direction }) | Rank unassigned entities into a deterministic waitlist. |
solve(problem)
Run the configured solver (default: greedySolver). Returns a Result with:
assignments— every entity/resource pair.assignments[*].reasons— why the selected resource won and what alternatives failed.unassigned— entities that could not be placed, with reasons.waitlist— ranked view of unassigned entities (with score and reasons).resourceUsage— used/capacity/remaining per resource.decisions— full decision log (assignments and rejections).summary— totals.
Scoring direction
"desc"(default) — higher score is better."asc"— lower score is better.
A rule may return number or number[]. Arrays are compared lexicographically, which is useful for multi-criteria sorting such as [total, english, math].
Notes
- Global priority rules are used only for entity ordering. They receive
undefinedforresourceandcontext, so they must not access those fields. - Per-resource constraints and scoring rules are evaluated for each candidate resource during allocation.
- Capacity is tracked per resource and enforced by the built-in
capacityconstraint or custom constraints.
