{"_id":"@asleep-ai/sleep-stats","_rev":"5-40066d146a594366dd52a772c697d45d","name":"@asleep-ai/sleep-stats","dist-tags":{"latest":"1.0.1"},"versions":{"0.1.0":{"name":"@asleep-ai/sleep-stats","version":"0.1.0","keywords":["sleep","statistics","hypnogram","sleep-tracking","sleep-analysis","waso","sleep-stages"],"author":{"name":"Asleep"},"license":"MIT","_id":"@asleep-ai/sleep-stats@0.1.0","maintainers":[{"name":"keenranger","email":"kghnkl0103@gmail.com"},{"name":"qube_asleep","email":"qube@asleep.ai"}],"homepage":"https://github.com/asleep-ai/sleep-stats-ts#readme","bugs":{"url":"https://github.com/asleep-ai/sleep-stats-ts/issues"},"dist":{"shasum":"fd2299c454195f3ca31b5f473066d08680046673","tarball":"https://registry.npmjs.org/@asleep-ai/sleep-stats/-/sleep-stats-0.1.0.tgz","fileCount":11,"integrity":"sha512-ENEOleP06Ihu3r57/fdrK4+LwIly6UXfrsa+uQ4cevw/4dGcn3m5ylbP2NbifraM83FqLZGaUrVcII+cMWhevw==","signatures":[{"sig":"MEQCIExRd0QRsh8QG6pEqyPH7gtmvM1n9O31ytmEMl+zpr6dAiA6b43evLCz8bWha4FqPN68CHtRMTIjYJPLMCi6ApKm9Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":88986},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"522ff55478d0a30554abecf569b7a7797eda9d21","scripts":{"dev":"tsup src/index.ts --format cjs,esm --dts --watch","lint":"eslint src --ext .ts","test":"jest","build":"tsup src/index.ts --format cjs,esm --dts","typecheck":"tsc --noEmit","test:watch":"jest --watch"},"_npmUser":{"name":"keenranger","email":"kghnkl0103@gmail.com"},"repository":{"url":"git+https://github.com/asleep-ai/sleep-stats-ts.git","type":"git"},"_npmVersion":"10.9.2","description":"Calculate sleep statistics from hypnogram arrays (30-second sleep stage slots)","directories":{},"_nodeVersion":"22.15.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","jest":"^29.5.0","tsup":"^8.5.0","eslint":"^8.0.0","ts-jest":"^29.1.0","typescript":"^5.0.0","@types/jest":"^29.5.0","@types/node":"^20.0.0","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sleep-stats_0.1.0_1763014513015_0.26171816592684793","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@asleep-ai/sleep-stats","version":"1.0.1","keywords":["sleep","statistics","hypnogram","sleep-tracking","sleep-analysis","waso","sleep-stages"],"author":{"name":"Asleep"},"license":"MIT","_id":"@asleep-ai/sleep-stats@1.0.1","maintainers":[{"name":"keenranger","email":"kghnkl0103@gmail.com"},{"name":"qube_asleep","email":"qube@asleep.ai"}],"homepage":"https://github.com/asleep-ai/sleep-stats-ts#readme","bugs":{"url":"https://github.com/asleep-ai/sleep-stats-ts/issues"},"dist":{"shasum":"4c22678cfd809b164eedb66a4a9d76889cb38937","tarball":"https://registry.npmjs.org/@asleep-ai/sleep-stats/-/sleep-stats-1.0.1.tgz","fileCount":12,"integrity":"sha512-F6qpKNUaA3u+AjGlnFIrUdF23a2Xx0wLm1wd5Yr6GUP84DrR3SIGvuyGjSLzkgcnuPCR0t4HIMir3r6uq2WVBA==","signatures":[{"sig":"MEQCIDupH4IRc4clygyEQXB+TyYFTHDPvzpbXehT4cCuSCNbAiAnfWXtH2t9LCwqeoXBLiDfRVLf8+Vh4Pkd/2FknTB1nw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@asleep-ai%2fsleep-stats@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":90806},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"94c5443f9745992d4e152d5d2763049a5e2f7ae3","scripts":{"dev":"tsup src/index.ts --format cjs,esm --dts --watch","lint":"eslint src --ext .ts","test":"jest","build":"tsup src/index.ts --format cjs,esm --dts","typecheck":"tsc --noEmit","test:watch":"jest --watch"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:01c1df87-5f07-463f-a986-ebb9c24d6f19"}},"repository":{"url":"git+https://github.com/asleep-ai/sleep-stats-ts.git","type":"git"},"_npmVersion":"11.6.2","description":"Calculate sleep statistics from hypnogram arrays (30-second sleep stage slots)","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","jest":"^29.5.0","tsup":"^8.5.0","eslint":"^8.0.0","ts-jest":"^29.1.0","typescript":"^5.0.0","@types/jest":"^29.5.0","@types/node":"^20.0.0","semantic-release":"^25.0.2","@semantic-release/git":"^10.0.1","@semantic-release/npm":"^13.1.2","@typescript-eslint/parser":"^6.0.0","@semantic-release/changelog":"^6.0.3","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sleep-stats_1.0.1_1763367056932_0.10509873648703327","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2025-11-13T06:15:12.915Z","modified":"2026-02-19T07:01:09.201Z","0.1.0":"2025-11-13T06:15:13.192Z","1.0.0":"2025-11-17T07:14:50.637Z","1.0.1":"2025-11-17T08:10:57.130Z"},"bugs":{"url":"https://github.com/asleep-ai/sleep-stats-ts/issues"},"author":{"name":"Asleep"},"license":"MIT","homepage":"https://github.com/asleep-ai/sleep-stats-ts#readme","keywords":["sleep","statistics","hypnogram","sleep-tracking","sleep-analysis","waso","sleep-stages"],"repository":{"url":"git+https://github.com/asleep-ai/sleep-stats-ts.git","type":"git"},"description":"Calculate sleep statistics from hypnogram arrays (30-second sleep stage slots)","maintainers":[{"email":"kghnkl0103@gmail.com","name":"keenranger"}],"readme":"# Sleep Statistics Calculator\n\nA standalone TypeScript library for calculating comprehensive sleep statistics from hypnogram arrays (30-second sleep stage time slots).\n\n## What is a Hypnogram?\n\nA **hypnogram** (also called sleep stages array) is an array where each element represents a 30-second time slot of sleep tracking data:\n\n- **Index**: Time offset (index 0 = 0s, index 1 = 30s, index 2 = 60s, etc.)\n- **Value**: Sleep stage during that slot\n  - `0` = Wake\n  - `1` = Light Sleep\n  - `2` = Deep Sleep\n  - `3` = REM Sleep\n  - `-1` = No Data\n\n## Features\n\n- Zero runtime dependencies\n- Pure TypeScript with full type definitions\n- Comprehensive sleep metrics calculation\n- Sleep cycle detection from REM clusters\n- Wakeup latency calculation\n- Key moments tracking\n- Support for custom slot durations\n- Well-tested with realistic sleep data\n\n## Installation\n\n```bash\nnpm install @asleep-ai/sleep-stats\n# or\nyarn add @asleep-ai/sleep-stats\n```\n\n## Usage\n\n### Basic Usage\n\n```typescript\nimport { calculateSleepStatistics } from '@asleep-ai/sleep-stats';\n\n// Example: 8-hour sleep session with 30-second slots\nconst sleepStages = [\n  ...Array(60).fill(0),   // 30min awake (sleep latency)\n  ...Array(240).fill(1),  // 2h light sleep\n  ...Array(120).fill(2),  // 1h deep sleep\n  ...Array(120).fill(3),  // 1h REM sleep\n  ...Array(240).fill(1),  // 2h light sleep\n  ...Array(60).fill(0),   // 30min wake (WASO)\n  ...Array(120).fill(1),  // 1h light sleep\n];\n\nconst stats = calculateSleepStatistics(sleepStages);\n\nconsole.log(stats);\n// {\n//   timeInBed: 28800,           // 8 hours in seconds\n//   timeInSleep: 23400,         // 6.5 hours in seconds\n//   timeInWake: 3600,           // 1.5 hours in seconds\n//   timeInLight: 16200,         // 4.5 hours\n//   timeInDeep: 3600,           // 1 hour\n//   timeInRem: 3600,            // 1 hour\n//   sleepEfficiency: 0.8125,    // 81.25%\n//   sleepLatency: 1800,         // 30 minutes to fall asleep\n//   wakeupLatency: 0,           // Time from last sleep to end\n//   latencies: {\n//     sleep: 1800,\n//     light: 1800,\n//     deep: 5400,\n//     rem: 9000\n//   },\n//   ratios: {\n//     wake: 0.125,\n//     light: 0.5625,\n//     deep: 0.125,\n//     rem: 0.125,\n//     sleep: 0.8125\n//   },\n//   waso: {\n//     waso: 1800,              // 30min wake after sleep onset\n//     wasoCount: 1,            // 1 wake episode\n//     longestWaso: 1800        // Longest wake episode: 30min\n//   },\n//   timeInSleepPeriod: 25200,  // timeInSleep + waso\n//   stageBreakdown: {          // Same as timeIn* values\n//     wake: 3600,\n//     light: 16200,\n//     deep: 3600,\n//     rem: 3600\n//   },\n//   sleepCycleCount: 1,        // Number of detected sleep cycles\n//   averageSleepCycle: 9000    // Average cycle duration (2.5h)\n// }\n```\n\n### Advanced Usage\n\n#### Custom Slot Duration\n\n```typescript\nimport { calculateSleepStatistics } from '@asleep-ai/sleep-stats';\n\n// Use 60-second slots instead of 30-second\nconst sleepStages = [0, 1, 2, 3];\nconst stats = calculateSleepStatistics(sleepStages, { slotDuration: 60 });\n\nconsole.log(stats.timeInBed); // 240 seconds (4 slots * 60s)\n```\n\n#### Individual Calculations\n\n```typescript\nimport {\n  calculateStageBreakdown,\n  calculateSleepLatency,\n  calculateWaso,\n  calculateStageRatios,\n  calculateWakeupLatency,\n  calculateKeyMoments,\n  calculateSleepCycles,\n} from '@asleep-ai/sleep-stats';\n\nconst sleepStages = [0, 0, 1, 1, 2, 3];\n\n// Calculate just the stage breakdown\nconst breakdown = calculateStageBreakdown(sleepStages);\n// { wake: 60, light: 60, deep: 30, rem: 30 }\n\n// Calculate just sleep latency\nconst latency = calculateSleepLatency(sleepStages);\n// 60 (2 slots until sleep onset)\n\n// Calculate wakeup latency\nconst wakeupLatency = calculateWakeupLatency(sleepStages);\n// 0 (no wake time after last sleep stage)\n\n// Calculate WASO\nconst waso = calculateWaso(sleepStages);\n// { waso: 0, wasoCount: 0, longestWaso: 0 }\n\n// Calculate key moments\nconst moments = calculateKeyMoments(sleepStages);\n// { firstSleepIdx: 2, lastSleepIdx: 5, ... }\n\n// Calculate sleep cycles\nconst cycles = calculateSleepCycles(sleepStages);\n// { cycleCount: 0, averageCycle: null }\n\n// Calculate ratios\nconst timeInBed = sleepStages.length * 30;\nconst ratios = calculateStageRatios(breakdown, timeInBed);\n// { wake: 0.33, light: 0.33, deep: 0.17, rem: 0.17, sleep: 0.67 }\n```\n\n## API Reference\n\n### Types\n\n```typescript\nenum SleepStage {\n  WAKE = 0,\n  LIGHT = 1,\n  DEEP = 2,\n  REM = 3,\n  NO_DATA = -1,\n}\n\ninterface StageBreakdown {\n  wake: number;   // seconds\n  light: number;  // seconds\n  deep: number;   // seconds\n  rem: number;    // seconds\n}\n\ninterface StageLatencies {\n  sleep: number;  // Time until first non-wake stage\n  light: number;  // Time until first light sleep\n  deep: number;   // Time until first deep sleep\n  rem: number;    // Time until first REM sleep\n}\n\ninterface StageRatios {\n  wake: number;   // 0-1\n  light: number;  // 0-1\n  deep: number;   // 0-1\n  rem: number;    // 0-1\n  sleep: number;  // Combined sleep ratio (0-1)\n}\n\ninterface WasoStatistics {\n  waso: number;         // Total wake time after sleep onset (seconds)\n  wasoCount: number;    // Number of wake episodes\n  longestWaso: number;  // Longest wake episode (seconds)\n}\n\ninterface SleepMoments {\n  firstSleepIdx: number;  // Index of first non-wake stage\n  lastSleepIdx: number;   // Index of last non-wake stage\n  firstLightIdx: number;  // Index of first light sleep (-1 if never reached)\n  firstDeepIdx: number;   // Index of first deep sleep (-1 if never reached)\n  firstRemIdx: number;    // Index of first REM sleep (-1 if never reached)\n  wakeCount: number;      // Count of wake stages between first and last sleep\n  lightCount: number;     // Count of light sleep stages\n  deepCount: number;      // Count of deep sleep stages\n  remCount: number;       // Count of REM sleep stages\n}\n\ninterface SleepCycleInfo {\n  cycleCount: number;        // Number of complete sleep cycles\n  averageCycle: number | null;  // Average cycle duration in seconds (null if no cycles)\n}\n\ninterface SleepStatistics {\n  timeInBed: number;\n  timeInSleep: number;\n  timeInWake: number;\n  timeInDeep: number;\n  timeInLight: number;\n  timeInRem: number;\n  sleepEfficiency: number;\n  sleepLatency: number;\n  wakeupLatency: number;\n  latencies: StageLatencies;\n  ratios: StageRatios;\n  waso: WasoStatistics;\n  timeInSleepPeriod: number;\n  stageBreakdown: StageBreakdown;\n  sleepCycleCount: number;\n  averageSleepCycle: number | null;\n}\n```\n\n### Functions\n\n#### `calculateSleepStatistics(sleepStages, options?)`\n\nCalculate complete sleep statistics from a hypnogram array.\n\n**Parameters:**\n- `sleepStages: number[]` - Array of sleep stage values\n- `options?: CalculationOptions` - Optional configuration\n  - `slotDuration?: number` - Duration of each slot in seconds (default: 30)\n\n**Returns:** `SleepStatistics`\n\n#### `calculateStageBreakdown(sleepStages, slotDuration?)`\n\nCalculate time spent in each sleep stage.\n\n**Parameters:**\n- `sleepStages: number[]` - Array of sleep stage values\n- `slotDuration?: number` - Duration of each slot in seconds (default: 30)\n\n**Returns:** `StageBreakdown`\n\n#### `calculateSleepLatency(sleepStages, slotDuration?)`\n\nCalculate sleep latency (time until first non-wake stage).\n\n**Returns:** `number` (seconds)\n\n#### `calculateStageLatencies(sleepStages, slotDuration?)`\n\nCalculate latencies to reach each sleep stage.\n\n**Returns:** `StageLatencies`\n\n#### `calculateWaso(sleepStages, slotDuration?)`\n\nCalculate Wake After Sleep Onset (WASO) statistics.\n\n**Returns:** `WasoStatistics`\n\n#### `calculateKeyMoments(sleepStages, slotDuration?)`\n\nIdentify critical indices and stage counts for sleep analysis.\n\n**Parameters:**\n- `sleepStages: number[]` - Array of sleep stage values\n\n**Returns:** `SleepMoments`\n\n#### `calculateWakeupLatency(sleepStages, slotDuration?)`\n\nCalculate time from last sleep stage to end of recording.\n\n**Parameters:**\n- `sleepStages: number[]` - Array of sleep stage values\n- `slotDuration?: number` - Duration of each slot in seconds (default: 30)\n\n**Returns:** `number` (seconds)\n\n#### `calculateRemClusters(sleepStages)`\n\nDetect continuous or nearby REM periods that form clusters. A REM cluster is a sequence of REM periods separated by no more than THRESHOLD_REM_CLUSTER_DISTANCE epochs (20 epochs = 10 minutes). Only clusters with at least THRESHOLD_REM_COUNT REM epochs are considered valid.\n\n**Parameters:**\n- `sleepStages: number[]` - Array of sleep stage values\n\n**Returns:** `number[][]` - Array of [startIdx, endIdx] pairs for each valid REM cluster\n\n#### `calculateSleepCycles(sleepStages, slotDuration?)`\n\nCalculate sleep cycle information from REM clusters. Each REM cluster represents one complete sleep cycle.\n\n**Parameters:**\n- `sleepStages: number[]` - Array of sleep stage values\n- `slotDuration?: number` - Duration of each slot in seconds (default: 30)\n\n**Returns:** `SleepCycleInfo`\n\n#### `calculateStageRatios(breakdown, timeInBed)`\n\nCalculate ratios of time spent in each stage.\n\n**Parameters:**\n- `breakdown: StageBreakdown` - Stage breakdown in seconds\n- `timeInBed: number` - Total tracking duration in seconds\n\n**Returns:** `StageRatios`\n\n## Constants\n\n```typescript\nimport {\n  SLOT_DURATION_SECONDS,\n  SLEEP_STAGE,\n  SECONDS_IN_ONE_HOUR,\n  THRESHOLD_REM_CLUSTER_DISTANCE,\n  THRESHOLD_REM_COUNT\n} from '@asleep-ai/sleep-stats';\n\nconsole.log(SLOT_DURATION_SECONDS); // 30\nconsole.log(SECONDS_IN_ONE_HOUR); // 3600\n\nconsole.log(SLEEP_STAGE);\n// { WAKE: 0, LIGHT: 1, DEEP: 2, REM: 3, NO_DATA: -1 }\n\nconsole.log(THRESHOLD_REM_CLUSTER_DISTANCE); // 20 epochs (10 minutes)\nconsole.log(THRESHOLD_REM_COUNT); // 20 minimum REM epochs for valid cluster\n```\n\n## Key Metrics Explained\n\n### Sleep Efficiency\nPercentage of time spent asleep while in bed.\n```\nsleepEfficiency = timeInSleep / timeInBed\n```\n\n### Sleep Latency\nTime taken to fall asleep (first non-wake stage).\n\n### Wakeup Latency\nTime from the last sleep stage to the end of the recording. This represents the final wake period before the recording ended.\n\n### WASO (Wake After Sleep Onset)\nTotal time spent awake after initially falling asleep. Does not include wake time before sleep onset (sleep latency) or after final wake.\n\n### Stage Latencies\nTime taken to reach each sleep stage:\n- **Sleep Latency**: Time to any sleep stage\n- **Light Latency**: Time to first light sleep\n- **Deep Latency**: Time to first deep sleep\n- **REM Latency**: Time to first REM sleep\n\n### Sleep Cycles\nNumber of REM clusters detected in the sleep session. Each REM cluster represents a complete sleep cycle. Sleep cycles typically last 90-120 minutes and progress through stages: Light → Deep → REM. The library detects these cycles by identifying REM clusters (groups of REM periods separated by no more than 10 minutes).\n\n### Time in Sleep Period\nTotal time from sleep onset to final wake, including both sleep and wake periods.\n```\ntimeInSleepPeriod = timeInSleep + waso\n```\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Run tests\nnpm test\n\n# Watch mode\nnpm run dev\n```\n\n## Publishing\n\nThis package uses [semantic-release](https://github.com/semantic-release/semantic-release) for automated publishing. Releases are triggered by commit messages following [Conventional Commits](https://www.conventionalcommits.org/):\n\n- `fix: description` → Patch release (0.1.0 → 0.1.1)\n- `feat: description` → Minor release (0.1.0 → 0.2.0)\n- `feat!: description` or `BREAKING CHANGE:` → Major release (0.1.0 → 1.0.0)\n\nReleases happen automatically when changes are pushed to the main branch.\n\n## License\n\nMIT\n\n## Contributing\n\nContributions are welcome. Please open an issue or submit a pull request.","readmeFilename":"README.md"}