# Meditation Libraries — Technical Specifications

> 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 }`.

`ContentMetadata` — `title`, 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 }`.

`AudioTrack` — `id: TrackId`, optional `contentId`, `title`, `durationSeconds`,
`url: UrlString`, `mimeType: string`, optional `sizeBytes`, optional
`waveform: number[]`, `updatedAt: Timestamp`.

`MeditationSession` — `id`, `type: SessionType`, `state: SessionState`,
`startedAt: Timestamp`, optional `endedAt`, optional `targetDurationSeconds`,
`elapsedSeconds`, optional `contentId`, optional `trackId`, optional `metadata`.

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

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

`InterruptionRecord` — `interruptedAt`, 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`                                      |

`AudioSource` — `url`, `format: AudioFormat`, `quality: AudioQuality`, optional
`bitrate`, `size`, `cached`, `cacheExpiry`.

`ContentMarker` — `id`, `time` (seconds), `type` (`chapter` / `cue` /
`instruction` / `transition` / `custom`), `label`, optional `data`.

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

`MeditationTrack` — `id`, `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`.

`PlayerError` — `code: PlayerErrorCode`, `message`, optional `cause`, optional
`trackId`, `timestamp`, `recoverable: boolean`.

`PlaybackProgress` — `position`, `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` |

`AudioLayer` — `id`, `type`, `volume`, `muted`, `pan` (−1 to 1), `solo`,
optional `track`.

`MixerState` — `masterVolume`, `masterMuted`,
`layers: Record<AudioLayerType, AudioLayer>`.

`CrossfadeConfig` — `duration` (ms), `easing` (`linear` / `ease-in` / `ease-out`
/ `ease-in-out`), `overlap: boolean`.

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

`AutomationPoint` — `time` (ms), `volume`, optional `easing`. `VolumeAutomation`
— `layerType`, `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.

`QueueItem` — `queueId`, `track`, `addedAt`, `source` (`user` / `auto` /
`recommendation` / `program`).

`QueueState` — `items`, `currentIndex` (−1 when nothing playing),
`originalOrder: string[]`, `history: QueueItem[]`, `historySize`.

`PlaybackQueueConfig` — `maxHistorySize`, `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.

`VisualizationData` — `frequencyData: 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.

`FrequencyBand` — `index`, `startFrequency`, `endFrequency`, `amplitude`,
`peak`. `BeatData` — `isBeat`, `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).

`Platform` — `web`, `ios`, `android`, `electron`, `unknown`. `AudioBackend` —
`web-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.

`RemoteCommand` — `play`, `pause`, `stop`, `togglePlayPause`, `nextTrack`,
`previousTrack`, `seekForward`, `seekBackward`, `seek`.

`AudioSessionCategory` — `ambient`, `soloAmbient`, `playback`, `record`,
`playAndRecord`, `multiRoute`. `AudioInterruptionType` — `began`, `ended`.
`AudioInterruptionReason` — `default`, `appWasSuspended`, `builtInMicMuted`,
`routeChange`, `phoneCall`, `alarm`, `other`. `AudioRoute.type` — `speaker`,
`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.

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

`AdaptiveStreamingConfig` — `enableAdaptive`, `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.

`BufferState` — `empty`, `filling`, `sufficient`, `full`, `stalled`.
`NetworkQuality` — `offline`, `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`.

`BellTrigger` — `start`, `interval`, `warning`, `end`, `preparation-end`.

`BellSound` — `type: BellType`, optional `customUrl`, `volume`, `repeatCount`,
`repeatDelay` (ms), `fadeOutDuration` (ms).

`BellConfiguration` — `startBell`, `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.

`AmbientSoundCategory` — `nature`, `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.

`AmbientSound` — `type`, `category`, optional `customUrl`, `volume`, `loop`.
`AmbientLayer` — `id`, `sound`, `currentVolume`, `active`, `muted`. `AmbientMix`
— `id`, `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).

`IntervalActionType` — `bell`, `vibration`, `voice-prompt`, `ambient-change`,
`custom`. `IntervalAction` carries the type plus the matching payload field.
`TimerInterval` — `id`, `timeOffset` (s), `action`, `triggered`, optional
`label`. `RecurringInterval` — `every` (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.

`PreparationConfig` — `enabled`, `duration` (s), `voiceCountdown`.
`WindDownConfig` — `enabled`, `duration` (s), `fadeAmbient`, `reminderBell`.

`TimerConfig` — `duration` (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`.

`HapticConfig` — `enabled`, `intensity` (`light` / `medium` / `heavy`),
`onStart`, `onInterval`, `onComplete`, `breathingPattern`. `HapticType` —
`impact`, `notification`, `selection`. `HapticNotificationType` — `success`,
`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`. `PresetCategory` —
`quick`, `standard`, `extended`, `pomodoro`, `sleep`, `custom`.

`TimerPreset` — `id`, `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.

`TimerStatistics` — `totalSessions`, `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`                                |

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

`BreathingPattern` — `id`, `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.

`ExerciseConfig` — `pattern`, `completionMode`, `targetCycles`,
`targetDurationMinutes`, `audioGuidance`, `hapticFeedback`, `countdownSeconds`,
`transitionSounds`, `voiceType` (`none` / `bell` / `voice-male` /
`voice-female`), `volume` (0–1). `DEFAULT_EXERCISE_CONFIG` —
`completionMode: 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.

`VisualizationState` — `phase`, `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`.

`VisualizationConfig` — `minScale`, `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.

`HapticIntensity` — `light`, `medium`, `heavy`. `BreathingHapticConfig` —
`enabled`, `intensity`, `onPhaseTransition`, `inhalePattern`, `exhalePattern`,
`onCycleComplete`, `onExerciseComplete`, `pacing: VibrationPacingConfig`.
`VibrationPacingConfig` — `enabled`, `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.

`AudioCueType` — `inhale`, `hold`, `exhale`, `release`, `cycle-complete`,
`exercise-complete`, `countdown`, `bell`, `custom`. `AudioCue` — `type`, `url`,
`volume`, `delay`. `AudioGuidanceConfig` — `enabled`, `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.

`SessionRecord` — `id`, `patternId`, `patternName`, `startedAt`, `endedAt`,
`durationSeconds`, `completedCycles`, `completed`, `completionMode`, `target`,
optional `notes`, `rating`. `HistoryStatistics` — `totalSessions`,
`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`.

`SessionAudioConfig` — `primarySource`, `backgroundSource`, `guidanceSource`,
`primaryVolume`, `backgroundVolume`, `enableDucking`, `fadeDurationMs`.

`SessionTimingConfig` — `targetDurationSeconds`, `preparationSeconds`,
`completionSeconds`, `progressIntervalMs`, `allowOvertime`,
`maxOvertimeSeconds`.

`SessionNotificationConfig` — `startBell`, `endBell`, `intervalBells`,
`bellIntervalSeconds`, `hapticFeedback`.

`SessionConfig` — `type`, 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.

`PersistenceConfig` — `autoSave`, `autoSaveIntervalMs`, `saveOnPause`,
`saveOnBackground`, `keepHistory`, `maxHistoryItems`.
`DEFAULT_PERSISTENCE_CONFIG` auto-saves every 5000ms, keeps 100 history items.

`AnalyticsConfig` — `enabled`, optional `tracker`, `includeDeviceInfo`,
`sampleRate` (0–1), optional `customProperties`.

`SchedulerConfig` — `storage`, `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()`.

`SessionProgress` — `elapsedSeconds`, `remainingSeconds`, `progressPercentage`,
`isOvertime`, `overtimeSeconds`, `currentPhase` (`preparation` / `main` /
`completion` / `overtime`).

`SessionInterruption` — `timestamp`, `reason`, `durationMs`, `resumed`.
`SessionPause` — `pausedAt`, `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`.

`SessionResult` — `session`, `completionStatus`, `completionPercentage`,
`activeDurationSeconds`, `totalPauseSeconds`, `pauseCount`, `interruptionCount`,
`countsTowardStreak`, optional `achievements` / `milestones`.

`ScheduledSession` — `id`, `sessionConfig`, `title`, optional `description`,
`enabled`, `time: TimeString`, `recurrence`, optional `daysOfWeek` /
`dayOfMonth`, `startDate`, optional `endDate`, `notification`, `createdAt`,
`updatedAt`, optional `lastTriggeredAt` / `nextScheduledAt` / `metadata`.
`ScheduleNotification` — `enabled`, `minutesBefore`, `title`, `body`, optional
`sound` / `vibrate` / `persistent`. `UpcomingSession` — `schedule`,
`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`.

`StreakData` — `currentStreak`, `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:

`StreakConfig` — `minSessionSeconds`, `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

`ExportDataTypes` — `sessions`, `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.

`SyncState` — `status`, `lastSyncAt`, `pendingChanges`, `lastError`, `isOnline`.
`SyncConfig` — `endpoint`, `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.

`DownloadableContent` — `id`, `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`.

`DownloadError` — `type`, `message`, optional `statusCode`, `retryable`,
`timestamp`, `retryCount`. `DownloadProgress` — `downloadId`, `contentId`,
`status`, `downloadedBytes`, `totalBytes`, `progressPercentage`,
`speedBytesPerSecond`, `estimatedSecondsRemaining`, `startedAt`, optional
`error`.

### 9.3 Configuration

`QueueConfig` — `maxConcurrent`, `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`.

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

`CleanupConfig` — `strategy`, `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.

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

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

`SuggestionConfig` — `maxSuggestions`, `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.

`ScoringWeights` — `DEFAULT_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`. `DownloadSuggestion`
— `content`, `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.

`AnalyticsClientOptions` — `anonymousId`, `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-player` — `BackgroundAudioHandler` / `Web…` / `Noop…` /
  `createBackgroundAudioHandler`; `AudioSessionManager` / `Web…` / `Noop…` /
  `createAudioSessionManager`.
- `meditation-timer` — `TimerBackgroundHandler` / `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.
