@dofek/scoring 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/dist/breathwork.d.ts +27 -0
  4. package/dist/breathwork.d.ts.map +1 -0
  5. package/dist/breathwork.js +65 -0
  6. package/dist/breathwork.js.map +1 -0
  7. package/dist/colors.d.ts +74 -0
  8. package/dist/colors.d.ts.map +1 -0
  9. package/dist/colors.js +74 -0
  10. package/dist/colors.js.map +1 -0
  11. package/dist/healthspan-years.d.ts +22 -0
  12. package/dist/healthspan-years.d.ts.map +1 -0
  13. package/dist/healthspan-years.js +33 -0
  14. package/dist/healthspan-years.js.map +1 -0
  15. package/dist/loading-policy.d.ts +8 -0
  16. package/dist/loading-policy.d.ts.map +1 -0
  17. package/dist/loading-policy.js +9 -0
  18. package/dist/loading-policy.js.map +1 -0
  19. package/dist/menstrual-cycle.d.ts +24 -0
  20. package/dist/menstrual-cycle.d.ts.map +1 -0
  21. package/dist/menstrual-cycle.js +32 -0
  22. package/dist/menstrual-cycle.js.map +1 -0
  23. package/dist/query-cache.d.ts +2 -0
  24. package/dist/query-cache.d.ts.map +1 -0
  25. package/dist/query-cache.js +2 -0
  26. package/dist/query-cache.js.map +1 -0
  27. package/dist/scoring.d.ts +114 -0
  28. package/dist/scoring.d.ts.map +1 -0
  29. package/dist/scoring.js +325 -0
  30. package/dist/scoring.js.map +1 -0
  31. package/dist/sleep-performance.d.ts +36 -0
  32. package/dist/sleep-performance.d.ts.map +1 -0
  33. package/dist/sleep-performance.js +59 -0
  34. package/dist/sleep-performance.js.map +1 -0
  35. package/dist/strain-target.d.ts +18 -0
  36. package/dist/strain-target.d.ts.map +1 -0
  37. package/dist/strain-target.js +59 -0
  38. package/dist/strain-target.js.map +1 -0
  39. package/dist/tokens.d.ts +99 -0
  40. package/dist/tokens.d.ts.map +1 -0
  41. package/dist/tokens.js +106 -0
  42. package/dist/tokens.js.map +1 -0
  43. package/package.json +83 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Asher Cohen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @dofek/scoring
2
+
3
+ Platform-agnostic TypeScript models for health scores, sleep and readiness
4
+ presentation, breathwork sessions, and shared design tokens. The package is used
5
+ by both web and mobile clients but has no UI-framework dependency.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npm install @dofek/scoring
11
+ ```
12
+
13
+ Requires Node.js 22.14 or newer.
14
+
15
+ ## Usage
16
+
17
+ There is no root export; import the subpath for the model you need.
18
+
19
+ ```ts
20
+ import {
21
+ StrainScore,
22
+ zScoreToRecoveryScore,
23
+ } from "@dofek/scoring/scoring";
24
+
25
+ const strain = StrainScore.fromRawLoad(120);
26
+
27
+ console.log({
28
+ strain: strain.value,
29
+ strainLabel: strain.label,
30
+ recovery: zScoreToRecoveryScore(0.75),
31
+ });
32
+ ```
33
+
34
+ ## Public API
35
+
36
+ | Subpath | Purpose |
37
+ | --- | --- |
38
+ | `@dofek/scoring/scoring` | Strain, recovery, workload-ratio, trend, score-zone, and presentation helpers |
39
+ | `@dofek/scoring/colors` | Semantic, chart, surface, text, sleep-stage, and activity colors |
40
+ | `@dofek/scoring/tokens` | Framework-neutral typography, spacing, radius, animation, and chart tokens |
41
+ | `@dofek/scoring/strain-target` | Daily strain-target calculation and result types |
42
+ | `@dofek/scoring/sleep-performance` | Sleep-performance components, tiers, and recommended-bedtime calculation |
43
+ | `@dofek/scoring/healthspan-years` | Score-to-years mapping and formatting |
44
+ | `@dofek/scoring/menstrual-cycle` | Cycle-phase estimation and display metadata |
45
+ | `@dofek/scoring/breathwork` | Built-in breathing techniques and session-duration helpers |
46
+ | `@dofek/scoring/loading-policy` | Blocking-loading state policy |
47
+ | `@dofek/scoring/query-cache` | Shared query-cache age constant |
48
+
49
+ ## Model behavior
50
+
51
+ - `StrainScore.fromRawLoad` applies logarithmic scaling, maps non-positive loads
52
+ to zero, and caps the result at 21.
53
+ - `zScoreToRecoveryScore` uses a Dofek-defined asymmetric sigmoid where a
54
+ z-score of zero maps to 62.
55
+ - Strain targets use Dofek-defined readiness bands and cap the target when the
56
+ acute-to-chronic workload ratio exceeds 1.3.
57
+ - Sleep performance defaults to 70% sleep sufficiency and 30% efficiency. When
58
+ consistency or low-stress inputs are supplied, it averages all available
59
+ components equally.
60
+ - Healthspan display deltas map scores from 0–100 onto +3 to -2 years.
61
+ - Cycle phases estimate ovulation as `cycleLength - 14`; this is a display
62
+ estimate, not a clinical assessment.
63
+ - The design-token modules contain values only; they do not install fonts or
64
+ render UI.
65
+
66
+ The package performs no network requests and needs no authentication or runtime
67
+ configuration. Validate untrusted input before calling the calculation
68
+ functions. These models are for fitness software and are not medical advice.
69
+
70
+ ## License and contributing
71
+
72
+ [MIT](./LICENSE). Source is in the
73
+ [Dofek repository](https://github.com/Asherlc/dofek/tree/main/packages/scoring).
74
+ Please [open an issue](https://github.com/Asherlc/dofek/issues) for bugs or API
75
+ proposals, or [submit a pull request](https://github.com/Asherlc/dofek/pulls).
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Breathwork technique definitions and session helpers.
3
+ *
4
+ * Each technique defines the breathing pattern (inhale/hold/exhale/hold durations)
5
+ * and default number of rounds.
6
+ */
7
+ export interface BreathworkTechnique {
8
+ id: string;
9
+ name: string;
10
+ description: string;
11
+ /** Inhale duration in seconds */
12
+ inhaleSeconds: number;
13
+ /** Hold after inhale in seconds (optional) */
14
+ holdInSeconds?: number;
15
+ /** Exhale duration in seconds */
16
+ exhaleSeconds: number;
17
+ /** Hold after exhale in seconds (optional) */
18
+ holdOutSeconds?: number;
19
+ /** Default number of rounds */
20
+ defaultRounds: number;
21
+ }
22
+ export declare const TECHNIQUES: BreathworkTechnique[];
23
+ /** Look up a technique by its ID */
24
+ export declare function getTechniqueById(id: string): BreathworkTechnique | undefined;
25
+ /** Compute total session duration in seconds for a given number of rounds */
26
+ export declare function totalSessionSeconds(technique: BreathworkTechnique, rounds: number): number;
27
+ //# sourceMappingURL=breathwork.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breathwork.d.ts","sourceRoot":"","sources":["../src/breathwork.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,UAAU,EAAE,mBAAmB,EAkD3C,CAAC;AAEF,oCAAoC;AACpC,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAE5E;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO1F"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Breathwork technique definitions and session helpers.
3
+ *
4
+ * Each technique defines the breathing pattern (inhale/hold/exhale/hold durations)
5
+ * and default number of rounds.
6
+ */
7
+ export const TECHNIQUES = [
8
+ {
9
+ id: "box-breathing",
10
+ name: "Box Breathing",
11
+ description: "Equal parts inhale, hold, exhale, hold. Used by Navy SEALs to reduce stress and improve focus.",
12
+ inhaleSeconds: 4,
13
+ holdInSeconds: 4,
14
+ exhaleSeconds: 4,
15
+ holdOutSeconds: 4,
16
+ defaultRounds: 4,
17
+ },
18
+ {
19
+ id: "4-7-8",
20
+ name: "4-7-8 Breathing",
21
+ description: "Relaxing breath technique. Inhale 4s, hold 7s, exhale 8s. Promotes deep relaxation and sleep.",
22
+ inhaleSeconds: 4,
23
+ holdInSeconds: 7,
24
+ exhaleSeconds: 8,
25
+ defaultRounds: 4,
26
+ },
27
+ {
28
+ id: "coherent",
29
+ name: "Coherent Breathing",
30
+ description: "Slow, rhythmic breathing at 5 breaths per minute. Balances the autonomic nervous system and optimizes HRV.",
31
+ inhaleSeconds: 6,
32
+ exhaleSeconds: 6,
33
+ defaultRounds: 10,
34
+ },
35
+ {
36
+ id: "physiological-sigh",
37
+ name: "Physiological Sigh",
38
+ description: "Double inhale through the nose followed by a long exhale. Rapidly reduces stress and anxiety.",
39
+ inhaleSeconds: 3,
40
+ holdInSeconds: 1,
41
+ exhaleSeconds: 6,
42
+ defaultRounds: 5,
43
+ },
44
+ {
45
+ id: "wim-hof",
46
+ name: "Wim Hof Method",
47
+ description: "Power breathing with deep inhales and passive exhales. Increases energy, focus, and cold tolerance.",
48
+ inhaleSeconds: 2,
49
+ exhaleSeconds: 2,
50
+ defaultRounds: 30,
51
+ },
52
+ ];
53
+ /** Look up a technique by its ID */
54
+ export function getTechniqueById(id) {
55
+ return TECHNIQUES.find((t) => t.id === id);
56
+ }
57
+ /** Compute total session duration in seconds for a given number of rounds */
58
+ export function totalSessionSeconds(technique, rounds) {
59
+ const roundSeconds = technique.inhaleSeconds +
60
+ (technique.holdInSeconds ?? 0) +
61
+ technique.exhaleSeconds +
62
+ (technique.holdOutSeconds ?? 0);
63
+ return roundSeconds * rounds;
64
+ }
65
+ //# sourceMappingURL=breathwork.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breathwork.js","sourceRoot":"","sources":["../src/breathwork.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkBH,MAAM,CAAC,MAAM,UAAU,GAA0B;IAC/C;QACE,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,gGAAgG;QAClG,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,cAAc,EAAE,CAAC;QACjB,aAAa,EAAE,CAAC;KACjB;IACD;QACE,EAAE,EAAE,OAAO;QACX,IAAI,EAAE,iBAAiB;QACvB,WAAW,EACT,+FAA+F;QACjG,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;KACjB;IACD;QACE,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EACT,4GAA4G;QAC9G,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,EAAE;KAClB;IACD;QACE,EAAE,EAAE,oBAAoB;QACxB,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EACT,+FAA+F;QACjG,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;KACjB;IACD;QACE,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,gBAAgB;QACtB,WAAW,EACT,qGAAqG;QACvG,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,EAAE;KAClB;CACF,CAAC;AAEF,oCAAoC;AACpC,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,SAA8B,EAAE,MAAc;IAChF,MAAM,YAAY,GAChB,SAAS,CAAC,aAAa;QACvB,CAAC,SAAS,CAAC,aAAa,IAAI,CAAC,CAAC;QAC9B,SAAS,CAAC,aAAa;QACvB,CAAC,SAAS,CAAC,cAAc,IAAI,CAAC,CAAC,CAAC;IAClC,OAAO,YAAY,GAAG,MAAM,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Semantic color palette shared across web and iOS.
3
+ *
4
+ * Web components use these in ECharts config and inline styles.
5
+ * iOS components use these directly in React Native StyleSheets.
6
+ * Tailwind utility classes remain platform-specific (web only).
7
+ */
8
+ /** Status colors for scores, alerts, and thresholds.
9
+ * Darkened one shade vs originals for contrast on light sage background. */
10
+ export declare const statusColors: {
11
+ /** Positive / good / green — scores ≥67, optimal zones */
12
+ readonly positive: "#16a34a";
13
+ /** Warning / moderate / yellow — scores 34-66, caution zones */
14
+ readonly warning: "#ca8a04";
15
+ /** Danger / poor / red — scores <34, injury risk */
16
+ readonly danger: "#dc2626";
17
+ /** Informational / neutral / blue */
18
+ readonly info: "#2563eb";
19
+ /** Orange — elevated warnings (e.g. high ramp rate) */
20
+ readonly elevated: "#ea580c";
21
+ };
22
+ /** Chart series colors for consistent multi-series plots.
23
+ * Adjusted for contrast on light (#eef3ed) background. */
24
+ export declare const chartColors: {
25
+ readonly teal: "#0ea5e9";
26
+ readonly purple: "#5E35B1";
27
+ readonly blue: "#2563eb";
28
+ readonly green: "#16a34a";
29
+ readonly orange: "#ea580c";
30
+ readonly emerald: "#059669";
31
+ readonly pink: "#db2777";
32
+ readonly amber: "#d97706";
33
+ };
34
+ /** Light sage/mint theme surface colors */
35
+ export declare const surfaceColors: {
36
+ readonly background: "#eef3ed";
37
+ readonly surface: "#f5f9f5";
38
+ readonly surfaceSecondary: "#e8ede7";
39
+ readonly accent: "#2d7a56";
40
+ };
41
+ /** Text colors for sage/mint light theme */
42
+ export declare const textColors: {
43
+ readonly primary: "#1a2e1a";
44
+ readonly secondary: "#4a6a4a";
45
+ readonly tertiary: "#6b8a6b";
46
+ /** Neutral gray — used for stable/unchanged trends */
47
+ readonly neutral: "#8aaa8a";
48
+ };
49
+ /** Sleep stage colors — consistent across web (ECharts) and mobile (SVG).
50
+ * Uses the Material-inspired palette for maximum stage distinction. */
51
+ export declare const sleepStageColors: {
52
+ readonly deep: "#5E35B1";
53
+ readonly rem: "#42A5F5";
54
+ readonly light: "#78909C";
55
+ readonly awake: "#FF8A65";
56
+ };
57
+ /** Activity metric chart series colors — universal associations. */
58
+ export declare const activityMetricColors: {
59
+ readonly heartRate: "#dc2626";
60
+ readonly power: "#d97706";
61
+ readonly speed: "#2563eb";
62
+ readonly cadence: "#5E35B1";
63
+ };
64
+ /** Chart chrome colors for ECharts tooltips, axes, and grid lines */
65
+ export declare const chartThemeColors: {
66
+ readonly gridLine: "rgba(74, 158, 122, 0.12)";
67
+ readonly axisLine: "rgba(74, 158, 122, 0.25)";
68
+ readonly axisLabel: "#6b8a6b";
69
+ readonly tooltipBackground: "#ffffff";
70
+ readonly tooltipBorder: "rgba(74, 158, 122, 0.2)";
71
+ readonly tooltipText: "#1a2e1a";
72
+ readonly legendText: "#4a6a4a";
73
+ };
74
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;6EAC6E;AAC7E,eAAO,MAAM,YAAY;IACvB,0DAA0D;;IAE1D,gEAAgE;;IAEhE,oDAAoD;;IAEpD,qCAAqC;;IAErC,uDAAuD;;CAE/C,CAAC;AAEX;2DAC2D;AAC3D,eAAO,MAAM,WAAW;;;;;;;;;CASd,CAAC;AAEX,2CAA2C;AAC3C,eAAO,MAAM,aAAa;;;;;CAKhB,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,UAAU;;;;IAIrB,sDAAsD;;CAE9C,CAAC;AAEX;wEACwE;AACxE,eAAO,MAAM,gBAAgB;;;;;CAKnB,CAAC;AAEX,oEAAoE;AACpE,eAAO,MAAM,oBAAoB;;;;;CAKvB,CAAC;AAEX,qEAAqE;AACrE,eAAO,MAAM,gBAAgB;;;;;;;;CAQnB,CAAC"}
package/dist/colors.js ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Semantic color palette shared across web and iOS.
3
+ *
4
+ * Web components use these in ECharts config and inline styles.
5
+ * iOS components use these directly in React Native StyleSheets.
6
+ * Tailwind utility classes remain platform-specific (web only).
7
+ */
8
+ /** Status colors for scores, alerts, and thresholds.
9
+ * Darkened one shade vs originals for contrast on light sage background. */
10
+ export const statusColors = {
11
+ /** Positive / good / green — scores ≥67, optimal zones */
12
+ positive: "#16a34a",
13
+ /** Warning / moderate / yellow — scores 34-66, caution zones */
14
+ warning: "#ca8a04",
15
+ /** Danger / poor / red — scores <34, injury risk */
16
+ danger: "#dc2626",
17
+ /** Informational / neutral / blue */
18
+ info: "#2563eb",
19
+ /** Orange — elevated warnings (e.g. high ramp rate) */
20
+ elevated: "#ea580c",
21
+ };
22
+ /** Chart series colors for consistent multi-series plots.
23
+ * Adjusted for contrast on light (#eef3ed) background. */
24
+ export const chartColors = {
25
+ teal: "#0ea5e9",
26
+ purple: "#5E35B1",
27
+ blue: "#2563eb",
28
+ green: "#16a34a",
29
+ orange: "#ea580c",
30
+ emerald: "#059669",
31
+ pink: "#db2777",
32
+ amber: "#d97706",
33
+ };
34
+ /** Light sage/mint theme surface colors */
35
+ export const surfaceColors = {
36
+ background: "#eef3ed",
37
+ surface: "#f5f9f5",
38
+ surfaceSecondary: "#e8ede7",
39
+ accent: "#2d7a56",
40
+ };
41
+ /** Text colors for sage/mint light theme */
42
+ export const textColors = {
43
+ primary: "#1a2e1a",
44
+ secondary: "#4a6a4a",
45
+ tertiary: "#6b8a6b",
46
+ /** Neutral gray — used for stable/unchanged trends */
47
+ neutral: "#8aaa8a",
48
+ };
49
+ /** Sleep stage colors — consistent across web (ECharts) and mobile (SVG).
50
+ * Uses the Material-inspired palette for maximum stage distinction. */
51
+ export const sleepStageColors = {
52
+ deep: "#5E35B1",
53
+ rem: "#42A5F5",
54
+ light: "#78909C",
55
+ awake: "#FF8A65",
56
+ };
57
+ /** Activity metric chart series colors — universal associations. */
58
+ export const activityMetricColors = {
59
+ heartRate: "#dc2626",
60
+ power: "#d97706",
61
+ speed: "#2563eb",
62
+ cadence: "#5E35B1",
63
+ };
64
+ /** Chart chrome colors for ECharts tooltips, axes, and grid lines */
65
+ export const chartThemeColors = {
66
+ gridLine: "rgba(74, 158, 122, 0.12)",
67
+ axisLine: "rgba(74, 158, 122, 0.25)",
68
+ axisLabel: "#6b8a6b",
69
+ tooltipBackground: "#ffffff",
70
+ tooltipBorder: "rgba(74, 158, 122, 0.2)",
71
+ tooltipText: "#1a2e1a",
72
+ legendText: "#4a6a4a",
73
+ };
74
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;6EAC6E;AAC7E,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,0DAA0D;IAC1D,QAAQ,EAAE,SAAS;IACnB,gEAAgE;IAChE,OAAO,EAAE,SAAS;IAClB,oDAAoD;IACpD,MAAM,EAAE,SAAS;IACjB,qCAAqC;IACrC,IAAI,EAAE,SAAS;IACf,uDAAuD;IACvD,QAAQ,EAAE,SAAS;CACX,CAAC;AAEX;2DAC2D;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;CACR,CAAC;AAEX,2CAA2C;AAC3C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,UAAU,EAAE,SAAS;IACrB,OAAO,EAAE,SAAS;IAClB,gBAAgB,EAAE,SAAS;IAC3B,MAAM,EAAE,SAAS;CACT,CAAC;AAEX,4CAA4C;AAC5C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,SAAS;IACpB,QAAQ,EAAE,SAAS;IACnB,sDAAsD;IACtD,OAAO,EAAE,SAAS;CACV,CAAC;AAEX;wEACwE;AACxE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,SAAS;IACf,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,SAAS;CACR,CAAC;AAEX,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;CACV,CAAC;AAEX,qEAAqE;AACrE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,0BAA0B;IACpC,QAAQ,EAAE,0BAA0B;IACpC,SAAS,EAAE,SAAS;IACpB,iBAAiB,EAAE,SAAS;IAC5B,aAAa,EAAE,yBAAyB;IACxC,WAAW,EAAE,SAAS;IACtB,UAAU,EAAE,SAAS;CACb,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Healthspan Years — maps a healthspan score (0-100) to a biological age delta.
3
+ *
4
+ * Score 0 → +3 years (aging faster than chronological age)
5
+ * Score 50 → 0 years (aging at expected rate)
6
+ * Score 100 → -2 years (aging slower than chronological age)
7
+ *
8
+ * The scale is intentionally asymmetric: poor health has a bigger penalty
9
+ * than good health has a benefit, reflecting longevity research showing
10
+ * inactivity/poor sleep shortens life more than peak fitness extends it.
11
+ */
12
+ /**
13
+ * Convert a healthspan score (0-100) to a years-of-life delta.
14
+ * Positive = aging faster (losing years). Negative = aging slower (gaining years).
15
+ */
16
+ export declare function scoreToYearsDelta(score: number): number;
17
+ /**
18
+ * Format a years delta for display.
19
+ * Examples: "+1.5 yr", "-2.0 yr", "+0.0 yr"
20
+ */
21
+ export declare function formatYearsDelta(years: number): string;
22
+ //# sourceMappingURL=healthspan-years.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthspan-years.d.ts","sourceRoot":"","sources":["../src/healthspan-years.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Healthspan Years — maps a healthspan score (0-100) to a biological age delta.
3
+ *
4
+ * Score 0 → +3 years (aging faster than chronological age)
5
+ * Score 50 → 0 years (aging at expected rate)
6
+ * Score 100 → -2 years (aging slower than chronological age)
7
+ *
8
+ * The scale is intentionally asymmetric: poor health has a bigger penalty
9
+ * than good health has a benefit, reflecting longevity research showing
10
+ * inactivity/poor sleep shortens life more than peak fitness extends it.
11
+ */
12
+ /**
13
+ * Convert a healthspan score (0-100) to a years-of-life delta.
14
+ * Positive = aging faster (losing years). Negative = aging slower (gaining years).
15
+ */
16
+ export function scoreToYearsDelta(score) {
17
+ const clamped = Math.max(0, Math.min(100, score));
18
+ if (clamped <= 50) {
19
+ // 0→+3, 50→0 (linear interpolation)
20
+ return Math.round((3 - (clamped / 50) * 3) * 10) / 10;
21
+ }
22
+ // 50→0, 100→-2 (linear interpolation)
23
+ return Math.round((-(clamped - 50) / 50) * 2 * 10) / 10;
24
+ }
25
+ /**
26
+ * Format a years delta for display.
27
+ * Examples: "+1.5 yr", "-2.0 yr", "+0.0 yr"
28
+ */
29
+ export function formatYearsDelta(years) {
30
+ const sign = years >= 0 ? "+" : "";
31
+ return `${sign}${years.toFixed(1)} yr`;
32
+ }
33
+ //# sourceMappingURL=healthspan-years.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthspan-years.js","sourceRoot":"","sources":["../src/healthspan-years.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IAElD,IAAI,OAAO,IAAI,EAAE,EAAE,CAAC;QAClB,oCAAoC;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IACxD,CAAC;IACD,sCAAsC;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACnC,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACzC,CAAC"}
@@ -0,0 +1,8 @@
1
+ interface LoadingPolicyInput {
2
+ data: unknown;
3
+ isLoading: boolean;
4
+ isFetching?: boolean;
5
+ }
6
+ export declare function shouldShowBlockingLoading({ data, isLoading, isFetching, }: LoadingPolicyInput): boolean;
7
+ export {};
8
+ //# sourceMappingURL=loading-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading-policy.d.ts","sourceRoot":"","sources":["../src/loading-policy.ts"],"names":[],"mappings":"AAAA,UAAU,kBAAkB;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAOD,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EACJ,SAAS,EACT,UAAkB,GACnB,EAAE,kBAAkB,GAAG,OAAO,CAE9B"}
@@ -0,0 +1,9 @@
1
+ function hasExistingData(data) {
2
+ if (Array.isArray(data))
3
+ return data.length > 0;
4
+ return data != null;
5
+ }
6
+ export function shouldShowBlockingLoading({ data, isLoading, isFetching = false, }) {
7
+ return (isLoading || isFetching) && !hasExistingData(data);
8
+ }
9
+ //# sourceMappingURL=loading-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading-policy.js","sourceRoot":"","sources":["../src/loading-policy.ts"],"names":[],"mappings":"AAMA,SAAS,eAAe,CAAC,IAAa;IACpC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAChD,OAAO,IAAI,IAAI,IAAI,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,EACxC,IAAI,EACJ,SAAS,EACT,UAAU,GAAG,KAAK,GACC;IACnB,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Menstrual cycle phase computation.
3
+ *
4
+ * Four phases based on day within cycle:
5
+ * 1. Menstrual (days 1-5): period bleeding
6
+ * 2. Follicular (days 6 to ovulation-1): estrogen rises
7
+ * 3. Ovulatory (around ovulation ±1 day): peak fertility
8
+ * 4. Luteal (post-ovulation to end): progesterone dominant
9
+ *
10
+ * Ovulation is estimated at cycleLength - 14 (luteal phase is ~14 days).
11
+ */
12
+ export type CyclePhase = "menstrual" | "follicular" | "ovulatory" | "luteal";
13
+ /**
14
+ * Compute the cycle phase for a given day within a cycle.
15
+ * @param dayOfCycle 1-based day number within the cycle
16
+ * @param cycleLength Total cycle length in days (typically 21-35)
17
+ */
18
+ export declare function computePhase(dayOfCycle: number, cycleLength: number): CyclePhase;
19
+ /** Display name and color for each phase */
20
+ export declare const PHASE_DISPLAY: Record<CyclePhase, {
21
+ label: string;
22
+ color: string;
23
+ }>;
24
+ //# sourceMappingURL=menstrual-cycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menstrual-cycle.d.ts","sourceRoot":"","sources":["../src/menstrual-cycle.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE7E;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAqBhF;AAED,4CAA4C;AAC5C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAK9E,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { chartColors, statusColors } from "./colors.js";
2
+ /**
3
+ * Compute the cycle phase for a given day within a cycle.
4
+ * @param dayOfCycle 1-based day number within the cycle
5
+ * @param cycleLength Total cycle length in days (typically 21-35)
6
+ */
7
+ export function computePhase(dayOfCycle, cycleLength) {
8
+ // Menstrual phase: first 5 days
9
+ if (dayOfCycle <= 5) {
10
+ return "menstrual";
11
+ }
12
+ // Estimated ovulation day (luteal phase is roughly constant at 14 days)
13
+ const ovulationDay = Math.max(6, cycleLength - 14);
14
+ // Ovulatory window: ovulation day ± 1
15
+ if (dayOfCycle >= ovulationDay - 1 && dayOfCycle <= ovulationDay + 1) {
16
+ return "ovulatory";
17
+ }
18
+ // Follicular: between menstrual and ovulatory
19
+ if (dayOfCycle < ovulationDay - 1) {
20
+ return "follicular";
21
+ }
22
+ // Luteal: after ovulatory window
23
+ return "luteal";
24
+ }
25
+ /** Display name and color for each phase */
26
+ export const PHASE_DISPLAY = {
27
+ menstrual: { label: "Menstrual", color: statusColors.danger },
28
+ follicular: { label: "Follicular", color: statusColors.info },
29
+ ovulatory: { label: "Ovulatory", color: chartColors.purple },
30
+ luteal: { label: "Luteal", color: chartColors.amber },
31
+ };
32
+ //# sourceMappingURL=menstrual-cycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menstrual-cycle.js","sourceRoot":"","sources":["../src/menstrual-cycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgBxD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,WAAmB;IAClE,gCAAgC;IAChC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,wEAAwE;IACxE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,EAAE,CAAC,CAAC;IAEnD,sCAAsC;IACtC,IAAI,UAAU,IAAI,YAAY,GAAG,CAAC,IAAI,UAAU,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACrE,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,8CAA8C;IAC9C,IAAI,UAAU,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,iCAAiC;IACjC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAyD;IACjF,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE;IAC7D,UAAU,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE;IAC7D,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE;IAC5D,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE;CACtD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const QUERY_CACHE_MAX_AGE_MS: number;
2
+ //# sourceMappingURL=query-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-cache.d.ts","sourceRoot":"","sources":["../src/query-cache.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,QAAsB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const QUERY_CACHE_MAX_AGE_MS = 1000 * 60 * 60 * 12;
2
+ //# sourceMappingURL=query-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-cache.js","sourceRoot":"","sources":["../src/query-cache.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Whoop-like 0–21 strain score with display classification.
3
+ *
4
+ * Construct directly with a strain value or use `fromRawLoad()` to convert
5
+ * raw daily training load (duration_min × avg_hr/max_hr) using logarithmic
6
+ * scaling that produces diminishing returns at higher loads.
7
+ */
8
+ export declare class StrainScore {
9
+ #private;
10
+ readonly value: number;
11
+ constructor(value: number);
12
+ get color(): string;
13
+ get label(): string;
14
+ get description(): string;
15
+ static fromRawLoad(rawLoad: number): StrainScore;
16
+ static fromPhysiologicalLoad(physiologicalLoad: number): StrainScore;
17
+ static fromAcuteLoad(acuteLoad: number): StrainScore;
18
+ }
19
+ /** Get the color for a recovery/readiness score (0-100) */
20
+ export declare const SCORE_WARNING_THRESHOLD = 50;
21
+ export declare const SCORE_RECOVERED_THRESHOLD = 70;
22
+ export interface ScoreZone {
23
+ /** Lower bound (inclusive) */
24
+ min: number;
25
+ /** Upper bound (inclusive for charting) */
26
+ max: number;
27
+ status: "danger" | "warning" | "positive";
28
+ }
29
+ /** Visual score zones used by charts and cards to keep threshold bands consistent. */
30
+ export declare const SCORE_ZONES: ScoreZone[];
31
+ export declare function scoreColor(score: number): string;
32
+ /** Get a human-readable label for a recovery score (0-100) */
33
+ export declare function scoreLabel(score: number): string;
34
+ /** Get a contextual description for a recovery score explaining what it means */
35
+ export declare function scoreDescription(score: number): string;
36
+ /** Strain zone classification with display properties. */
37
+ export declare class StrainZone {
38
+ readonly zone: string;
39
+ constructor(zone: string);
40
+ get color(): string;
41
+ get label(): string;
42
+ }
43
+ /** Workload ratio (ACWR) with display classification. */
44
+ export declare class WorkloadRatio {
45
+ readonly value: number | null;
46
+ constructor(value: number | null);
47
+ get color(): string;
48
+ get hint(): string | null;
49
+ }
50
+ export interface WeekSummary {
51
+ week: string;
52
+ hours: number;
53
+ fraction: number;
54
+ }
55
+ /** Aggregate weekly volume rows into a summary with relative fractions */
56
+ export declare function aggregateWeeklyVolume(rows: Array<{
57
+ week: string;
58
+ hours: number;
59
+ }>): WeekSummary[];
60
+ /** Determine trend direction from two values */
61
+ export declare function trendDirection(current: number, previous: number): "up" | "down" | "stable";
62
+ /** Stress score (0-3 Whoop-like scale) with display classification. */
63
+ export declare class StressScore {
64
+ readonly value: number;
65
+ constructor(value: number);
66
+ get color(): string;
67
+ get label(): string;
68
+ }
69
+ /** Get the color for a trend direction */
70
+ export declare function trendColor(trend: "improving" | "worsening" | "stable" | "declining"): string;
71
+ /** Get the color for a categorical readiness level (high/moderate/low) */
72
+ export declare function readinessLevelColor(level: "high" | "moderate" | "low" | "unknown"): string;
73
+ /** Get the color for a health metric status (excellent/good/fair/poor) */
74
+ export declare function healthStatusColor(status: "excellent" | "good" | "fair" | "poor"): string;
75
+ /** Get the color for a ramp rate value (always uses absolute value) */
76
+ export declare function rampRateColor(rate: number): string;
77
+ /**
78
+ * Map a z-score to a 0-100 recovery score using an asymmetric sigmoid.
79
+ * Tuned to match Whoop's recovery scoring:
80
+ * z=0 (at baseline mean) → 62 (average day feels "recovered")
81
+ * z=+1 → ~80, z=-1 → ~40
82
+ * z=+2 → ~93, z=-2 → ~18
83
+ * Uses separate scales for positive/negative z to handle the asymmetric center.
84
+ */
85
+ export declare function zScoreToRecoveryScore(zScore: number): number;
86
+ /** Get the color for a sleep performance tier */
87
+ export declare function sleepTierColor(tier: "Excellent" | "Good" | "Fair" | "Poor"): string;
88
+ /** Get a contextual description for a sleep performance tier */
89
+ export declare function sleepTierDescription(tier: "Excellent" | "Good" | "Fair" | "Poor"): string;
90
+ /** Get the color for sleep debt in minutes */
91
+ export declare function sleepDebtColor(minutes: number): string;
92
+ /** Get the color for a sleep performance percentage (actual vs target) */
93
+ export declare function sleepPerformanceColor(percentage: number): string;
94
+ /** Form zone boundaries (intervals.icu defaults) */
95
+ export declare const FORM_ZONE_TRANSITION = 25;
96
+ export declare const FORM_ZONE_FRESH = 5;
97
+ export declare const FORM_ZONE_GREY = -10;
98
+ export declare const FORM_ZONE_OPTIMAL = -30;
99
+ /** Colors for form (training stress balance) zones */
100
+ export declare const FORM_ZONE_COLORS: {
101
+ readonly transition: "#60a5fa";
102
+ readonly fresh: "#16a34a";
103
+ readonly grey: "#a1a1aa";
104
+ readonly optimal: "#16a34a";
105
+ readonly highRisk: "#dc2626";
106
+ };
107
+ /** Form zone (training stress balance) classification with display properties. */
108
+ export declare class FormZone {
109
+ readonly value: number;
110
+ constructor(value: number);
111
+ get color(): string;
112
+ get label(): string;
113
+ }
114
+ //# sourceMappingURL=scoring.d.ts.map