@aleonnet/healthcare-scheduler
v0.1.29
Published
Healthcare scheduling and medication reminder system
Maintainers
Readme
@healthcare-scheduler
Production-ready healthcare scheduling library for medication reminders, appointments, and health-related notifications with OS-level integration.
Status
- 181/181 tests passing
- 29 test suites (unit, integration, validation)
- 100% TypeScript (strict mode)
- 6 scheduling patterns validated
Features
- 📅 Flexible Scheduling: 6 schedule types (TIMES, WEEKLY, INTERVAL, CYCLIC, DAYS_INTERVAL, PARTS_OF_DAY)
- 🔔 OS Notifications: Native notification scheduling with automatic reconciliation
- 💾 Offline-First: SQLite local storage
- 📊 Adherence Tracking: Materialized daily adherence metrics
- 🎮 Gamification: Streak calculation and level progression
- 🔌 Extensible: Event-driven plugin system
- 🧪 Test Coverage: Comprehensive validation suite
Installation
npm install @healthcare-scheduler
# Peer dependency (if using React Native)
npm install expo-sqliteQuick Start
import { HealthcareScheduler, MockAdapter, MockNotificationAdapter } from '@healthcare-scheduler';
const scheduler = new HealthcareScheduler({
storage: new MockAdapter(), // or SQLiteAdapter for production
notificationDriver: new MockNotificationAdapter(), // or NotifeeAdapter (see below)
windowDays: 14,
maxPendingNotifications: 45,
enableGrouping: false
});
await scheduler.bootstrap();
// Create medication plan
const planId = await scheduler.createPlan({
item: {
type: 'MED',
name: 'Losartana 50mg',
meta: { concentration: '50mg' }
},
schedule: {
kind: 'TIMES',
times: ['08:00', '20:00']
},
startsAt: new Date().toISOString()
});
// Get today's medication schedule
const today = await scheduler.getTodayOccurrences();
// Record medication taken
await scheduler.recordEvent(occurrenceId, 'TAKEN', { source: 'user' });Schedule Types
1. TIMES (Fixed times per day)
{ kind: 'TIMES', times: ['08:00', '14:00', '20:00'] }2. WEEKLY (Weekday-specific schedule)
{
kind: 'WEEKLY',
lines: [
{ id: '1', daysOfWeek: [1,2,3,4,5], time: '08:00', qty: 1 }
]
}3. INTERVAL (Every N hours)
{
kind: 'INTERVAL',
intervalH: 8,
firstDoseAt: '2025-01-01T08:00:00.000Z'
}4. CYCLIC (Take/pause cycles, e.g. contraceptives)
{
kind: 'CYCLIC',
takeDays: 21,
pauseDays: 7,
times: ['21:00'],
cycleAnchorISO: '2025-01-01T00:00:00.000Z'
}5. DAYS_INTERVAL (Every N days)
{
kind: 'DAYS_INTERVAL',
daysInterval: 7,
times: ['09:00'],
anchorDateISO: '2025-01-01T00:00:00.000Z'
}6. PARTS_OF_DAY (Morning/Afternoon/Evening)
{
kind: 'PARTS_OF_DAY',
parts: [
{ code: 'morning', label: 'Manhã', time: '08:00', qty: 1 },
{ code: 'night', label: 'Noite', time: '21:00', qty: 1 }
]
}Gamification Plugin
import { GamificationPlugin } from '@healthcare-scheduler';
const gamification = new GamificationPlugin();
scheduler.registerPlugin(gamification);
await scheduler.bootstrap();
// Get current level and streak
const plugin = scheduler.getPlugin<GamificationPlugin>('gamification');
const levels = await plugin.getCurrentLevelsState();
// => { streak: 5, levelId: 'bronze', levelName: 'Bronze', progress: 0.4, ... }
// Get daily adherence
const adherence = await plugin.getDayAdherence(new Date());
// => { date: '2025-10-11', total: 4, done: 3, pending: 1, percent: 75 }Production Usage with NotifeeAdapter
For React Native production apps, use NotifeeAdapter with Platform information:
import { Platform } from 'react-native';
import {
HealthcareScheduler,
SQLiteAdapter,
NotifeeAdapter,
GamificationPlugin
} from '@healthcare-scheduler';
// Initialize storage
const storage = await SQLiteAdapter.connect({
dbName: 'healthcare.db',
autoApplySchema: true
});
// Initialize notifications with Platform info
const notificationDriver = new NotifeeAdapter({
platform: Platform // Required for Android/iOS specific behavior
});
// Create scheduler
const scheduler = new HealthcareScheduler({
storage,
notificationDriver,
windowDays: 14,
maxPendingNotifications: 45
});
// Register plugins
scheduler.registerPlugin(new GamificationPlugin());
// Bootstrap
await scheduler.bootstrap();Note: NotifeeAdapter requires platform to be passed to avoid dynamic imports of react-native, ensuring compatibility with React Native's New Architecture and preventing deprecated API warnings.
Handling Significant Time/Timezone Changes (DST, travel)
When the device timezone/offset changes, OS timestamp-based triggers fire at the same absolute epoch, which may shift local times. To guarantee exact local times, re-build the window and re-schedule notifications:
// iOS: observe UIApplicationSignificantTimeChange
// Android: observe ACTION_TIMEZONE_CHANGED / ACTION_TIME_CHANGED
await scheduler.handleSignificantTimeChange({ horizonDays: 2 });What it does:
- Rebuilds the local window using current timezone.
- Cancels all scheduled notifications.
- Reconciles with the OS scheduling upcoming items again, including a 1-day past check to clean border cases.
References:
- Apple UIApplication.significantTimeChangeNotification — https://developer.apple.com/documentation/uikit/uiapplication/1622943-significanttimechangenotification
- Android ACTION_TIMEZONE_CHANGED / ACTION_TIME_CHANGED — https://developer.android.com/reference/android/content/Intent#ACTION_TIMEZONE_CHANGED
- Notifee timestamp triggers — https://notifee.app/react-native/docs/trigger-notifications#timestamp-trigger
Architecture
src/
├── core/ # Main API, EventBus, types
├── database/ # Storage abstraction, repositories
│ ├── repositories/ # ItemsRepo, PlansRepo, OccurrencesRepo, EventsRepo
│ └── adapters/ # MockAdapter, SQLiteAdapter (peer dep)
├── planning/ # Schedule expansion engine
├── reconciler/ # DB ↔ OS notification sync
├── notifications/ # Notification driver abstraction
├── plugins/ # Extensible plugin system
│ └── gamification/ # Adherence tracking & levels
└── utils/ # Queue, timezone helpersEvents
scheduler.on('plans:changed', ({ planIds }) => {
console.log('Plans updated:', planIds);
});
scheduler.on('occurrences:changed', ({ occurrenceIds }) => {
console.log('Occurrences changed:', occurrenceIds);
});
scheduler.on('window:filled', ({ count }) => {
console.log('Notifications scheduled:', count);
});Note:
- Window fill and reconciliation are asynchronous and triggered by events (
plans:changed,occurrences:changed). Wait forwindow:filledin tests or after batch operations; there is no publicupdateWindowmethod.
Development
# Install
npm install
# Run tests
npm test
# Run specific test suite
npm test -- tests/validation/scenarios/scenario1_weekly.test.ts
# Build
npm run build
# Lint
npm run lintTesting
The library includes comprehensive validation:
- Unit tests: Planning engine, repositories, window scheduler
- Integration tests: Full CRUD cycles
- Validation scenarios: 6 real-world medication schedules
- Plugin tests: Gamification, streak calculation
All tests run in-memory using MockAdapter (no database required).
Documentation
License
MIT
Credits
Alessandro Barbosa.
Built with TypeScript, tested with Jest, validated against real-world use cases.
