A zone-aware seed starting calendar for gardeners. Input your last frost date, and get precise indoor start, transplant, and direct-sow dates for over 60 vegetables. Plan your entire season with confidence.
✅ Zone-Aware Calculations – Schedules adapt to your specific last and first frost dates. ✅ 60+ Vegetables – Comprehensive database covering common and heirloom varieties. ✅ Succession Planting – Automatically generates schedules for multiple sowings. ✅ TypeScript First – Full type safety, strict mode, and zero external dependencies. ✅ Multiple Outputs – Get raw data for your app or formatted exports for printing.
# Using npm
npm install @adametherzlab/seed-planner
# Using Bun
bun add @adametherzlab/seed-planner// REMOVED external import: import { getSchedule, findPlantById } from '@adametherzlab/seed-planner';
// Find a plant and get its schedule
const tomato = findPlantById('tomato-cherry');
if (tomato) {
const schedule = getSchedule(tomato, '2025-05-15');
console.log(`Start tomatoes indoors: ${schedule.startIndoors}`);
// Output: "Start tomatoes indoors: 2025-03-27"
}| Function | Description | Parameters | Returns |
|---|---|---|---|
findPlantById(id) |
Find a plant by its unique ID. | id: PlantId |
Plant | undefined |
findPlantsByName(name) |
Search plants by common name (case-insensitive). | name: string |
readonly Plant[] |
getAllPlants() |
Get the complete database of 60+ plants. | none | readonly Plant[] |
filterPlants(filter) |
Filter plants by frost tolerance, sowing method, etc. | filter: PlantFilter |
readonly Plant[] |
getSchedule(plant, lastFrostDate, firstFrostDate?) |
Calculate base planting dates for a single plant. | plant: Plant, lastFrostDate: string, firstFrostDate?: string |
PlantingSchedule |
getSuccessionSchedule(plant, lastFrostDate, firstFrostDate?) |
Generate multiple sowing dates for succession planting. | plant: Plant, lastFrostDate: string, firstFrostDate?: string |
SuccessionSchedule |
getPlantSchedule(plant, config) |
Get a complete schedule (with optional succession). | plant: Plant, config: PlannerConfig |
PlantScheduleResult |
getAllSchedules(plants, config) |
Generate schedules for multiple plants at once. | plants: readonly Plant[], config: PlannerConfig |
PlantScheduleResult[] |
createPlannerResult(plants, config) |
Create a structured result object with all schedules. | plants: readonly Plant[], config: PlannerConfig |
PlannerResult |
validateConfig(config) |
Validate planner configuration for correctness. | config: PlannerConfig |
ValidationError[] |
validateDateString(date) |
Validate that a string is a proper ISO date. | date: string |
boolean |
exportSchedules(result, options?) |
Export schedules to CSV or JSON format. | result: PlannerResult, options?: ExportOptions |
string |
calculateSeasonBoundaries(lastFrostDate, firstFrostDate?) |
Calculate the start and end of the growing season. | lastFrostDate: string, firstFrostDate?: string |
SeasonBoundaries |
Here’s a real-world example planning an entire garden bed with succession planting.
import {
createPlannerResult,
findPlantsByName,
filterPlants,
FrostTolerance,
exportSchedules
} from '@adametherzlab/seed-planner';
// 1. Select plants for a "salad bed"
const lettuce = findPlantsByName('lettuce');
const radishes = findPlantsByName('radish');
const carrots = findPlantsByName('carrot');
const selectedPlants = [...lettuce, ...radishes, ...carrots];
// 2. Configure for zone 5b with a last frost of May 15th
const config = {
lastFrostDate: '2025-05-15',
firstFrostDate: '2025-10-10', // Optional, improves fall scheduling
includeSuccession: true,
maxSuccessionPlantings: 3
};
// 3. Generate all schedules
const gardenPlan = createPlannerResult(selectedPlants, config);
// 4. Export to CSV for printing
const csv = exportSchedules(gardenPlan, { format: 'csv' });
console.log(csv);
// 5. Or, just inspect the dates for lettuce
const firstLettuceSchedule = gardenPlan.schedules[0];
console.log('First lettuce sowing:', firstLettuceSchedule.schedule.startIndoors);
if (firstLettuceSchedule.succession) {
console.log('Additional sowings:', firstLettuceSchedule.succession.sowings.map(s => s.startDate));
}Nightshades
- Tomato (Cherry, Beefsteak, Heirloom)
- Pepper (Bell, Hot)
- Eggplant
- Tomatillo
Brassicas
- Broccoli
- Cauliflower
- Cabbage (Green, Red, Savoy)
- Kale
- Brussels Sprouts
- Kohlrabi
Root Vegetables
- Carrot
- Radish
- Beet
- Turnip
- Parsnip
- Rutabaga
Legumes
- Pea (Shelling, Snap, Snow)
- Bean (Bush, Pole)
- Fava Bean
Cucurbits
- Cucumber
- Squash (Summer, Winter)
- Zucchini
- Pumpkin
- Melon (Cantaloupe, Watermelon)
Alliums
- Onion
- Leek
- Garlic
- Shallot
Leafy Greens
- Lettuce (Head, Leaf, Romaine)
- Spinach
- Swiss Chard
- Arugula
- Bok Choy
- Mustard Greens
Herbs
- Basil
- Cilantro
- Dill
- Parsley
Other
- Corn
- Celery
- Asparagus
- Rhubarb
- Okra
- Artichoke
- Last Frost Date: The average date of the last spring frost in your area. This is the primary input for all spring planting calculations.
- First Frost Date: The average date of the first fall frost. Providing this enables accurate fall planting and succession schedules.
- Zone IDs: Use USDA Plant Hardiness Zone format (e.g.,
"5b"). While the library uses branded types for safety, any string can be passed for labeling. - Date Format: All dates are ISO 8601 strings (
YYYY-MM-DD). UsevalidateDateString()to check inputs.
// REMOVED external import: import type { Plant, PlantingSchedule, PlannerConfig } from '@adametherzlab/seed-planner';
// Use branded types for extra safety
const plantId: PlantId = 'tomato-cherry' as PlantId;
// All functions have explicit return types
function planMyGarden(config: PlannerConfig): PlannerResult {
// ...
}See CONTRIBUTING.md for development setup, coding standards, and pull request guidelines.
MIT © AdametherzLab