- ✅ Daily tracking — Record counts per coop and breed with strict date validation
- ✅ Smart averages — Calculate weekly, monthly, or custom rolling averages
- ✅ Drop detection — Automatically flag when production falls below expected thresholds
- ✅ Seasonal insights — Analyze patterns across months to optimize flock management
- ✅ Zero dependencies — Pure TypeScript with strict types, runs on Bun or Node.js 20+
npm install @adametherzlab/egg-tracker
# or
bun add @adametherzlab/egg-tracker// REMOVED external import: import { createTracker } from "@adametherzlab/egg-tracker";
const tracker = createTracker();
// Record today's haul
tracker.addEntry({
date: "2024-01-15",
coopId: "main-coop",
breed: "Rhode Island Red",
count: 5
});
// Get weekly average
const weekly = tracker.calculateAverage({
coopId: "main-coop",
days: 7
});
console.log(`Average: ${weekly.count} eggs/day`);function createTracker(): TrackerReturns: Tracker — A new tracker instance with empty state.
Example:
const tracker = createTracker();addEntry(entry: EggEntry): voidParameters:
entry— Object containingdate(ISO string),coopId(string),breed(string),count(number)
Example:
tracker.addEntry({
date: "2024-03-15",
coopId: "north-coop",
breed: "Leghorn",
count: 4
});removeEntry(date: string, coopId: CoopId, breed: BreedId): booleanReturns: boolean — True if entry was found and removed.
Example:
const removed = tracker.removeEntry("2024-03-15", "north-coop", "Leghorn");getEntries(options?: QueryOptions): EggEntry[]Parameters:
options.coopId— Filter by specific coopoptions.breed— Filter by breedoptions.startDate— Start of date range (ISO)options.endDate— End of date range (ISO)
Example:
const marchEntries = tracker.getEntries({
startDate: "2024-03-01",
endDate: "2024-03-31",
coopId: "main-coop"
});getCoops(): CoopId[]Example:
const coops: string[] = tracker.getCoops(); // ["main-coop", "north-coop"]Returns all breed IDs, optionally filtered by coop.
getBreeds(coopId?: CoopId): BreedId[]Example:
const allBreeds = tracker.getBreeds();
const coopBreeds = tracker.getBreeds("main-coop");Wipes all data. Use with caution.
clearAll(): voidcalculateAverage(options: {
coopId?: CoopId;
breed?: BreedId;
days: number;
aggregation?: AggregationLevel;
}): AverageResultReturns: AverageResult with count (number) and period (days).
Example:
const monthly = tracker.calculateAverage({
coopId: "main-coop",
days: 30,
aggregation: "daily"
});Identifies significant production decreases.
detectProductionDrops(options: DropDetectionOptions): ProductionDrop[]Parameters:
options.threshold— Percentage drop to flag (0-100)options.windowDays— Comparison window sizeoptions.minBaseline— Minimum eggs to consider (prevents false positives on empty coops)
Returns: Array of ProductionDrop objects with date, severity, and expected vs actual counts.
Example:
const drops = tracker.detectProductionDrops({
threshold: 30,
windowDays: 7,
minBaseline: 3
});analyzeSeasonalTrends(): SeasonalTrend[]Returns: Array of trends showing average production per month with variance data.
Example:
const trends = tracker.analyzeSeasonalTrends();
// trends[0] = { month: 1, averageCount: 4.2, variance: 0.8 }interface EggEntry {
readonly date: string; // ISO 8601 date (YYYY-MM-DD)
readonly coopId: CoopId; // Coop identifier
readonly breed: BreedId; // Breed name
readonly count: number; // Eggs laid (0 or positive)
}interface TrackerConfig {
readonly dataDir?: string; // Defaults to ~/.egg-tracker
readonly maxEntries?: number;
}interface DropDetectionOptions {
readonly threshold: number; // Percentage (0-100)
readonly windowDays: number; // Rolling window for baseline
readonly minBaseline?: number; // Minimum eggs to trigger
}interface DateRange {
readonly startDate: string;
readonly endDate: string;
}interface CoopConfig {
readonly id: CoopId;
readonly capacity?: number;
readonly establishedDate?: string;
}interface ProductionDrop {
readonly date: string;
readonly coopId: CoopId;
readonly breed: BreedId;
readonly expectedCount: number;
readonly actualCount: number;
readonly severity: number; // Percentage drop
}interface SeasonalTrend {
readonly month: number; // 1-12
readonly averageCount: number;
readonly sampleSize: number;
readonly variance: number;
}interface AverageResult {
readonly count: number;
readonly period: number; // Days in calculation
}interface QueryOptions {
readonly coopId?: CoopId;
readonly breed?: BreedId;
readonly startDate?: string;
readonly endDate?: string;
}interface BatchResult {
readonly added: number;
readonly errors: Array<{ entry: EggEntry; reason: string }>;
}type CoopId = string;
type BreedId = string;
type AggregationLevel = "daily" | "weekly" | "monthly";Running a multi-coop operation with automated health monitoring:
// REMOVED external import: import { createTracker, type EggEntry, type ProductionDrop } from "@adametherzlab/egg-tracker";
const tracker = createTracker();
// Batch import historical data
const historical: EggEntry[] = [
{ date: "2024-01-01", coopId: "coop-a", breed: "Orpington", count: 3 },
{ date: "2024-01-01", coopId: "coop-b", breed: "Sussex", count: 5 },
// ... more entries
];
historical.forEach(entry => tracker.addEntry(entry));
// Detect health issues
const drops: ProductionDrop[] = tracker.detectProductionDrops({
threshold: 40, // Flag 40% or greater drops
windowDays: 14, // Compare against 2-week baseline
minBaseline: 2 // Ignore if normally laying less than 2
});
if (drops.length > 0) {
console.warn("Potential health issues detected:", drops);
}
// Plan for winter
const trends = tracker.analyzeSeasonalTrends();
const winterAvg = trends.find(t => t.month === 12)?.averageCount;
console.log(`December average: ${winterAvg} eggs/day`);See CONTRIBUTING.md
MIT (c) AdametherzLab