diabetic-utils
Version:
Zero-bloat TypeScript utilities for diabetes data: glucose, A1C, conversions, time-in-range, and more.
3 lines • 26.3 kB
JavaScript
;function ot(e){return `${e.toFixed(1)}%`}function T(e){return typeof e=="number"&&Number.isFinite(e)&&e>0&&e<20}function it(e,t){let n=t?.normalMax??5.7,o=t?.prediabetesMax??6.5;return T(e)?e<=n?"normal":e<=o?"prediabetes":"diabetes":"invalid"}function at(e,t=[6.5,7],n){let o=n?.min??t[0],i=n?.max??t[1];return T(e)&&e>=o&&e<=i}function lt(e,t){if(!T(e)||!T(t))throw new Error("Invalid A1C value");return +(e-t).toFixed(2)}function st(e){if(!Array.isArray(e)||e.length<2)return "insufficient data";let t=e[e.length-1]-e[0];return Math.abs(t)<.1?"stable":t>0?"increasing":"decreasing"}var J=405,x={VERY_SENSITIVE:1,NORMAL:2,EARLY_RESISTANCE:2.9},X=70,Z=180,K=3.9,ee=10,_=28.7,E=46.7,v={MMOL_L_SLOPE:1.57,MMOL_L_INTERCEPT:3.5,MG_DL_SLOPE:.03,MG_DL_INTERCEPT:2.4,A1C_SLOPE:.02392,A1C_INTERCEPT:3.31},f=18.0182,m="mg/dL",h="mmol/L",Ge="#D32F2F",ve="#388E3C",Te="#4CAF50",xe="#2E7D32",_e="#FBC02D",Ee="#F57C00",ct={LOW:Ge,NORMAL:ve,ELEVATED:_e,HIGH:Ee,NORMAL_UP:Te,NORMAL_DOWN:xe},mt={STEADY:"\u2192",RISING:"\u2197",FALLING:"\u2198",RAPIDRISE:"\u2191",RAPIDFALL:"\u2193"},te=54,ne=70,re=180,oe=250,dt=3,pt=3.9,gt=10,ft=13.9,ie=63,ae=140,le=3.5,se=7.8,O=70,A=4,ue=1,I=25,ce=5,me=50,de=1,pe=.5;function ge(e){if(typeof e!="object"||e===null)return false;let t=e;return typeof t.value=="number"&&typeof t.unit=="string"}function fe(e){return typeof e!="string"?false:/^\d+(\.\d+)?\s+(mg\/dL|mmol\/L)$/i.test(e.trim())}function Oe(e,t=m,n){let o=n?.mgdl??X,i=n?.mmoll??K;return t===m?e<o:e<i}function Ae(e,t=m,n){let o=n?.mgdl??Z,i=n?.mmoll??ee;return t===m?e>o:e>i}function yt(e,t=m,n){return Oe(e,t,n?.hypo)?"low":Ae(e,t,n?.hyper)?"high":"normal"}function Re(e){if(!fe(e))throw new Error('Invalid glucose string format. Use "100 mg/dL" or "5.5 mmol/L".');let n=e.trim().replace(/\s+/g," ").match(/^([\d.]+) (mg\/dL|mmol\/L)$/i),[,o,i]=n;return {value:parseFloat(o),unit:i.toLowerCase()==="mg/dl"?m:h}}function S(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0&&(t===m||t===h)}function C(e){return typeof e=="number"&&Number.isFinite(e)&&e>0&&e<1e3}function Ie(e,t){if(!S(e,m))throw new Error("Invalid fasting glucose value (must be a positive number in mg/dL)");if(!C(t))throw new Error("Invalid fasting insulin value (must be a positive number in \xB5IU/mL)");let n=e*t/405;return {value:n,interpretation:Ne(n)}}function Ne(e){return e<x.VERY_SENSITIVE?"Very insulin sensitive":e>=x.VERY_SENSITIVE&&e<x.NORMAL?"Normal insulin sensitivity":e>=x.NORMAL&&e<x.EARLY_RESISTANCE?"Early insulin resistance":"Significant insulin resistance"}function Et(e,t,n){if(!T(e))throw new Error("Invalid A1C value (must be a positive number < 20%)");if(!S(t,m))throw new Error("Invalid fasting glucose value (must be a positive number in mg/dL)");if(!C(n))throw new Error("Invalid fasting insulin value (must be a positive number in \xB5IU/mL)");let o=_*e-E,i=Ie(t,n),r=[];return t>o+20&&r.push("Fasting glucose is higher than estimated average glucose from A1C."),n<2&&t>110&&r.push("Low insulin with high glucose may indicate reduced insulin secretion."),{estimatedAverageGlucose:o,homaIR:i,flags:r,recommendation:r.length?"Some inconsistencies were detected in your markers. This may occur for various reasons including diet, stress, or lab variability.":"Your markers appear consistent based on this informational analysis.",disclaimer:"This tool is for informational and educational purposes only. It does not constitute medical advice, diagnosis, or treatment."}}function Dt(e){return +((e+E)/_).toFixed(2)}function wt(e){return Math.round(e*_-E)}function St(e){if(e<0)throw new Error("A1C must be positive");let t=Number((e*_-E).toFixed(10));return Math.round(t)}function Ct(e,t=m){return +(((t===h?e*f:e)+46.7)/28.7).toFixed(2)}function Pt(e){return +(v.A1C_INTERCEPT+v.A1C_SLOPE*e).toFixed(2)}function Ht(e,t){let n,o;if(ge(e))n=e.value,o=e.unit;else if(typeof e=="string"){let r=Re(e);n=r.value,o=r.unit;}else {if(!t)throw new Error("Unit is required when input is a number.");n=e,o=t;}if(![m,h].includes(o))throw new Error(`Unsupported glucose unit: ${o}`);if(n<=0||!Number.isFinite(n))throw new Error("Glucose value must be a positive number.");let i=o===h?v.MMOL_L_SLOPE*n+v.MMOL_L_INTERCEPT:v.MG_DL_SLOPE*n+v.MG_DL_INTERCEPT;return parseFloat(i.toFixed(1))}function Ft(e){if(!Number.isFinite(e)||e<=0)throw new Error("Invalid glucose value");return +(e/f).toFixed(1)}function Ut(e){if(!Number.isFinite(e)||e<=0)throw new Error("Invalid glucose value");return Math.round(e*f)}function Vt({value:e,unit:t}){if(!Number.isFinite(e)||e<=0)throw new Error("Invalid glucose value");if(![m,h].includes(t))throw new Error("Invalid unit");return t===m?{value:Math.round(e/f*10)/10,unit:h}:{value:Math.round(e*f),unit:m}}function kt(e,t,n={}){let o=n.digits??0,i=n.suffix??true,r=e.toFixed(o);return i?`${r} ${t}`:r}function Yt(e,t=1){return `${e.toFixed(t)}%`}function Bt(e,t){if(isNaN(Date.parse(e)))throw new RangeError("Invalid ISO timestamp");return new Date(e).toLocaleString("en-US",{timeZone:t,year:"numeric",month:"short",day:"2-digit",hour:"2-digit",minute:"2-digit"})}function Wt(e,t){if(e.length===0)return {inRange:0,belowRange:0,aboveRange:0};let n=0,o=0,i=0;for(let l of e)l.value<t.min?o++:l.value>t.max?i++:n++;let r=e.length;return {inRange:+(n/r*100).toFixed(1),belowRange:+(o/r*100).toFixed(1),aboveRange:+(i/r*100).toFixed(1)}}function jt(e){return `In Range: ${e.inRange}%, Below: ${e.belowRange}%, Above: ${e.aboveRange}%`}function Qt(e){return e.reduce((t,n)=>{let o=n.timestamp.split("T")[0];return t[o]=t[o]||[],t[o].push(n),t},{})}function qt(e,t,n){return e.length===0?0:e.filter(i=>i>=t&&i<=n).length/e.length*100}var Zt=[m,h];function P(e,t={}){if(!Array.isArray(e)||e.length<2)return NaN;let n=e.filter(a=>typeof a=="number"&&!isNaN(a)&&isFinite(a)&&a!==null&&a!==void 0);if(n.length<3)return NaN;let o=n.reduce((a,c)=>a+c,0)/n.length,i=n.reduce((a,c)=>a+Math.pow(c-o,2),0)/(n.length-1),r=Math.sqrt(i);if(r===0||!isFinite(r))return NaN;let{shortWindow:l=Math.max(3,Math.min(5,Math.floor(n.length/8))),longWindow:s=Math.max(l+2,Math.min(32,Math.floor(n.length/3))),direction:u="auto"}=t;if(n.length<10||s>=n.length-2)return D(n,r);try{let a=De(n,l,s),c=we(n,a);if(c.length<3)return D(n,r);let d=Se(c,r);return d.length===0?D(n,r):Ce(d,u)}catch{return D(n,r)}}function De(e,t,n){let o=he(e,t),i=he(e,n),r=[0];for(let l=1;l<o.length;l++){let s=o[l-1],u=o[l],a=i[l-1],c=i[l];(s<=a&&u>c||s>=a&&u<c)&&r.push(l);}return r.push(e.length-1),r}function he(e,t){let n=[],o=Math.floor(t/2);for(let i=0;i<e.length;i++){let r=Math.max(0,i-o),l=Math.min(e.length-1,i+o),s=0,u=0;for(let a=r;a<=l;a++)s+=e[a],u++;n.push(s/u);}return n}function we(e,t){let n=[];for(let o=0;o<t.length-1;o++){let i=t[o],r=t[o+1],l=i,s=e[i],u=true;if(n.length>0)u=n[n.length-1].type==="nadir";else {let a=e[i],c=e[i],d=i,g=i;for(let p=i+1;p<=r;p++)e[p]>a&&(a=e[p],d=p),e[p]<c&&(c=e[p],g=p);let b=Math.abs(a-e[i]),R=Math.abs(c-e[i]);b>=R?(l=d,s=a,u=true):(l=g,s=c,u=false);}if(u)for(let a=i;a<=r;a++)e[a]>s&&(s=e[a],l=a);else for(let a=i;a<=r;a++)e[a]<s&&(s=e[a],l=a);n.push({index:l,value:s,type:u?"peak":"nadir"});}return n}function Se(e,t){let n=[];for(let o=0;o<e.length-2;o++){let i=e[o],r=e[o+1],l=e[o+2],s=Math.abs(r.value-i.value),u=Math.abs(l.value-r.value);s>t&&u>t&&n.push({leftAmplitude:s,rightAmplitude:u,direction:r.type==="peak"?"ascending":"descending",indices:[i.index,r.index,l.index]});}return n}function Ce(e,t){let n;t==="auto"?n=e[0].direction:n=t;let o=e.filter(r=>r.direction===n);if(o.length===0)return NaN;let i=o.map(r=>n==="ascending"?r.leftAmplitude:r.rightAmplitude);return i.reduce((r,l)=>r+l,0)/i.length}function D(e,t){if(e.length<3)return NaN;let n=[],o=Math.max(1,Math.min(3,Math.floor(e.length/10)));for(let r=o;r<e.length-o;r++){let l=e[r],s=true,u=true;for(let a=r-o;a<=r+o;a++)a!==r&&(e[a]>=l&&(s=false),e[a]<=l&&(u=false));s?n.push({index:r,value:l,type:"peak"}):u&&n.push({index:r,value:l,type:"nadir"});}if(n.length===0)for(let r=1;r<e.length-1;r++)e[r]>e[r-1]&&e[r]>e[r+1]?n.push({index:r,value:e[r],type:"peak"}):e[r]<e[r-1]&&e[r]<e[r+1]&&n.push({index:r,value:e[r],type:"nadir"});if(n.length<2)return NaN;let i=[];for(let r=0;r<n.length-1;r++){let l=n[r],s=n[r+1],u=Math.abs(s.value-l.value);u>t&&i.push(u);}for(let r=0;r<n.length-2;r++){let l=n[r],s=n[r+1],u=n[r+2];if(l.type==="peak"&&s.type==="nadir"&&u.type==="peak"||l.type==="nadir"&&s.type==="peak"&&u.type==="nadir"){let a=Math.abs(s.value-l.value),c=Math.abs(u.value-s.value);if(a>t&&c>t){let d=(a+c)/2;i.push(d);}}}return i.length===0?NaN:i.reduce((r,l)=>r+l,0)/i.length}function w(e){if(!Array.isArray(e)||e.length<2)return NaN;let t=e.reduce((o,i)=>o+i,0)/e.length,n=e.reduce((o,i)=>o+Math.pow(i-t,2),0)/(e.length-1);return Math.sqrt(n)}function H(e){if(!Array.isArray(e)||e.length<2)return NaN;let t=e.reduce((o,i)=>o+i,0)/e.length;return t===0?NaN:w(e)/t*100}function Pe(e,t){if(!Array.isArray(e)||e.length===0)return {};let n=[...e].sort((i,r)=>i-r),o={};for(let i of t){if(typeof i!="number"||i<0||i>100)continue;let r=Math.ceil(i/100*n.length);o[i]=n[Math.max(0,r-1)];}return o}function He(e,t){return P(e,t)}var F=.01;function U(e,t){if(e.length===0)throw new Error("Cannot calculate Enhanced TIR: readings array is empty");let n=t?.population??"standard",o=t?.veryLowThreshold??te,i=t?.lowThreshold??ne,r=t?.highThreshold??re,l=t?.veryHighThreshold??oe,s=e.map(p=>{let y=p.unit===m?p.value:p.value*f;return {...p,normalizedValue:y}});be(s,600);let u=L(s,0,o),a=L(s,o,i),c=L(s,i,r,true),d=L(s,r+F,l,true),g=L(s,l+F,1/0,true),b=ye(e),R=Ve({veryLow:u,low:a,inRange:c,high:d,veryHigh:g},n);return {veryLow:u,low:a,inRange:c,high:d,veryHigh:g,meetsTargets:R,summary:b}}function Fe(e,t){if(e.length===0)throw new Error("Cannot calculate Pregnancy TIR: readings array is empty");let n=t?.unit,o=e.filter(p=>p.unit===m).length,i=n?n===m:o>=e.length/2,r=i?ie:le,l=i?ae:se,s=e.map(p=>{let y;return i?y=p.unit===m?p.value:p.value*f:y=p.unit===m?p.value/f:p.value,{...p,normalizedValue:y}});be(s,i?600:33.3);let a=L(s,0,r),c=L(s,r,l,true),d=L(s,l+F,1/0,true),g=ye(e),b=c.percentage>=O&&a.percentage<A&&d.percentage<I,R=ke({belowRange:a,inRange:c,aboveRange:d});return {belowRange:a,inRange:c,aboveRange:d,meetsPregnancyTargets:b,recommendations:R,summary:g}}function be(e,t){for(let n of e)if(n.normalizedValue<0||n.normalizedValue>t||!Number.isFinite(n.normalizedValue)){let o=t===600?" mg/dL":"";throw new Error(`Invalid glucose value: ${n.value} ${n.unit} (normalized: ${n.normalizedValue}${o})`)}}function Me(e){if(e.length<2)return 5;let t=e.map(i=>new Date(i.timestamp).getTime()).filter(Number.isFinite).sort((i,r)=>i-r);if(t.length<2)return 5;let n=[];for(let i=1;i<Math.min(10,t.length);i++){let r=t[i]-t[i-1];r>0&&n.push(r);}return n.length===0?5:n.reduce((i,r)=>i+r,0)/n.length/(1e3*60)}function L(e,t,n,o=false){let i=e.filter(c=>{let d=c.normalizedValue>=t,g=o?c.normalizedValue<=n:c.normalizedValue<n;return d&&g}),r=i.length,l=r/e.length*100,s=Me(e),u=r*s,a=r>0?i.reduce((c,d)=>c+d.normalizedValue,0)/r:null;return {percentage:Math.round(l*10)/10,duration:Math.round(u),readingCount:r,averageValue:a?Math.round(a):null}}function ye(e){let t=e.length,n=Me(e),o=t*n,i=o/(60*24),r;return i>=14?r="excellent":i>=7?r="good":i>=3?r="fair":r="poor",{totalReadings:t,totalDuration:Math.round(o),dataQuality:r}}function Ue(e){let t=e==="standard";return {tirGoal:t?O:me,tbrLevel1Goal:t?A:de,tbrLevel2Goal:t?ue:pe}}function Ve(e,t){let {tirGoal:n,tbrLevel1Goal:o,tbrLevel2Goal:i}=Ue(t),r=e.inRange.percentage>=n,l=e.low.percentage<o,s=e.veryLow.percentage<i,u=e.high.percentage<I,a=e.veryHigh.percentage<ce,c,d=!s||!a;!u&&e.high.percentage<=30;d?c="concerning":!l||!r?c="needs improvement":e.inRange.percentage>=n+10&&e.veryLow.percentage<.5?c="excellent":c="good";let R=ze({ranges:e,tirMeetsGoal:r,tbrLevel1Safe:l,tbrLevel2Safe:s,tarLevel1Acceptable:u,tarLevel2Acceptable:a,population:t});return {tirMeetsGoal:r,tbrLevel1Safe:l,tbrLevel2Safe:s,tarLevel1Acceptable:u,tarLevel2Acceptable:a,overallAssessment:c,recommendations:R}}function ze(e){let t=[];return e.tbrLevel2Safe||t.push(`Level 2 hypoglycemia (${e.ranges.veryLow.percentage.toFixed(1)}%) exceeds the consensus target.`),e.tarLevel2Acceptable||t.push(`Level 2 hyperglycemia (${e.ranges.veryHigh.percentage.toFixed(1)}%) exceeds the consensus target.`),!e.tbrLevel1Safe&&e.tbrLevel2Safe&&t.push(`Level 1 hypoglycemia (${e.ranges.low.percentage.toFixed(1)}%) is elevated.`),!e.tarLevel1Acceptable&&e.tarLevel2Acceptable&&t.push(`Level 1 hyperglycemia (${e.ranges.high.percentage.toFixed(1)}%) is elevated.`),!e.tirMeetsGoal&&e.tbrLevel1Safe&&e.tbrLevel2Safe&&t.push(`Time-in-range (${e.ranges.inRange.percentage.toFixed(1)}%) is below the consensus target.`),e.tirMeetsGoal&&e.tbrLevel1Safe&&e.tbrLevel2Safe&&e.tarLevel1Acceptable&&e.tarLevel2Acceptable?t.push("All metrics meet consensus targets."):e.tbrLevel2Safe&&e.tarLevel2Acceptable&&t.length===0&&t.push("Some metrics are outside consensus targets."),(e.population==="older-adults"||e.population==="high-risk")&&t.push("Older/high-risk population targets applied (lower TIR goal, stricter hypoglycemia limits)."),t}function ke(e){let t=[];return e.belowRange.percentage>=A&&t.push(`Time below range (${e.belowRange.percentage.toFixed(1)}%) exceeds the pregnancy consensus target.`),e.aboveRange.percentage>=I&&t.push(`Time above range (${e.aboveRange.percentage.toFixed(1)}%) exceeds the pregnancy consensus target.`),e.inRange.percentage<O&&t.push(`Time-in-range (${e.inRange.percentage.toFixed(1)}%) is below the pregnancy target of 70%.`),e.inRange.percentage>=O&&e.belowRange.percentage<A&&e.aboveRange.percentage<I?t.push("All metrics meet pregnancy consensus targets."):t.length===0&&t.push("Some pregnancy metrics are outside consensus targets."),t.push("Pregnancy target range: 63-140 mg/dL (3.5-7.8 mmol/L)."),t}function N(e){return 1.509*(Math.pow(Math.log(e),1.084)-5.381)}function V(e){let t=e.filter(o=>Number.isFinite(o)&&o>0);if(t.length===0)return NaN;let n=0;for(let o of t){let i=N(o);i<0&&(n+=10*i*i);}return n/t.length}function z(e){let t=e.filter(o=>Number.isFinite(o)&&o>0);if(t.length===0)return NaN;let n=0;for(let o of t){let i=N(o);i>0&&(n+=10*i*i);}return n/t.length}function k(e){if(e.length===0)return NaN;let t=new Map;for(let o of e){let i=o.unit===m?o.value:o.value*f;if(!Number.isFinite(i)||i<=0)continue;let r=o.timestamp.slice(0,10);t.has(r)||t.set(r,[]),t.get(r).push(i);}if(t.size===0)return NaN;let n=0;for(let o of t.values()){let i=0,r=0;for(let l of o){let s=N(l),u=10*s*s;s<0&&u>i&&(i=u),s>0&&u>r&&(r=u);}n+=i+r;}return Math.round(n/t.size*100)/100}function Ye(e){let t=e/18.0182;if(t<=1)return 0;let n=Math.log10(t);if(n<=0)return 0;let o=Math.log10(n)+.16;return 425*o*o}function Y(e,t=70,n=140){let o=e.filter(a=>Number.isFinite(a)&&a>0);if(o.length===0)return {grade:NaN,gradeEuglycemia:NaN,gradeHyperglycemia:NaN,gradeHypoglycemia:NaN};let i=0,r=0,l=0,s=0;for(let a of o){let c=Ye(a);i+=c,a<t?r+=c:a>n?s+=c:l+=c;}let u=i/o.length;return i===0?{grade:0,gradeEuglycemia:0,gradeHyperglycemia:0,gradeHypoglycemia:0}:{grade:Math.round(u*100)/100,gradeEuglycemia:Math.round(l/i*1e4)/100,gradeHyperglycemia:Math.round(s/i*1e4)/100,gradeHypoglycemia:Math.round(r/i*1e4)/100}}function B(e){if([e.veryLowPercent,e.lowPercent,e.highPercent,e.veryHighPercent].some(u=>!Number.isFinite(u)||u<0||u>100))throw new RangeError("GRI inputs must be finite percentages between 0 and 100.");let n=3*e.veryLowPercent+2.4*e.lowPercent,o=1.6*e.veryHighPercent+.8*e.highPercent,i=n+o,r=Math.min(100,Math.max(0,i)),l=Math.round(r*10)/10,s;return r<=20?s="A":r<=40?s="B":r<=60?s="C":r<=80?s="D":s="E",{score:l,zone:s,hypoComponent:Math.round(n*10)/10,hyperComponent:Math.round(o*10)/10}}function $(e){let t=e.filter(r=>Number.isFinite(r)&&r>0);if(t.length<2)return NaN;let n=t.reduce((r,l)=>r+l,0)/t.length,o=t.reduce((r,l)=>r+(l-n)**2,0)/(t.length-1),i=Math.sqrt(o);return Math.round(.001*(n+i)**2*100)/100}var Be=24*60*60*1e3,$e=15*60*1e3;function W(e,t){if(e.length<2)return NaN;let n=t?.toleranceMinutes!==void 0?t.toleranceMinutes*60*1e3:$e,o=e.map(r=>({time:new Date(r.timestamp).getTime(),value:r.unit===m?r.value:r.value*f})).filter(r=>Number.isFinite(r.time)&&Number.isFinite(r.value)&&r.value>0).sort((r,l)=>r.time-l.time);if(o.length<2)return NaN;let i=[];for(let r=0;r<o.length;r++){let l=o[r].time+Be,s=r+1,u=o.length-1,a=-1,c=1/0;for(;s<=u;){let d=s+u>>>1,g=Math.abs(o[d].time-l);g<c&&(c=g,a=d),o[d].time<l?s=d+1:u=d-1;}a!==-1&&c<=n&&i.push(Math.abs(o[a].value-o[r].value));}return i.length===0?NaN:Math.round(i.reduce((r,l)=>r+l,0)/i.length*10)/10}function j(e,t){if(e.length<2)return NaN;let n=t?.hours??1,o=(t?.toleranceMinutes??15)*60*1e3,i=n*60*60*1e3,r=e.map(a=>({time:new Date(a.timestamp).getTime(),value:a.unit===m?a.value:a.value*f})).filter(a=>Number.isFinite(a.time)&&Number.isFinite(a.value)&&a.value>0).sort((a,c)=>a.time-c.time);if(r.length<2)return NaN;let l=[];for(let a=0;a<r.length;a++){let c=r[a].time-i;if(c<r[0].time)continue;let d=-1,g=1/0,b=0,R=a-1;for(;b<=R;){let p=b+R>>>1,y=Math.abs(r[p].time-c);y<g&&(g=y,d=p),r[p].time<c?b=p+1:R=p-1;}d!==-1&&g<=o&&l.push(r[a].value-r[d].value);}if(l.length<2)return NaN;let s=l.reduce((a,c)=>a+c,0)/l.length,u=l.reduce((a,c)=>a+(c-s)**2,0)/(l.length-1);return Math.round(Math.sqrt(u)*10)/10}function Q(e,t){let n=t?.expectedIntervalMinutes??5;if(e.length<2)return {activePercent:NaN,actualReadings:e.length,expectedReadings:0,totalMinutes:0,meetsClinicalMinimum:false};let o=e.map(a=>new Date(a.timestamp).getTime()).filter(a=>Number.isFinite(a)).sort((a,c)=>a-c);if(o.length<2)return {activePercent:NaN,actualReadings:o.length,expectedReadings:0,totalMinutes:0,meetsClinicalMinimum:false};let r=(o[o.length-1]-o[0])/(1e3*60),l=Math.floor(r/n)+1,s=o.length,u=l>0?Math.min(100,Math.round(s/l*1e3)/10):NaN;return {activePercent:u,actualReadings:s,expectedReadings:l,totalMinutes:Math.round(r*10)/10,meetsClinicalMinimum:Number.isFinite(u)&&u>=70}}function We(e,t){if(e.length===0)throw new Error("Cannot calculate AGP metrics: readings array is empty");let n=e.map(u=>u.unit===m?u.value:u.value*f).filter(u=>Number.isFinite(u)&&u>0),o=n.length>0?Math.round(n.reduce((u,a)=>u+a,0)/n.length*10)/10:NaN,i=w(n),r=H(n),l=U(e),s={veryLowPercent:l.veryLow.percentage,lowPercent:l.low.percentage,highPercent:l.high.percentage,veryHighPercent:l.veryHigh.percentage};return {meanGlucose:o,sd:Number.isFinite(i)?Math.round(i*10)/10:NaN,cv:Number.isFinite(r)?Math.round(r*10)/10:NaN,lbgi:V(n),hbgi:z(n),adrr:k(e),grade:Y(n),gri:B(s),jIndex:$(n),modd:W(e,t?.modd),conga:j(e,t?.conga),activePercent:Q(e,t?.activePercent),totalReadings:n.length}}var je={DoubleUp:"rapidRising",SingleUp:"rising",FortyFiveUp:"slightlyRising",Flat:"flat",FortyFiveDown:"slightlyFalling",SingleDown:"falling",DoubleDown:"rapidFalling",None:"unknown",NotComputable:"unknown",RateOutOfRange:"unknown"};function Qe(e){let t=e.match(/Date\((\d+)\)/);if(t)return new Date(Number(t[1])).toISOString();let n=Date.parse(e);if(isNaN(n))throw new Error(`Unable to parse Dexcom date: ${e}`);return new Date(n).toISOString()}function qe(e){return e==null?"unknown":je[e]??"unknown"}function Je(e){return {value:e.Value,unit:m,timestamp:Qe(e.WT),trend:qe(e.Trend),source:"dexcom",vendorId:e.ST??e.DT}}function zn(e){return e.map(Je).sort((t,n)=>new Date(t.timestamp).getTime()-new Date(n.timestamp).getTime())}var Xe={1:"rapidFalling",2:"falling",3:"flat",4:"rising",5:"rapidRising"};function Ze(e){return e==null||e<1||e>5?"unknown":Xe[e]??"unknown"}function Ke(e){let t=Date.parse(e.Timestamp);if(isNaN(t))throw new Error(`Unable to parse Libre timestamp: ${e.Timestamp}`);return {value:e.Value,unit:m,timestamp:new Date(t).toISOString(),trend:Ze(e.TrendArrow),source:"libre"}}function Bn(e){return e.map(Ke).sort((t,n)=>new Date(t.timestamp).getTime()-new Date(n.timestamp).getTime())}var et={DoubleUp:"rapidRising",SingleUp:"rising",FortyFiveUp:"slightlyRising",Flat:"flat",FortyFiveDown:"slightlyFalling",SingleDown:"falling",DoubleDown:"rapidFalling",NONE:"unknown","NOT COMPUTABLE":"unknown","RATE OUT OF RANGE":"unknown"};function tt(e){return e?et[e]??"unknown":"unknown"}function nt(e){let t=(()=>{if(e.dateString){let o=Date.parse(e.dateString);if(!Number.isNaN(o))return new Date(o).toISOString();if(e.date!==void 0&&e.date!==null){let i=new Date(e.date);if(!Number.isNaN(i.getTime()))return i.toISOString()}throw new Error(`Unable to parse Nightscout timestamp from 'dateString': ${e.dateString}`)}let n=new Date(e.date);if(Number.isNaN(n.getTime()))throw new Error(`Unable to parse Nightscout timestamp from 'date' field: ${String(e.date)}`);return n.toISOString()})();return {value:e.sgv,unit:m,timestamp:t,trend:tt(e.direction),source:"nightscout",vendorId:e._id}}function jn(e){return e.map(nt).sort((t,n)=>new Date(t.timestamp).getTime()-new Date(n.timestamp).getTime())}var M="http://loinc.org",G="http://unitsofmeasure.org";function Jn(e,t,n){let o=[{code:{coding:[{system:M,code:"97507-8",display:"Time in range (70-180 mg/dL)"}]},valueQuantity:{value:e.inRange.percentage,unit:"%",system:G,code:"%"}},{code:{coding:[{system:M,code:"97506-0",display:"Time below range level 1 (54-69 mg/dL)"}]},valueQuantity:{value:e.low.percentage,unit:"%",system:G,code:"%"}},{code:{coding:[{system:M,code:"97505-2",display:"Time below range level 2 (<54 mg/dL)"}]},valueQuantity:{value:e.veryLow.percentage,unit:"%",system:G,code:"%"}},{code:{coding:[{system:M,code:"97508-6",display:"Time above range level 1 (181-250 mg/dL)"}]},valueQuantity:{value:e.high.percentage,unit:"%",system:G,code:"%"}},{code:{coding:[{system:M,code:"97509-4",display:"Time above range level 2 (>250 mg/dL)"}]},valueQuantity:{value:e.veryHigh.percentage,unit:"%",system:G,code:"%"}}];return n?.meanGlucose!==void 0&&o.push({code:{coding:[{system:M,code:"97507-0",display:"Mean glucose"}]},valueQuantity:{value:n.meanGlucose,unit:"mg/dL",system:G,code:"mg/dL"}}),n?.cv!==void 0&&o.push({code:{coding:[{system:M,code:"97506-2",display:"Coefficient of variation"}]},valueQuantity:{value:n.cv,unit:"%",system:G,code:"%"}}),{resourceType:"Observation",status:"final",code:{coding:[{system:M,code:"97504-5",display:"CGM summary"}]},effectivePeriod:{start:t.start,end:t.end},component:o}}function rt(e){let t=e.unit===m;return {resourceType:"Observation",status:"final",code:{coding:[{system:M,code:t?"99504-3":"14745-4",display:t?"Glucose [Mass/volume] in Interstitial fluid":"Glucose [Moles/volume] in Interstitial fluid"}]},effectiveDateTime:e.timestamp,valueQuantity:{value:e.value,unit:e.unit,system:G,code:t?"mg/dL":"mmol/L"}}}function Xn(e){return e.map(rt)}var q={namespace:"omh",name:"blood-glucose",version:"3.0"};function Le(e){return {blood_glucose:{value:e.value,unit:e.unit===m?"mg/dL":"mmol/L"},effective_time_frame:{date_time:e.timestamp},specimen_source:"interstitial fluid"}}function er(e,t){return {header:{id:t,schema_id:{namespace:q.namespace,name:q.name,version:q.version},creation_date_time:new Date().toISOString()},body:Le(e)}}function tr(e){return e.map(Le)}
exports.A1C_TO_EAG_CONSTANT=E;exports.A1C_TO_EAG_MULTIPLIER=_;exports.AllowedGlucoseUnits=Zt;exports.GLUCOSE_COLOR_ELEVATED=_e;exports.GLUCOSE_COLOR_HIGH=Ee;exports.GLUCOSE_COLOR_LOW=Ge;exports.GLUCOSE_COLOR_NORMAL=ve;exports.GLUCOSE_COLOR_NORMAL_DOWN=xe;exports.GLUCOSE_COLOR_NORMAL_UP=Te;exports.GLUCOSE_ZONE_COLORS=ct;exports.GMI_COEFFICIENTS=v;exports.HOMA_IR_CUTOFFS=x;exports.HOMA_IR_DENOMINATOR=J;exports.HYPER_THRESHOLD_MGDL=Z;exports.HYPER_THRESHOLD_MMOLL=ee;exports.HYPO_THRESHOLD_MGDL=X;exports.HYPO_THRESHOLD_MMOLL=K;exports.MGDL_MMOLL_CONVERSION=f;exports.MG_DL=m;exports.MMOL_L=h;exports.PREGNANCY_TARGET_HIGH_MGDL=ae;exports.PREGNANCY_TARGET_HIGH_MMOLL=se;exports.PREGNANCY_TARGET_LOW_MGDL=ie;exports.PREGNANCY_TARGET_LOW_MMOLL=le;exports.TAR_LEVEL1_GOAL=I;exports.TAR_LEVEL2_GOAL=ce;exports.TBR_LEVEL1_GOAL=A;exports.TBR_LEVEL1_GOAL_OLDER_ADULTS=de;exports.TBR_LEVEL2_GOAL=ue;exports.TBR_LEVEL2_GOAL_OLDER_ADULTS=pe;exports.TIR_GOAL_OLDER_ADULTS=me;exports.TIR_GOAL_STANDARD=O;exports.TIR_HIGH_THRESHOLD_MGDL=re;exports.TIR_HIGH_THRESHOLD_MMOLL=gt;exports.TIR_LOW_THRESHOLD_MGDL=ne;exports.TIR_LOW_THRESHOLD_MMOLL=pt;exports.TIR_VERY_HIGH_THRESHOLD_MGDL=oe;exports.TIR_VERY_HIGH_THRESHOLD_MMOLL=ft;exports.TIR_VERY_LOW_THRESHOLD_MGDL=te;exports.TIR_VERY_LOW_THRESHOLD_MMOLL=dt;exports.TREND_ARROWS=mt;exports.a1cDelta=lt;exports.a1cToGMI=Pt;exports.a1cTrend=st;exports.buildFHIRCGMSummary=Jn;exports.buildFHIRSensorReading=rt;exports.buildFHIRSensorReadings=Xn;exports.buildOMHBloodGlucose=Le;exports.buildOMHBloodGlucoseList=tr;exports.buildOMHDataPoint=er;exports.calculateADRR=k;exports.calculateAGPMetrics=We;exports.calculateActivePercent=Q;exports.calculateCONGA=j;exports.calculateEnhancedTIR=U;exports.calculateGRADE=Y;exports.calculateGRI=B;exports.calculateHOMAIR=Ie;exports.calculateJIndex=$;exports.calculateMODD=W;exports.calculatePregnancyTIR=Fe;exports.calculateTIR=Wt;exports.calculateTimeInRange=qt;exports.checkGlycemicAlignment=Et;exports.clinicalMAGE=P;exports.convertGlucoseUnit=Vt;exports.estimateA1CFromAverage=Ct;exports.estimateA1CFromAvgGlucose=Dt;exports.estimateAvgGlucoseFromA1C=wt;exports.estimateEAG=St;exports.estimateGMI=Ht;exports.fbg=N;exports.formatA1C=ot;exports.formatDate=Bt;exports.formatGlucose=kt;exports.formatPercentage=Yt;exports.getA1CCategory=it;exports.getGlucoseLabel=yt;exports.getTIRSummary=jt;exports.glucoseCoefficientOfVariation=H;exports.glucoseHBGI=z;exports.glucoseLBGI=V;exports.glucoseMAGE=He;exports.glucosePercentiles=Pe;exports.glucoseStandardDeviation=w;exports.groupByDay=Qt;exports.isA1CInTarget=at;exports.isEstimateGMIOptions=ge;exports.isHyper=Ae;exports.isHypo=Oe;exports.isValidA1C=T;exports.isValidGlucoseString=fe;exports.isValidGlucoseValue=S;exports.isValidInsulin=C;exports.mgDlToMmolL=Ft;exports.mmolLToMgDl=Ut;exports.normalizeDexcomEntries=zn;exports.normalizeDexcomEntry=Je;exports.normalizeDexcomTrend=qe;exports.normalizeLibreEntries=Bn;exports.normalizeLibreEntry=Ke;exports.normalizeLibreTrend=Ze;exports.normalizeNightscoutDirection=tt;exports.normalizeNightscoutEntries=jn;exports.normalizeNightscoutEntry=nt;exports.parseDexcomDate=Qe;exports.parseGlucoseString=Re;//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map