Domain · Specifications

Meditation Libraries — Technical Specifications

The table below captures the key metadata for the meditation domain as a whole.

14sections29 minread

On this page

Comprehensive technical reference for the meditation engine libraries (libs/meditation/*). Every type, enum, union value, constant, hook factory, storage backend, and configuration default below is taken directly from the package source.


This document is the authoritative technical reference for the eight meditation engine libraries. It is written for engineers who need to know exact type shapes, enum values, default configuration numbers, and primary API signatures — the detail level required to implement a feature, write a test, or debug a type error. For a feature-oriented overview of what each library can do, see features.md. For the architectural rationale and design patterns shared across packages, see architecture.md.

Each section covers one package in the order they are typically layered: core types first, then the primary engine libraries (player, timer, breathing, session, progress, offline), then the analytics package. Cross-cutting patterns (event system, platform abstraction, storage backends, React hooks, branded types) are collected in §11. Configuration examples are in §12, build and test commands in §13, and acceptance criteria in §14.


1. Domain Overview#

The table below captures the key metadata for the meditation domain as a whole. All eight packages share the scope:meditation Nx project tag, which means pnpm nx run-many --target=test --projects=tag:scope:meditation runs all meditation tests in one command.

Property Value
Scope identifier meditation — every package is tagged scope:meditation
Package count 8
Application count 0 (engine layer; no app, API, database, or UI)
Language TypeScript (ESM)
Build executor @nx/js:tsc for 7 packages; @nx/esbuild:esbuild for meditation-session
Project tags All 8 packages: ["scope:meditation", "layer:core", "type:lib"]
Runtime dep eventemitter3 (player, timer, breathing, session, progress, offline)
Peer dep react >=18.0.0 optional (player, timer, breathing, session, offline); progress declares typescript catalog:

meditation-core and meditation-analytics have no eventemitter3 dependency and no React peer dependency — they are pure type/utility and analytics packages.


2. Package Inventory#

The table below maps each package to its filesystem path, Nx build executor, and the subpath exports it exposes for consumers. Fine-grained subpath exports (e.g., @oshun/meditation-player/hooks) allow consumers to import only what they need without pulling in unrelated code.

Package Path Build executor Subpath exports
@oshun/meditation-core libs/meditation/core @nx/js:tsc ., ./types, ./duration, ./dates, ./format, ./validation
@oshun/meditation-player libs/meditation/player @nx/js:tsc ., ./player, ./queue, ./mixer, ./playback-rate, ./hooks
@oshun/meditation-timer libs/meditation/timer @nx/js:tsc ., ./timer, ./presets, ./bells, ./ambient, ./hooks
@oshun/meditation-breathing libs/meditation/breathing @nx/js:tsc ., ./exercise, ./patterns, ./builder, ./guidance, ./visualization, ./haptics, ./hooks, ./history
@oshun/meditation-session libs/meditation/session @nx/esbuild:esbuild ., ./types, ./manager, ./persistence, ./analytics, ./scheduling, ./hooks
@oshun/meditation-progress libs/meditation/progress @nx/js:tsc ., ./tracker, ./streaks, ./statistics, ./achievements, ./milestones, ./export, ./hooks, ./sync
@oshun/meditation-offline libs/meditation/offline @nx/js:tsc ., ./types, ./manager, ./queue, ./storage, ./versioning, ./suggestions, ./hooks
@oshun/meditation-analytics libs/meditation/analytics @nx/js:tsc .

meditation-session builds with @nx/esbuild:esbuild configured for ["esm"] format with declaration: true. The meditation-player and meditation-timer packages additionally declare a publishConfig block that remaps main, types, and exports to ./dist/*.js paths for a published build.


3. @oshun/meditation-core — Shared Primitives#

meditation-core is the shared type and utility layer. It has no runtime dependencies and no eventemitter3 usage. All other packages depend on it conceptually (not via package imports) — they reference its branded types and utility functions at compile time.

3.1 Branded Types#

core/src/types.ts defines a generic brand helper and the shared identifier/value types. Branded types prevent accidental misuse — a function that accepts SessionId will reject a plain string at compile time even though both are strings at runtime.

typescript
type Brand<TValue, TBrand extends string> = TValue & {
  readonly __brand: TBrand;
};

The following branded types are defined:

Brand Base Meaning
SessionId string Meditation session identifier
TrackId string Audio track identifier
ContentId string Content item identifier
TeacherId string Teacher/narrator identifier
CourseId string Course identifier
DurationSeconds number Duration in seconds
DurationMilliseconds number Duration in milliseconds
Timestamp number Unix timestamp (ms)
Percentage number Value 0–100
ByteSize number Size in bytes
DateString string YYYY-MM-DD calendar date
TimeString string HH:MM time of day
UrlString string Validated URL
LocaleCode string xx or xx-YY locale code

3.2 Enums and Literal Unions#

The following literal-union vocabularies are shared across packages. Using these centrally prevents the string values from drifting between packages.

Type Values
Platform web, ios, android, desktop, server
SubscriptionTier free, premium, lifetime
ExperienceLevel beginner, intermediate, advanced
MeditationCategory sleep, stress, focus, anxiety, morning, evening, breathwork, body-scan, visualization, gratitude, self-compassion, relationships, work, creativity, general
SessionType timer, breathing, guided, ambient, body-scan, visualization, mindfulness, sleep, focus, custom
SessionState idle, preparing, active, paused, completing, completed, cancelled, interrupted
PlaybackState idle, loading, buffering, playing, paused, stopped, ended, error
ContentKind meditation, course, lesson, teacher, program, ambient-sound, breathing-pattern

3.3 Content and Session Models#

These are the primary data models shared across the engine. Downstream packages (session, progress, offline) define their own local shapes where they need additional fields, but these core models establish the common vocabulary.

ContentVersion{ version: string; checksum?: string; publishedAt: Timestamp; expiresAt?: Timestamp }.

LocalizedText{ locale: LocaleCode; value: string }.

ContentMetadatatitle, optional description, language: LocaleCode, category: MeditationCategory, tags: string[], optional durationSeconds, optional teacherId, optional imageUrl, premium: boolean, appStoreSafe: boolean, updatedAt: Timestamp.

ContentItem{ id: ContentId; kind: ContentKind; slug: string; metadata: ContentMetadata; version: ContentVersion }.

AudioTrackid: TrackId, optional contentId, title, durationSeconds, url: UrlString, mimeType: string, optional sizeBytes, optional waveform: number[], updatedAt: Timestamp.

MeditationSessionid, type: SessionType, state: SessionState, startedAt: Timestamp, optional endedAt, optional targetDurationSeconds, elapsedSeconds, optional contentId, optional trackId, optional metadata.

SessionResultsessionId, status (one of completed / partial / cancelled / interrupted), startedAt, endedAt, elapsedSeconds, optional targetDurationSeconds, completionPercentage: Percentage.

PauseRecord{ pausedAt: Timestamp; resumedAt?: Timestamp; reason?: string }.

InterruptionRecordinterruptedAt, optional resolvedAt, resumed: boolean, and a reason from: phone-call, alarm, notification, app-background, low-battery, connection-lost, user-action, system, unknown.

DateRange{ startDate: DateString; endDate: DateString }.

PageRequest / PageResult<TItem> — pagination helpers; PageResult carries items, totalCount, limit, offset, hasMore.

3.4 Utility Functions#

The utility modules are pure functions with no side effects. They are tree-shaken individually via subpath exports so consumers only pay for what they import.

Module Exports
duration.ts seconds, milliseconds, minutesToSeconds, secondsToMilliseconds, millisecondsToSeconds, clampPercentage, progressPercentage, splitSeconds
dates.ts timestamp, dateString, isTimeString, compareDateStrings, dateRange, addDays
format.ts formatDuration (M:SS or H:MM:SS), formatMinutes (N min), slugify, titleCase
validation.ts assertNonEmptyString, localeCode, urlString (accepts http:/https:/file:), ensureFiniteNumber, ensureIntegerRange

Constructor-style helpers throw RangeError for invalid input (e.g. seconds rejects negative or non-finite numbers; dateString rejects non-calendar dates).


4. @oshun/meditation-player — Audio Player Engine#

4.1 Track and Content Types#

These types define what a track looks like from the player's perspective. A MeditationTrack carries multiple AudioSource entries for different quality levels so the adaptive streaming system can switch between them.

Type Values / Shape
TrackId string (plain alias, not branded in this package)
AudioContentType meditation, music, ambient, guided, sleep, breathing, soundscape
AudioQuality low, medium, high, lossless
AudioFormat mp3, aac, m4a, ogg, wav, flac

AudioSourceurl, format: AudioFormat, quality: AudioQuality, optional bitrate, size, cached, cacheExpiry.

ContentMarkerid, time (seconds), type (chapter / cue / instruction / transition / custom), label, optional data.

TrackMetadatatitle, optional description, teacher, category, duration (seconds), coverImageUrl, waveform: number[], tags, language, markers: ContentMarker[].

MeditationTrackid, type: AudioContentType, metadata: TrackMetadata, sources: AudioSource[], optional preferredSourceIndex, isPremium, isOfflineAvailable, updatedAt.

4.2 Player State#

The player state types describe what the MeditationPlayer can report at any moment. PlayerState is the full snapshot; PlaybackProgress is the sub-type used for time-based progress events.

Type Values
PlaybackState idle, loading, buffering, playing, paused, stopped, ended, error
RepeatMode off, one, all
ShuffleMode off, on

PlayerErrorCode is a real TypeScript enum with members UNKNOWN, NETWORK_ERROR, FORMAT_NOT_SUPPORTED, DECODE_ERROR, SOURCE_NOT_FOUND, PLAYBACK_ABORTED, OUTPUT_DEVICE_ERROR, DRM_ERROR, PERMISSION_DENIED, AUDIO_CONTEXT_ERROR, CROSSFADE_ERROR, INVALID_STATE.

PlayerErrorcode: PlayerErrorCode, message, optional cause, optional trackId, timestamp, recoverable: boolean.

PlaybackProgressposition, duration, buffered: Array<[number, number]>, percentage, remaining (all seconds except percentage).

PlayerState — full state: playbackState, currentTrack, progress, volume, muted, playbackRate, repeatMode, shuffleMode, error, seeking, crossfading.

4.3 Playback Rate (playback-rate.ts)#

The playback rate policy lives in @oshun/contracts and is re-exported by the player. It is the canonical, enforced rule for how fast narration can be played — not just a suggestion. normalizePlaybackRate throws PlaybackRateRangeError for any rate outside the 0.85×–1.25× window unless clamp is explicitly passed.

PlaybackRate is re-exported from @oshun/contracts/tara/playback-rate. The quality-preserving policy is the canonical control surface:

typescript
QUALITY_PRESERVING_PLAYBACK_RATE_POLICY = {
  minRate: 0.85,
  maxRate: 1.25,
  step: 0.05,
  decimalPlaces: 2,
  preservePitch: true,
};

QUALITY_PRESERVING_PLAYBACK_RATE_OPTIONS therefore yields 9 discrete rates: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15, 1.20, 1.25. The package re-exports normalizePlaybackRate, isQualityPreservingPlaybackRate, nextPlaybackRate, playbackRateOptions, applyQualityPreservingPlaybackRate, and the PlaybackRateRangeError class. normalizePlaybackRate throws PlaybackRateRangeError for rates outside the 0.85×–1.25× range unless clamp is passed. applyQualityPreservingPlaybackRate sets playbackRate, defaultPlaybackRate, and the preservesPitch / mozPreservesPitch / webkitPreservesPitch flags on a media element.

4.4 Audio Mixing Types#

The mixer manages multiple concurrent audio layers (voice, music, ambient, effects) with independent volume control and ducking. The types below describe the mixer's configuration and state surface.

Type Values
AudioLayerType voice, music, ambient, effects, master

AudioLayerid, type, volume, muted, pan (−1 to 1), solo, optional track.

MixerStatemasterVolume, masterMuted, layers: Record<AudioLayerType, AudioLayer>.

CrossfadeConfigduration (ms), easing (linear / ease-in / ease-out / ease-in-out), overlap: boolean.

DuckConfigenabled, duckLevel (0–1), attackTime (ms), releaseTime (ms), affectedLayers: AudioLayerType[]. DEFAULT_DUCK_CONFIG ducks ['music', 'ambient'] to 0.3 with 200ms attack / 500ms release.

AutomationPointtime (ms), volume, optional easing. VolumeAutomationlayerType, points: AutomationPoint[], enabled.

The mixer also defines a sleep-fade subsystem: SleepFadeCompletion (none / pause / stop), SleepFadeTarget (alias of AudioLayerType), SleepFadeConfig, and SleepFadeState.

DEFAULT_MIXER_CONFIG layer volumes: voice 1.0, music 0.6, ambient 0.5, effects 0.8, master 1.0.

4.5 Queue Types#

The queue tracks a list of pending tracks plus a history of recently played ones. These types describe the queue's state and configuration.

QueueItemqueueId, track, addedAt, source (user / auto / recommendation / program).

QueueStateitems, currentIndex (−1 when nothing playing), originalOrder: string[], history: QueueItem[], historySize.

PlaybackQueueConfigmaxHistorySize, initialRepeatMode, initialShuffleMode. DEFAULT_QUEUE_CONFIG keeps a 50-item history.

4.6 Visualization Types#

The visualizer feeds real-time frequency data to the UI at the configured updateInterval. The types below describe one visualization frame and how beat detection is represented.

VisualizationDatafrequencyData: Uint8Array, timeDomainData: Uint8Array, amplitude (RMS 0–1), peak (0–1), frequencyBinCount.

VisualizationConfig — optional fftSize, smoothingTimeConstant, minDecibels, maxDecibels. ExtendedVisualizationConfig adds updateInterval, enableBeatDetection, beatSensitivity, frequencyBands. DEFAULT_VISUALIZATION_CONFIG (player) uses fftSize 2048, smoothing 0.8, minDecibels −100, maxDecibels −30, updateInterval 16ms, beat detection off, beatSensitivity 0.7, 8 frequency bands.

FrequencyBandindex, startFrequency, endFrequency, amplitude, peak. BeatDataisBeat, energy, averageEnergy, timeSinceLastBeat, estimatedBpm: number | null. ExtendedVisualizationData adds bands, beat, sampleRate, timestamp.

getStandardFrequencyBands() returns 7 named ranges (Hz): sub-bass 20–60, bass 60–250, low-mid 250–500, mid 500–2000, upper-mid 2000–4000, presence 4000–6000, brilliance 6000–20000.

4.7 Platform Abstraction Types#

The player's platform abstraction layer defines both the abstract interface for background audio management and the types for audio session events (interruptions, route changes, remote commands from hardware media buttons).

Platformweb, ios, android, electron, unknown. AudioBackendweb-audio, html-audio, native, expo-av, react-native-track-player.

AudioAdapter is the platform-abstraction interface: getCapabilities, load, play, pause, stop, seek, setVolume, setPlaybackRate, getCurrentTime, getBuffered, getDuration, destroy, optional createPeerAdapter (for overlapping crossfades) plus onEnded / onError / onBuffering / onTimeUpdate callbacks.

RemoteCommandplay, pause, stop, togglePlayPause, nextTrack, previousTrack, seekForward, seekBackward, seek.

AudioSessionCategoryambient, soloAmbient, playback, record, playAndRecord, multiRoute. AudioInterruptionTypebegan, ended. AudioInterruptionReasondefault, appWasSuspended, builtInMicMuted, routeChange, phoneCall, alarm, other. AudioRoute.typespeaker, headphones, bluetooth, airplay, carplay, unknown.

4.8 Configuration#

PlayerConfig contains the top-level player options. Nearly all fields have defaults so callers only need to override what they care about.

PlayerConfig (all optional) — initialVolume, defaultPlaybackRate, defaultRepeatMode, enableBackgroundAudio, enableAudioDucking, crossfade, preferredQuality, autoPlayNext, progressUpdateInterval, bufferAhead, enableVisualization, cacheSize. DEFAULT_PLAYER_CONFIG uses initialVolume 1, defaultPlaybackRate 1, crossfade 3000ms ease-in-out overlap, preferredQuality high, progressUpdateInterval 250ms, bufferAhead 30s, cacheSize 500 MB.

AudioCacheConfigmaxSize (default 500 MB), dbName (meditation-audio-cache), storeName (audio-files), defaultExpiration (0 = never), minFreeSpace (50 MB), useCacheApi, requestTimeout (30 s), enablePartialDownloads, chunkSize (1 MB).

AdaptiveStreamingConfigenableAdaptive, minBufferForPlayback (2 s), targetBuffer (30 s), maxBuffer (60 s), rebufferThreshold (0.5 s), upgradeThreshold (1.5×), downgradeThreshold (0.8×), speedSampleCount (10), maxRetries (3), retryDelay (1000ms), segmentTimeout (30 s).

AudioSessionConfig default — category playback, mode spokenAudio, autoRequestFocus/autoAbandonFocus/autoHandleInterruptions/resumeAfterInterruption true, resumeDelay 500ms. BackgroundAudioConfig default — lock-screen controls (play/pause, skip next/previous, seek bar, seek forward/backward at 15s intervals; stop/like/dislike off), keepAudioSessionActive true, showInControlCenter true.

BufferStateempty, filling, sufficient, full, stalled. NetworkQualityoffline, poor, fair, good, excellent.

4.9 Primary Exports#

The code block below lists the primary classes, factory functions, and hook factories exported by @oshun/meditation-player.

typescript
// Core class — event-driven, extends EventEmitter<MeditationPlayerEvents>
class MeditationPlayer {
  load(track): Promise<void>; play(): Promise<void>; pause(): void;
  stop(): void; seek(position): Promise<void>;
  setVolume(v): void; setMuted(m): void; toggleMute(): void;
  setPlaybackRate(rate): void; setRepeatMode(mode): void; setShuffleMode(mode): void;
  fadeTo/fadeIn/fadeOut(duration): Promise<void>;
  crossfade(nextTrack, duration?): Promise<void>;
  setAdapter(a): void; setCrossfadeAdapter(a): void; destroy(): void;
  // getters: currentState, currentTrack, playbackState, isPlaying, isPaused,
  //          isLoading, volume, isMuted, playbackRate, progress, currentTime,
  //          duration, repeatMode, shuffleMode
}

// PlaybackQueue — extends EventEmitter<PlaybackQueueEvents>
class PlaybackQueue {
  add/addMultiple/insert/playNext; remove/removeById/removeByQueueId; clear;
  reorder; current/currentItem; next/previous; skipTo/skipToTrackId;
  hasNext/hasPrevious; getUpcoming/getAt/indexOf/contains;
  setShuffleMode/toggleShuffle/reshuffle;
  setRepeatMode/cycleRepeatMode; getHistory/clearHistory;
}

// AudioMixer, AudioVisualizer, StreamingManager, NetworkQualityMonitor,
// BufferManager, AdaptiveBitrateSelector, AudioCacheManager, AudioPreloader,
// ServiceWorkerCacheAdapter

// Platform three-tier abstractions (abstract base + Web + Noop + factory):
abstract class BackgroundAudioHandler { /* WebBackgroundAudioHandler, NoopBackgroundAudioHandler */ }
function createBackgroundAudioHandler(config?): BackgroundAudioHandler
abstract class AudioSessionManager { /* WebAudioSessionManager, NoopAudioSessionManager */ }
function createAudioSessionManager(config?): AudioSessionManager

// React hook factories
createPlayerHooks(): { usePlayer, usePlaybackProgress, useQueue,
                       useVisualization, useAudioSession, useMixer }

The factory functions select Web vs Noop by detecting window / navigator. There is no separate native platform argument — React Native implementations extend the abstract base class in the consuming app.


5. @oshun/meditation-timer — Meditation Timer#

5.1 State Types#

The timer moves through a strict linear state machine. The TimerPhase values correspond to the three sub-periods of a session (preparation, main meditation, wind-down) plus an interval phase that fires at recurring bell marks.

Type Values
TimerState idle, preparing, running, paused, completed
TimerPhase preparation, meditation, interval, wind-down

5.2 Bell Types#

These types define the bell sound system. The 11-value BellType union covers all built-in bells plus custom for user-supplied audio. Six pre-configured BellSound constants — with real volumes and fade-out durations — are exported for direct use.

BellType (11 values): tibetan-bowl, singing-bowl, gong, temple-bell, chime, bell, soft-tone, nature-chime, crystal-bowl, zen-bell, custom.

BellTriggerstart, interval, warning, end, preparation-end.

BellSoundtype: BellType, optional customUrl, volume, repeatCount, repeatDelay (ms), fadeOutDuration (ms).

BellConfigurationstartBell, intervalBell, warningBell, endBell, preparationEndBell (each BellSound | null).

bells.ts exports 6 built-in BellSound constants, each with a real fade-out:

Constant type volume fadeOutDuration
BELL_TIBETAN_BOWL tibetan-bowl 0.7 4000ms
BELL_SINGING_BOWL singing-bowl 0.7 3000ms
BELL_GONG gong 0.6 6000ms
BELL_TEMPLE_BELL temple-bell 0.7 5000ms
BELL_CHIME chime 0.6 2000ms
BELL_SOFT_TONE soft-tone 0.5 1500ms

All six default to repeatCount: 1, repeatDelay: 0. DEFAULT_BELL_CONFIG uses BELL_SINGING_BOWL as the start bell and a 3×-repeat singing bowl (2000ms delay) as the end bell. BellPlayer resolves sounds against BELL_SOUND_PATHS (ten paths under /sounds/bells/), uses the Web Audio API with an HTML5 Audio fallback, and supports custom sounds via createCustomBell(url, options).

5.3 Ambient Sound Types#

The ambient sound system supports up to 8 simultaneous layers, each drawn from a 27-value sound catalog. The most common sounds are promoted to named constants for convenience.

AmbientSoundCategorynature, weather, urban, musical, noise, binaural, custom.

AmbientSoundType (27 values): rain, rain-light, rain-heavy, thunderstorm, ocean, ocean-waves, river, stream, waterfall, forest, birds, crickets, wind, wind-leaves, fire, campfire, coffee-shop, city, singing-bowls, om, temple, white-noise, pink-noise, brown-noise, binaural-alpha, binaural-theta, binaural-delta, custom.

ambient.ts defines an internal AMBIENT_SOUND_DEFINITIONS map covering all 26 non-custom types (each with a name, description, asset path under /sounds/ambient/, and default volume), and exports 7 built-in AmbientSound constants: AMBIENT_RAIN (0.5), AMBIENT_OCEAN (0.5), AMBIENT_FOREST (0.4), AMBIENT_FIRE (0.4), AMBIENT_WHITE_NOISE (0.3), AMBIENT_PINK_NOISE (0.3), AMBIENT_BROWN_NOISE (0.3). All loop.

AmbientSoundtype, category, optional customUrl, volume, loop. AmbientLayerid, sound, currentVolume, active, muted. AmbientMixid, name, layers: AmbientLayer[], masterVolume.

The three built-in curated mixes provide ready-made combinations for the most common use cases:

Constant Layers masterVolume
AMBIENT_MIX_PEACEFUL rain @ 0.4 + singing-bowls @ 0.2 1.0
AMBIENT_MIX_FOCUS brown-noise @ 0.3 + fire @ 0.2 1.0
AMBIENT_MIX_SLEEP ocean @ 0.4 + binaural-delta @ 0.2 0.8

AmbientPlayer supports per-layer volume, smooth fadeLayerVolume with an ease-in-out curve, mute/solo, loadMix with optional crossfade, and createMixFromCurrent / createCustomSound. DEFAULT_AMBIENT_CONFIG allows up to 8 concurrent layers with a 2000ms default fade.

5.4 Interval Types#

Intervals allow actions to be fired at specific time offsets or on a recurring cadence within a session (e.g., a bell every 10 minutes, or a voice prompt at the halfway mark).

IntervalActionTypebell, vibration, voice-prompt, ambient-change, custom. IntervalAction carries the type plus the matching payload field. TimerIntervalid, timeOffset (s), action, triggered, optional label. RecurringIntervalevery (s), action, optional startAfter, stopBefore, includeFirst.

5.5 Configuration#

TimerConfig is the top-level configuration object passed to MeditationTimer. Key sub-types for preparation and wind-down phases are shown below.

PreparationConfigenabled, duration (s), voiceCountdown. WindDownConfigenabled, duration (s), fadeAmbient, reminderBell.

TimerConfigduration (s), preparation, windDown, bells, intervals: TimerInterval[], recurringIntervals: RecurringInterval[], ambientMix: AmbientMix | null, hapticFeedback, screenDimming, keepScreenOn, backgroundMode (continue / pause / notify).

DEFAULT_TIMER_CONFIG — 10-minute (600 s) session, 5 s preparation, 10 s wind-down with fadeAmbient, singing-bowl start bell, 3×-repeat singing-bowl end bell, backgroundMode: continue.

HapticConfigenabled, intensity (light / medium / heavy), onStart, onInterval, onComplete, breathingPattern. HapticTypeimpact, notification, selection. HapticNotificationTypesuccess, warning, error.

5.6 Presets#

PresetDuration is a literal union of valid durations (in minutes):

PresetDuration — literal union 1 | 3 | 5 | 10 | 15 | 20 | 25 | 30 | 45 | 60 | 90 | 120. PresetCategoryquick, standard, extended, pomodoro, sleep, custom.

TimerPresetid, name, description, category, durationMinutes, config: Partial<TimerConfig>, builtIn, optional icon, sortOrder.

presets.ts defines 12 built-in presets (ALL_PRESETS), grouped into 5 collections:

Collection Presets
QUICK_PRESETS PRESET_1_MINUTE (Mindful Moment), PRESET_3_MINUTES (Quick Break), PRESET_5_MINUTES (Short Session)
STANDARD_PRESETS PRESET_10_MINUTES (Standard Session), PRESET_15_MINUTES (Extended Session), PRESET_20_MINUTES (Deep Practice), PRESET_30_MINUTES (Long Session)
EXTENDED_PRESETS PRESET_45_MINUTES (Extended Practice), PRESET_60_MINUTES (Full Hour)
POMODORO_PRESETS PRESET_POMODORO_WORK (25 min Focus Work), PRESET_POMODORO_BREAK (5 min Short Break)
SLEEP_PRESETS PRESET_SLEEP (30 min Sleep Timer, no end bell)

The 9 single-duration presets (1/3/5/10/15/20/30/45/60 minutes) are re-exported by name from the package entry point; the Pomodoro and Sleep presets are part of ALL_PRESETS but are not individually re-exported. Each preset carries a full Partial<TimerConfig> — for example PRESET_20_MINUTES schedules a recurring soft-chime interval bell every 10 minutes, and PRESET_60_MINUTES every 15 minutes.

Preset utilities: getPresetById, getPresetsByCategory, getPresetsByDuration, createCustomPreset(name, durationMinutes, configOverrides?, description?), presetToConfig. The PresetManager interface (createPresetManager()) tracks custom presets and favorites: getAllPresets, getBuiltInPresets, getCustomPresets, getPreset, addCustomPreset, removeCustomPreset, updateCustomPreset, getFavorites, addFavorite, removeFavorite, isFavorite.

5.7 Statistics Type#

TimerStatistics aggregates usage data across sessions for the timer. It is returned by the PresetManager and available for session-history displays.

TimerStatisticstotalSessions, totalTimeSeconds, averageDuration, longestSession, currentStreak, longestStreak, sessionsThisWeek, timeThisWeekSeconds, sessionsToday, timeTodaySeconds, lastSessionDate, favoritePresetId, favoriteDuration.

5.8 Primary Exports#

typescript
// MeditationTimer — extends EventEmitter<TimerEvents>
class MeditationTimer {
  configure(config): void; setDuration(s): void;
  setBellCallback(cb): void; setHapticCallback(cb): void;
  start(): Promise<void>; pause(): void; resume(): Promise<void>;
  stop(): void; reset(): void; skipPreparation(): void;
  addTime(s): void; extend(minutes): void; loadPreset(preset): void;
  getProgress(): TimerProgress | null; getSession(): readonly TimerSession | null;
  dispose(): void; destroy(): void;
}
// Options: tickInterval (default 1000ms), driftCorrection (default true),
// autoStop (default true). The timer compares against wall-clock timestamps to
// correct drift over long sessions.

// BellPlayer, AmbientPlayer

// Background three-tier: TimerBackgroundHandler (abstract) +
// WebTimerBackgroundHandler + NoopTimerBackgroundHandler + createTimerBackgroundHandler
// — wraps Wake Lock API, Notifications API, Web Locks API, Page Visibility API.

// Haptics three-tier: HapticManager (abstract) + WebHapticManager (Vibration
// API) + NoopHapticManager + createHapticManager.

// React hook factories
createTimerHooks(hooks): { useMeditationTimer, useTimerState,
                           useTimerProgress, useTimerPresets }

createTimerHooks and each createUse* factory take an injected ReactHooks object (useState, useEffect, useCallback, useMemo, useRef). The package also exports convenience hooks useMeditationTimer, useTimerState, useTimerProgress, useTimerPresets that resolve hooks registered via setTimerReactHooks (re-exported as setReactHooks from hooks.ts).


6. @oshun/meditation-breathing — Breathing Exercise Engine#

6.1 Phase and State Types#

These types describe the vocabulary of a breathing exercise. BreathingPhase names every phase a pattern can contain; ExerciseState mirrors the standard lifecycle state machine used across all engine packages.

Type Values
BreathingPhase inhale, hold-in, exhale, hold-out, rest
ExerciseState idle, running, paused, completed
PhaseTransition start, continue, end
PatternCategory relaxation, focus, energy, sleep, stress-relief, meditation, custom
PatternDifficulty beginner, intermediate, advanced
CompletionMode cycles, duration, manual
EasingFunction linear, easeIn, easeOut, easeInOut, sine

PhaseConfigphase: BreathingPhase, duration (s), label, optional instruction. RatioPattern{ inhale; holdIn; exhale; holdOut } durations.

BreathingPatternid, name, description, category, difficulty, phases: PhaseConfig[], cycleDuration (computed from phases), builtIn, optional icon, color, sortOrder, benefits, instructions, recommendedCycles, recommendedDuration.

6.2 Built-In Breathing Patterns (10)#

patterns.ts exports 10 BreathingPattern constants in ALL_PATTERNS. The ratio column shows the literal inhale–hold-in–exhale–hold-out timing in seconds from createPhasesFromRatio; phases with duration 0 are omitted from phases.

Constant id Name Category Difficulty Ratio (s)
PATTERN_BOX_BREATHING box-breathing Box Breathing focus beginner 4-4-4-4
PATTERN_4_7_8 4-7-8 4-7-8 Relaxing Breath relaxation intermediate 4-7-8-0
PATTERN_COHERENT coherent Coherent Breathing meditation beginner 5-0-5-0
PATTERN_ENERGIZING energizing Energizing Breath energy beginner 2-0-4-0
PATTERN_CALMING calming Calming Breath stress-relief beginner 4-0-2-0
PATTERN_2_1_RATIO 2-1-ratio 2:1 Ratio Breathing relaxation intermediate 4-0-8-0
PATTERN_ALTERNATE_NOSTRIL alternate-nostril Alternate Nostril meditation intermediate see below
PATTERN_WIM_HOF wim-hof Wim Hof Power Breathing energy advanced 2-2 (one power breath)
PATTERN_SLEEP sleep Sleep Breathing sleep beginner 4-7-8-2
PATTERN_FOCUS focus Focus Breathing focus beginner 4-2-4-2

Notes on the patterns whose shape is not a simple four-part ratio:

  • Alternate Nostril is a hand-built 6-phase cycle: inhale 4 (left nostril) → hold-in 4 → exhale 4 (right) → inhale 4 (right) → hold-in 4 → exhale 4 (left).
  • Wim Hof is a single 2-phase power breath (inhale 2, exhale 2); recommendedCycles is 30, and the instructions describe the full retention protocol (30 breaths, exhale hold, 15 s recovery hold, 3 rounds).
  • Calming Breath is a long-inhale / short-exhale pattern (inhale 4, exhale 2), not a long-exhale pattern.
  • Energizing Breath is a short-inhale / longer-exhale pattern (inhale 2, exhale 4).

Each pattern carries an icon, hex color, sortOrder (1–10), a benefits array, an instructions string, recommendedCycles, and recommendedDuration.

Pattern collections (computed by filtering ALL_PATTERNS): BEGINNER_PATTERNS (difficulty beginner), RELAXATION_PATTERNS (category relaxation or sleep), FOCUS_PATTERNS (focus), ENERGY_PATTERNS (energy), MEDITATION_PATTERNS (meditation). Pattern utilities: getPatternById, getPatternsByCategory, getPatternsByDifficulty, createPatternFromRatio.

6.3 Exercise Configuration#

ExerciseConfig configures a single exercise run: which pattern to use, how completion is defined, and which guidance modalities (audio, haptics) are active.

ExerciseConfigpattern, completionMode, targetCycles, targetDurationMinutes, audioGuidance, hapticFeedback, countdownSeconds, transitionSounds, voiceType (none / bell / voice-male / voice-female), volume (0–1). DEFAULT_EXERCISE_CONFIGcompletionMode: cycles, 10 target cycles, 5-minute fallback, audio/haptics on, 3-second countdown, voiceType: bell, volume 0.7.

6.4 Visualization#

The VisualizationState snapshot is what the UI receives on every tick. It contains everything a renderer needs to draw the current breathing circle without requiring the UI to compute any animation math itself.

VisualizationStatephase, phaseProgress (0–1), cycleProgress (0–1), scale (0–1), targetScale, opacity, color (hex), rotation (degrees), pulse (0–1), easing: EasingFunction, label, timerDisplay.

PhaseColors — per-phase hex colors. DEFAULT_PHASE_COLORS — inhale #4CAF50, hold-in #2196F3, exhale #9C27B0, hold-out #FF9800, rest #607D8B.

VisualizationConfigminScale, maxScale, colors, showPulse, pulseFrequency (Hz), showRotation, rotationSpeed. DEFAULT_VISUALIZATION_CONFIG — minScale 0.6, maxScale 1.0, pulse on at 0.5 Hz, rotation off at 10°/s. Per-phase easing: inhale easeOut, exhale easeIn, holds linear, rest sine. interpolatePhaseColor blends two phase colors in RGB space.

6.5 Haptics#

The haptics subsystem provides phase-timed vibration feedback. The pacing controller builds full-phase vibration patterns — stronger pulses on inhale, lighter pulses on exhale — enabling eyes-closed, screen-free practice for accessibility.

HapticIntensitylight, medium, heavy. BreathingHapticConfigenabled, intensity, onPhaseTransition, inhalePattern, exhalePattern, onCycleComplete, onExerciseComplete, pacing: VibrationPacingConfig. VibrationPacingConfigenabled, intensity, intervalMs, phaseStartCue, completionCue, maxPhaseDurationSeconds. DEFAULT_VIBRATION_PACING_CONFIG is disabled by default with 1000ms interval and a 30-second cap; DEFAULT_HAPTIC_CONFIG is enabled with medium intensity.

The package exports BreathingHapticManager, VibrationPacingController, buildVibrationPacingPattern, getVibrationPacingCompletePattern, isVibrationPacingSupported, createWebVibrationAdapter, and the base PHASE_PATTERNS map. The VibrationAdapter interface (isSupported, vibrate, cancel) abstracts the platform vibration API. The pacing subsystem builds full-phase vibration patterns (stronger inhale pulses, lighter exhale pulses, steady holds) for hearing-accessibility eyes-closed practice.

6.6 Audio Guidance#

GuidancePlayer plays phase-boundary cues from /sounds/breathing/ asset paths, synchronized with the exercise runner's phase events. Each AudioCueType value maps to a specific trigger point in the exercise lifecycle.

AudioCueTypeinhale, hold, exhale, release, cycle-complete, exercise-complete, countdown, bell, custom. AudioCuetype, url, volume, delay. AudioGuidanceConfigenabled, masterVolume, phaseTransitions, cycleComplete, exerciseComplete, voiceCues, bellCues, customCues: Map<AudioCueType, AudioCue>. DEFAULT_AUDIO_CONFIG has bell cues on, voice cues off, master volume 0.7. GuidancePlayer resolves cues against /sounds/breathing/ asset paths and plays them via the Web Audio API in sync with the exercise's phase events.

6.7 Session History#

SessionRecord captures what happened in a single breathing exercise session. HistoryStatistics aggregates across all recorded sessions for history display.

SessionRecordid, patternId, patternName, startedAt, endedAt, durationSeconds, completedCycles, completed, completionMode, target, optional notes, rating. HistoryStatisticstotalSessions, totalTimeSeconds, totalCycles, averageSessionSeconds, averageCyclesPerSession, favoritePatternId, currentStreak, longestStreak, lastSessionDate, sessionsThisWeek, sessionsToday.

6.8 Primary Exports#

typescript
// BreathingExercise — extends EventEmitter<ExerciseEvents>
class BreathingExercise {
  start(pattern, configOverrides?): Promise<void>;
  pause(): void; resume(): void; stop(): void; reset(): void; skipPhase(): void;
  setRepetitions(n): void; setDuration(min): void; setCompletionMode(m): void;
  setAudioGuidance(b): void; setHapticFeedback(b): void;
  getConfig(); getSession(); dispose();
  // getters: state, phase; predicates: isRunning, isPaused, isCompleted, isIdle
}
// Options: tickInterval (default 100ms for smooth animation), driftCorrection.

// BreathingPatternBuilder — fluent builder:
//   .name/.description/.category/.difficulty/.icon/.color/.benefit/.benefits
//   .instructions/.recommendedCycles/.recommendedDuration
//   .inhale/.holdIn/.exhale/.holdOut/.rest  (hold-* and rest skip 0-duration)
//   .phase(type) -> PhaseBuilder  .repeat(n)  .fromRatio(i,h,e,h)  .build()
// Factories: createPatternBuilder, createPatternFromRatio (aka quickPattern),
//            createBoxBreathing(seconds), createCoherentBreathing(seconds).

// VisualizationProvider, GuidancePlayer, BreathingHapticManager

// SessionHistoryManager + InMemorySessionStorage + LocalSessionStorage
//   (createSessionHistoryManager / createLocalSessionHistoryManager);
//   exportAsJSON / exportAsCSV.

// React hook factories
createBreathingHooks(hooks): { useBreathingExercise, useBreathingState,
                               useBreathingAnimation, useBreathingPatterns }

createBreathingHooks and each createUse* factory take an injected ReactHooks object. The package also exports the convenience hooks useBreathingExercise, useBreathingState, useBreathingAnimation, useBreathingPatterns (which resolve hooks registered via setBreathingReactHooks).


7. @oshun/meditation-session — Session Management#

7.1 Branded Types and Enums#

The session package defines its own branded types (separate from core) for domain-specific safety, and its own enum values for session lifecycle concepts.

Branded types: SessionId, DurationSeconds, DurationMs, Timestamp, DateString, TimeString, Percentage.

| Type | Values | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | --- | --- | --- | --------------- | | SessionType | timer, breathing, guided, ambient, body-scan, visualization, mindfulness, sleep, focus, custom | | SessionState | idle, preparing, active, paused, completing, completed, cancelled, interrupted | | InterruptionReason | phone-call, alarm, notification, app-background, low-battery, connection-lost, user-action, system, unknown | | CompletionStatus | full, partial, minimal, abandoned | | RecurrencePattern | daily, weekdays, weekends, weekly, monthly, custom | | DayOfWeek | 0 | 1 | 2 | 3 | 4 | 5 | 6 (0 = Sunday) | | AnalyticsEventType | session_created, session_started, session_paused, session_resumed, session_completed, session_cancelled, session_interrupted, session_abandoned, phase_changed, milestone_reached, achievement_unlocked |

7.2 Configuration#

These configuration objects are passed to the session manager and its sub-systems at construction time. All fields have defaults; only override what differs from DEFAULT_SESSION_CONFIG.

SessionAudioConfigprimarySource, backgroundSource, guidanceSource, primaryVolume, backgroundVolume, enableDucking, fadeDurationMs.

SessionTimingConfigtargetDurationSeconds, preparationSeconds, completionSeconds, progressIntervalMs, allowOvertime, maxOvertimeSeconds.

SessionNotificationConfigstartBell, endBell, intervalBells, bellIntervalSeconds, hapticFeedback.

SessionConfigtype, optional title / description, timing, optional audio, notifications, keepScreenAwake, blockNotifications, optional metadata / tags. DEFAULT_SESSION_CONFIG defines a 600-second target with 5 s preparation, 10 s completion, audio ducking on, start/end bells on, interval bells off.

PersistenceConfigautoSave, autoSaveIntervalMs, saveOnPause, saveOnBackground, keepHistory, maxHistoryItems. DEFAULT_PERSISTENCE_CONFIG auto-saves every 5000ms, keeps 100 history items.

AnalyticsConfigenabled, optional tracker, includeDeviceInfo, sampleRate (0–1), optional customProperties.

SchedulerConfigstorage, checkIntervalMs, optional onNotification / onTrigger, timezone. DEFAULT_SCHEDULER_CONFIG checks every 60000ms in the resolved local timezone.

SessionManagerConfig — optional persistence / storage / analytics / defaultConfig, minStreakDurationSeconds, enableDndMode. DEFAULT_SESSION_MANAGER_CONFIG requires 60 seconds for streak credit and enables DND mode.

7.3 State and Result Models#

These models describe what a session looks like at runtime and what it produces when it ends. MeditationSession is the live session object; SessionResult is the summary returned by completeSession().

SessionProgresselapsedSeconds, remainingSeconds, progressPercentage, isOvertime, overtimeSeconds, currentPhase (preparation / main / completion / overtime).

SessionInterruptiontimestamp, reason, durationMs, resumed. SessionPausepausedAt, resumedAt | null, durationMs.

MeditationSession (session-package shape) — id, config, state, createdAt, startedAt, endedAt, updatedAt, activeDurationSeconds, totalElapsedSeconds, pauses, interruptions, optional notes, rating, moodBefore, moodAfter, heartRateData: number[], userData.

SessionResultsession, completionStatus, completionPercentage, activeDurationSeconds, totalPauseSeconds, pauseCount, interruptionCount, countsTowardStreak, optional achievements / milestones.

ScheduledSessionid, sessionConfig, title, optional description, enabled, time: TimeString, recurrence, optional daysOfWeek / dayOfMonth, startDate, optional endDate, notification, createdAt, updatedAt, optional lastTriggeredAt / nextScheduledAt / metadata. ScheduleNotificationenabled, minutesBefore, title, body, optional sound / vibrate / persistent. UpcomingSessionschedule, scheduledAt, notified, started, skipped.

7.4 Primary Exports#

typescript
// SessionManager — extends EventEmitter<SessionManagerEvents>
class SessionManager {
  createSession(config): MeditationSession;
  startSession(session): Promise<void>;
  pauseSession(): void;
  resumeSession(): void;
  completeSession(): Promise<SessionResult>;
  cancelSession(): void;
  recordInterruption(reason): void;
  getCurrentSession(): MeditationSession | null;
  getProgress(): SessionProgress | null;
}
// createSessionManager(config?)

// Persistence — three interchangeable backends:
//   InMemorySessionStorage, LocalSessionStorage, IndexedDBSessionStorage
//   + SessionPersistenceManager (auto-save, app-background save, recovery)
//   factories: createInMemoryStorage, createLocalStorage,
//              createIndexedDBStorage, createPersistenceManager

// Analytics:
//   SessionAnalyticsManager, ConsoleAnalyticsTracker, InMemoryAnalyticsStorage
//   factories: createAnalyticsManager, createConsoleTracker,
//              createInMemoryAnalyticsStorage
//   AggregatedAnalytics: totalSessions, totalActiveSeconds,
//     averageDurationSeconds, completionRate, sessionsByType,
//     sessionsByDayOfWeek, sessionsByHourOfDay, averageCompletionPercentage,
//     sessionsByCompletionStatus, totalPauseCount, averagePausesPerSession,
//     averageMoodChange

// Scheduling:
//   SessionScheduler + InMemorySchedulerStorage + LocalSchedulerStorage
//   exportToICalendar(schedule) -> iCalendar (.ics) text
//   factories: createSessionScheduler, createInMemorySchedulerStorage,
//              createLocalSchedulerStorage

// React hook factories
createSessionHooks: (createUseSessionManager,
  createUseCurrentSession,
  createUseSessionProgress,
  createUseSessionState,
  createUseScheduler);

useScheduler's result exposes schedules, upcoming, nextSession, isRunning, createSchedule, updateSchedule, deleteSchedule, enableSchedule, disableSchedule, start, stop.


8. @oshun/meditation-progress — Progress Tracking#

8.1 Branded Types and Enums#

The progress package defines its own branded types and the enum vocabularies specific to streak management, statistics, achievements, and sync.

Branded types: DurationMinutes, DurationSeconds, Timestamp, DateString, Percentage.

Type Values
SessionType same 10 values as core/session
SessionStatus completed, interrupted, cancelled
PredefinedRange today, yesterday, this-week, last-week, this-month, last-month, this-year, last-year, all-time
AchievementCategory streak, time, sessions, exploration, consistency, special
AchievementRarity common, uncommon, rare, epic, legendary
MilestoneCategory minutes, sessions, streak, days
SyncStatus idle, syncing, error, success
ConflictStrategy local-wins, remote-wins, merge, manual

8.2 Core Models#

MeditationSession (progress-package shape) has a simpler shape than the session package's version — it captures only the fields the progress tracker needs to update streaks and statistics.

MeditationSession (progress-package shape) — id, type, startedAt, endedAt, durationSeconds, optional targetDurationSeconds, status, optional patternId / contentId / rating / notes / metadata.

StreakDatacurrentStreak, longestStreak, streakStartDate, longestStreakEndDate, completedToday, completedYesterday, freezesRemaining, freezesUsed, lastFreezeDate, totalActiveDays.

StreakConfig governs how the streak calculator determines whether a session counts toward a given day's practice:

StreakConfigminSessionSeconds, maxFreezesPerMonth, timezone, dayResetHour, allowGracePeriod, gracePeriodHours. DEFAULT_STREAK_CONFIG requires 60-second sessions, allows 2 freezes/month, uses UTC, resets at hour 0, and allows a 4-hour grace period.

Statistics is a wide aggregate type: range, totalTimeSeconds, totalSessions, averageSessionSeconds, medianSessionSeconds, longestSessionSeconds, shortestSessionSeconds, sessionsByType, timeByType, sessionsByDayOfWeek, sessionsByTimeOfDay, mostActiveDay, mostActiveTime, completionRate, consistencyScore, sessionsPerWeek, sessionsPerDay, activeDays, activeDaysPercentage, averageRating, favoriteType. TimeOfDayDistribution buckets are earlyMorning (5–9am), morning (9–12), afternoon (12–5pm), evening (5–9pm), night (9pm–5am).

8.3 Achievements#

ACHIEVEMENT_DEFINITIONS contains 26 built-in achievements. Each has id, name, description, category, rarity, icon, points, hidden, condition.

The table below groups them by category. Rarity scales with goal difficulty — first-* achievements are common; year-long streak, 500 hours, and 1000 sessions are legendary and hidden until unlocked.

Group Achievements
First steps first-meditation (10pt), first-breathing (10pt), first-guided (10pt)
Streak streak-3, streak-7, streak-14, streak-30, streak-60, streak-100, streak-365 (25→2000pt)
Time time-1h, time-10h, time-50h, time-100h, time-500h (25→1500pt)
Sessions sessions-10, sessions-50, sessions-100, sessions-500, sessions-1000 (25→1000pt)
Exploration explorer-3 (try 3 types, 30pt), explorer-all (try all 10 types, 150pt)
Consistency early-bird (10 sessions before 7am), night-owl (10 sessions after 10pm)
Special perfectionist (a full week with 100% completion, hidden), marathon (a 60+-minute session)

Achievement extends AchievementDefinition with unlocked, unlockedAt, progress, currentValue, targetValue. The AchievementManager re-evaluates all ACHIEVEMENT_THRESHOLDS against completed sessions on every recorded session.

8.4 Milestones#

MILESTONE_DEFINITIONS contains 26 built-in milestones across 4 categories. Unlike achievements, milestones are strictly progress markers against quantitative thresholds; they do not have rarity tiers. Each has id, name, description, category, targetValue, icon, celebrationMessage.

Category Milestone target values
minutes 100, 500, 1000, 2500, 5000, 10000, 25000, 50000 (8)
sessions 10, 25, 50, 100, 250, 500, 1000 (7)
streak 7, 14, 30, 60, 90, 180, 365 days (7)
days 7, 30, 100, 365 active days (4)

Milestone extends MilestoneDefinition with reached, reachedAt, progress, currentValue. MilestoneNotification{ milestone; isNew }.

8.5 Export and Sync#

ExportDataTypessessions, statistics, streak, achievements, milestones (all boolean). ExportOptions — optional dateRange, dataTypes, sessionTypes, completedOnly, isoTimestamps. ExportData carries metadata plus the selected sections. ProgressExporter produces both JSON and CSV output.

SyncStatestatus, lastSyncAt, pendingChanges, lastError, isOnline. SyncConfigendpoint, getAuthToken, conflictStrategy, autoSyncInterval, syncOnStart, syncOnReconnect. ProgressSyncManager queues offline changes, uploads them, merges remote sessions, and resolves conflicts per the configured strategy.

8.6 Primary Exports#

typescript
// ProgressTracker — extends EventEmitter<ProgressEvents>
class ProgressTracker {
  recordSession(session): Promise<{ streak; newAchievements; newMilestones }>;
  getHistory(options?): Promise<SessionHistory>;
  // delegates to StreakCalculator, StatisticsCalculator, AchievementManager,
  // MilestoneManager, and (when syncConfig is supplied) ProgressSyncManager
}
// InMemoryProgressStorage, LocalProgressStorage
// factories: createProgressTracker, createInMemoryProgressTracker,
//            createLocalProgressTracker

// StreakCalculator (timezone-aware, freeze tokens, grace period)
// StatisticsCalculator + getDateRangeFromPredefined
// AchievementManager + ACHIEVEMENT_DEFINITIONS
// MilestoneManager + MILESTONE_DEFINITIONS
// ProgressExporter, ProgressSyncManager

// React hook factories
createProgressHooks: (createUseStreak,
  createUseStatistics,
  createUseAchievements,
  createUseMilestones,
  createUseSync,
  createUseProgressTracker);
// Convenience hooks (after setProgressReactHooks): useStreak, useStatistics,
//   useAchievements, useMilestones, useSync, useProgressTracker

recordSession is atomic: it saves the session, recalculates the streak, evaluates achievements, checks milestones, optionally queues a sync change, and returns the updated streak plus newly unlocked achievements/milestones in one call.


9. @oshun/meditation-offline — Offline Content Management#

9.1 Branded Types and Enums#

Branded types: ContentId, DownloadId, VersionString, ByteSize, Timestamp, Percentage, UrlString.

The enums below define every status and policy value in the offline system — from the download state machine to the suggestion engine.

Type Values
ContentType audio, guided-meditation, course, music, ambient-sound, video, image, data, bundle
ContentQuality low, medium, high, lossless
DownloadStatus queued, downloading, paused, completed, failed, cancelled, verifying
DownloadErrorType network, storage, authentication, not-found, checksum-mismatch, expired, cancelled, unknown
QueuePriority critical, high, normal, low, background
CleanupStrategy oldest-first, least-used, largest-first, expired-first, custom
VersionComparison older, same, newer
UpdatePolicy manual, notify, auto-wifi, auto-always
SuggestionReason frequently-used, favorite, course-progress, similar-content, popular, new-release, expiring-soon, small-size, recommended
NetworkType wifi, cellular, ethernet, offline, unknown

9.2 Content and Download Models#

DownloadableContent describes a content item that can be downloaded. Once downloaded, it is represented as DownloadedContent which extends the base with local storage metadata.

DownloadableContentid, type, version: VersionString, url, sizeBytes, optional checksum (SHA-256), metadata, qualityOptions, selectedQuality, requiresAuth, expiresAt. DownloadedContent extends it with localPath, downloadedBytes, downloadedAt, lastAccessedAt, accessCount, isComplete, isVerified.

DownloadErrortype, message, optional statusCode, retryable, timestamp, retryCount. DownloadProgressdownloadId, contentId, status, downloadedBytes, totalBytes, progressPercentage, speedBytesPerSecond, estimatedSecondsRemaining, startedAt, optional error.

9.3 Configuration#

QueueConfigmaxConcurrent, maxRetries, retryDelayMs, exponentialBackoff, maxBackoffMs, priorityWeights, autoStart. DEFAULT_QUEUE_CONFIG — 3 concurrent downloads, 3 retries, exponential backoff capped at 60000ms, priority weights critical 100 / high 75 / normal 50 / low 25 / background 10.

StorageLimitsmaxTotalBytes, warningThreshold, criticalThreshold, minFreeBytes, optional contentTypeLimits. DEFAULT_STORAGE_LIMITS — 5 GB cap, 80% warning, 95% critical, 100 MB minimum free.

CleanupConfigstrategy, targetUsagePercentage, minAgeDays, optional excludeTypes / excludeIds, autoCleanExpired, optional customCleanup. DEFAULT_CLEANUP_CONFIG uses least-used, targets 70% usage, requires a 7-day minimum age, auto-cleans expired content.

VersioningConfigcheckIntervalMs, updatePolicy, checkOnStart, optional manifestUrl, deleteOldVersion. DEFAULT_VERSIONING_CONFIG — checks every 24 hours with notify policy.

NetworkPreferenceswifiOnly, allowMetered, allowCellular, optional maxCellularBytes. DEFAULT_NETWORK_PREFERENCES — Wi-Fi not required, metered disallowed, cellular allowed up to 50 MB.

SuggestionConfigmaxSuggestions, minConfidence, useUsageData, suggestFavorites, suggestCourseProgress, suggestPopular, optional maxSuggestedBytes. DEFAULT_SUGGESTION_CONFIG returns up to 10 suggestions above 0.5 confidence.

OfflineManagerConfig — optional queue / storage / cleanup / versioning / network / suggestions / storagePath, verifyChecksums, resumeOnStart. DEFAULT_OFFLINE_MANAGER_CONFIG verifies checksums and resumes incomplete downloads on start.

9.4 Suggestion Scoring#

The suggestion engine combines eight behavioral signals into a single confidence score for each candidate content item. The weights are configurable — lower them to deprioritize a signal, or raise them to make it more influential.

ScoringWeightsDEFAULT_SCORING_WEIGHTS: frequentlyUsed 1.0, favorites 0.9, courseProgress 0.85, similarContent 0.7, popularity 0.6, newContent 0.5, preferencesMatch 0.4, smallSize 0.3. DownloadSuggestioncontent, reason: SuggestionReason, confidence (0–1), explanation, suggestedPriority.

9.5 Primary Exports#

typescript
// OfflineManager — extends EventEmitter<OfflineManagerEvents>
class OfflineManager {
  initialize(): Promise<void>;
  setDownloadHandler/setContentVerifier/setFileDeleter/setManifestFetch;
  setNetworkPreferences/setCatalog/setUserPreferences/updateNetworkState;
  download(content, priority?): ...; downloadMultiple(...);
  cancelDownload/pauseDownload/resumeDownload/pauseAllDownloads/resumeAllDownloads;
  clearDownloadQueue; getQueueState; getDownloadProgress; isDownloading;
  isAvailableOffline; getOfflineContent; getAllOfflineContent;
  removeOfflineContent/removeMultipleOfflineContent;
  getStorageInfo; cleanupStorage; cleanupExpired;
  checkForUpdates; checkContentUpdate; updateContent; getRequiredUpdates;
  getSuggestions; downloadSuggested; saveState; shutdown; clearAll;
}
// createOfflineManager(config?), createBrowserOfflineManager()
// detectNetworkState(), detectNetworkType()

// DownloadQueue (createDownloadQueue), StorageManager
// Storage backends: InMemoryStorage, LocalStorageBackend, IndexedDBStorage
//   (createInMemoryStorageManager / createLocalStorageManager /
//    createIndexedDBStorageManager)
// ContentVersionManager + version utilities: createVersion, incrementVersion,
//   isValidVersion, sortVersions, getLatestVersion, filterVersionRange
// SuggestionEngine (createSuggestionEngine), getQuickSuggestions

// React hook factories
createOfflineHooks: createUseOfflineContent, createUseOfflineContentById,
  createUseAllOfflineContent, createUseDownloadProgress, createUseNetworkState,
  createUseQueueState, createUseStorageInfo, createUseSuggestions

createBrowserOfflineManager() wires an IndexedDB-backed storage manager and browser network detection.


10. @oshun/meditation-analytics — Session Analytics#

A small, dependency-free package (no eventemitter3, no React) for privacy-conscious event tracking. It is distinct from SessionAnalyticsManager in the session package: that class computes local aggregate statistics, while this package emits outbound events to external analytics backends.

AnalyticsEventName (11 values): session_started, session_paused, session_resumed, session_completed, session_interrupted, content_started, content_completed, timer_completed, breathing_completed, download_completed, streak_updated.

AnalyticsEvent<TProperties>name, occurredAt, anonymousId, optional sessionId / contentId, properties.

AnalyticsProvider interface — optional identify, required track, optional flush. MemoryAnalyticsProvider is an in-memory provider (exposes events and identified, plus clear()) used for testing.

AnalyticsClientOptionsanonymousId, provider, optional enabled, consentGranted, now, defaultProperties. AnalyticsClient gates all tracking on enabled && consentGranted (setEnabled / setConsentGranted), merges defaultProperties into every event, and exposes identify, track, flush.

SessionSummaryInput / SessionSummary plus summarizeSession (computes durationSeconds and a clamped completionRatio, validates time ordering) and trackSessionSummary (emits session_completed or session_interrupted from a summary). Tracking captures only behavioural fields — durations, completion ratios, session type, IDs — and is keyed by an anonymousId, never personally identifiable content.


11. Cross-Cutting Patterns#

The six engine packages share the implementation patterns described in this section. Understanding these once lets you reason about any of the six packages without re-learning the pattern for each.

11.1 Event-Driven Core#

Each engine package's primary class extends EventEmitter from eventemitter3 with a typed event map (MeditationPlayerEvents, TimerEvents, ExerciseEvents, SessionManagerEvents, ProgressEvents, OfflineManagerEvents). The meditation-analytics package does not use eventemitter3.

11.2 Three-Tier Platform Abstraction#

Packages that touch platform APIs ship an abstract base class plus a Web implementation, a Noop implementation, and a factory. This makes every platform-dependent feature testable in Node.js (Noop), functional in the browser (Web), and extensible for React Native (extend the abstract base class in the consuming app).

The packages that use this pattern and what they abstract:

  • meditation-playerBackgroundAudioHandler / Web… / Noop… / createBackgroundAudioHandler; AudioSessionManager / Web… / Noop… / createAudioSessionManager.
  • meditation-timerTimerBackgroundHandler / Web… / Noop… / createTimerBackgroundHandler; HapticManager / WebHapticManager / NoopHapticManager / createHapticManager.

The breathing package uses a lighter VibrationAdapter interface with createWebVibrationAdapter. React Native implementations are provided by the consuming application by extending the abstract base classes — the factory functions only choose between Web and Noop based on window / navigator / navigator.vibrate detection.

11.3 Interchangeable Storage Backends#

All backends in a package implement the same storage interface. The table below maps each package to its available backends.

Package Backends
breathing InMemorySessionStorage, LocalSessionStorage
session InMemorySessionStorage, LocalSessionStorage, IndexedDBSessionStorage; schedulers add InMemorySchedulerStorage, LocalSchedulerStorage
progress InMemoryProgressStorage, LocalProgressStorage
offline InMemoryStorage, LocalStorageBackend, IndexedDBStorage
player AudioCacheManager (IndexedDB) + ServiceWorkerCacheAdapter

All backends implement the package's storage interface (SessionStorage, ProgressStorage, OfflineStorage, SchedulerStorage, SessionStorage for breathing history) so business logic is backend-agnostic.

11.4 React Hook Factory Pattern#

Player, timer, breathing, session, progress, and offline export createUse* hook factories plus a combined create*Hooks factory. React is an optional peer dependency: a setReactHooks registration function (re-exported under package-specific aliases — setTimerReactHooks, setBreathingReactHooks, setProgressReactHooks) registers useState / useEffect / useCallback / useMemo / useRef once at app startup.

The three steps for using the hook factories in a React application are:

typescript
// 1. Register React hooks once at app startup
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { setReactHooks } from '@oshun/meditation-player';
setReactHooks({ useState, useEffect, useCallback, useMemo, useRef });

// 2. Either use the package's convenience hooks directly...
import { usePlayer } from '@oshun/meditation-player';

// 3. ...or build bound hooks from a factory:
const hooks = { useState, useEffect, useCallback, useMemo, useRef };
const { useBreathingExercise } = createBreathingHooks(hooks);

createBreathingHooks, createTimerHooks, and createPlayerHooks accept the injected ReactHooks object as an argument. Breathing and progress also export zero-argument convenience hooks (useBreathingExercise, useStreak, …) that resolve hooks registered via their setReactHooks alias.

11.5 Branded Types#

meditation-core, meditation-session, meditation-progress, and meditation-offline each define their own branded primitive types (SessionId, DurationSeconds, ContentId, ByteSize, …) using the T & { readonly __brand: '…' } pattern, preventing accidental misuse of raw string / number values at compile time. meditation-player and meditation-timer use plain type aliases (TrackId, DurationSeconds = number) rather than brands.


12. Configuration Examples#

These short examples show the most common construction patterns. Each demonstrates selecting a storage backend and passing configuration overrides for a specific concern.

typescript
// Timer with a custom tick interval and drift correction
const timer = new MeditationTimer({
  tickInterval: 1000,
  driftCorrection: true,
  autoStop: true,
  config: {
    duration: 600,
    preparation: { enabled: true, duration: 5, voiceCountdown: false },
  },
});

// Session persistence with IndexedDB and auto-save
const manager = createSessionManager({
  storage: new IndexedDBSessionStorage(),
  persistence: { autoSave: true, autoSaveIntervalMs: 5000 },
});

// Offline manager: IndexedDB storage, 3 concurrent downloads, Wi-Fi-only
const offline = createOfflineManager({
  queue: { maxConcurrent: 3 },
  network: { wifiOnly: true },
  storage: { maxTotalBytes: (5 * 1024 * 1024 * 1024) as ByteSize },
  verifyChecksums: true,
  resumeOnStart: true,
});

13. Build and Test#

Running Tests and Builds#

bash
# Per-library tests (Nx)
pnpm nx test @oshun/meditation-player
pnpm nx test @oshun/meditation-timer

# Build a library
pnpm nx build @oshun/meditation-session

# All meditation-domain libraries share the scope:meditation tag
pnpm nx run-many --target=test --projects=tag:scope:meditation

Every package configures Vitest via a vitest.config.ts and an @nx/vite:test target. When Nx is blocked by duplicate worktree projects, run npx vitest run <path> and npx tsc --noEmit from the library directory.

13.1 Test Coverage#

The table below maps each package to its spec files. When tracing a failing test back to its source, match the spec filename to the package directory under libs/meditation/<package>/src/.

Package Spec files
core core.spec.ts
player player.spec.ts, queue.spec.ts, mixer.spec.ts, playback-rate.spec.ts
timer timer.spec.ts
breathing exercise.spec.ts, haptics.spec.ts
session manager.spec.ts, persistence.spec.ts, analytics.spec.ts, scheduler-crud.spec.ts, scheduler-ical.spec.ts, scheduler-recurrence.spec.ts, scheduler-triggers.spec.ts, scheduling-storage.spec.ts
progress tracker.spec.ts
offline manager.spec.ts, queue.spec.ts, storage.spec.ts, suggestions.spec.ts, versioning.spec.ts
analytics analytics.spec.ts

14. Acceptance Criteria#

A meditation library is considered correctly implemented when all eight of the following conditions are true. These criteria are designed to catch stub implementations — code that compiles but does not actually do what the specification requires.

  1. Every package builds with its configured Nx executor and tsc --noEmit passes.
  2. The primary class of each engine package extends eventemitter3 and emits the documented typed events.
  3. Built-in data is exactly as documented: 10 breathing patterns with the timings in §6.2, 12 timer presets in §5.6, 6 bell constants / 7 ambient constants / 3 ambient mixes in §5.2–§5.3, 26 achievements in §8.3, 26 milestones in §8.4.
  4. Playback rate stays inside the 0.85×–1.25× quality-preserving policy and normalizePlaybackRate throws PlaybackRateRangeError outside it.
  5. Storage backends are interchangeable through the per-package storage interface; switching backend does not change behaviour.
  6. React hook factories work without React installed until setReactHooks is called; calling a convenience hook before registration throws a clear error.
  7. Platform factories return a working Web implementation in the browser and a Noop implementation elsewhere; no platform-specific code runs server-side.
  8. Per-package Vitest suites pass.