ts-watches 0.1.0 → 0.1.2
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.
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +40 -49
- package/dist/chunk-79g2tyda.js +1 -0
- package/dist/chunk-dz0ym1nb.js +2 -0
- package/dist/chunk-e2wpg6tp.js +2 -0
- package/dist/{cloud → src/cloud}/garmin-activity-api.d.ts +29 -1
- package/dist/{cloud → src/cloud}/garmin-connect.d.ts +16 -0
- package/dist/src/cloud/index.d.ts +13 -0
- package/dist/src/drivers/index.js +1 -0
- package/dist/src/fit/index.js +1 -0
- package/dist/src/index.js +167 -168
- package/package.json +5 -5
- package/dist/chunk-gsgt8r0z.js +0 -2
- package/dist/cloud/index.d.ts +0 -4
- /package/dist/{analysis → src/analysis}/index.d.ts +0 -0
- /package/dist/{analysis → src/analysis}/personal-records.d.ts +0 -0
- /package/dist/{analysis → src/analysis}/race-predictor.d.ts +0 -0
- /package/dist/{analysis → src/analysis}/running-dynamics.d.ts +0 -0
- /package/dist/{analysis → src/analysis}/training-load.d.ts +0 -0
- /package/dist/{analysis → src/analysis}/zones.d.ts +0 -0
- /package/dist/{cloud → src/cloud}/strava.d.ts +0 -0
- /package/dist/{cloud → src/cloud}/trainingpeaks.d.ts +0 -0
- /package/dist/{config.d.ts → src/config.d.ts} +0 -0
- /package/dist/{drivers → src/drivers}/apple.d.ts +0 -0
- /package/dist/{drivers → src/drivers}/coros.d.ts +0 -0
- /package/dist/{drivers → src/drivers}/garmin.d.ts +0 -0
- /package/dist/{drivers → src/drivers}/index.d.ts +0 -0
- /package/dist/{drivers → src/drivers}/polar.d.ts +0 -0
- /package/dist/{drivers → src/drivers}/suunto.d.ts +0 -0
- /package/dist/{drivers → src/drivers}/wahoo.d.ts +0 -0
- /package/dist/{export → src/export}/csv.d.ts +0 -0
- /package/dist/{export → src/export}/geojson.d.ts +0 -0
- /package/dist/{export → src/export}/gpx.d.ts +0 -0
- /package/dist/{export → src/export}/index.d.ts +0 -0
- /package/dist/{export → src/export}/tcx.d.ts +0 -0
- /package/dist/{fit → src/fit}/constants.d.ts +0 -0
- /package/dist/{fit → src/fit}/decoder.d.ts +0 -0
- /package/dist/{fit → src/fit}/index.d.ts +0 -0
- /package/dist/{fit → src/fit}/parser.d.ts +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{realtime → src/realtime}/ant-plus.d.ts +0 -0
- /package/dist/{realtime → src/realtime}/bluetooth.d.ts +0 -0
- /package/dist/{realtime → src/realtime}/index.d.ts +0 -0
- /package/dist/{realtime → src/realtime}/live-tracking.d.ts +0 -0
- /package/dist/{types.d.ts → src/types.d.ts} +0 -0
- /package/dist/{workouts → src/workouts}/builder.d.ts +0 -0
- /package/dist/{workouts → src/workouts}/course.d.ts +0 -0
- /package/dist/{workouts → src/workouts}/index.d.ts +0 -0
- /package/dist/{workouts → src/workouts}/training-plan.d.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
package/dist/bin/cli.js
CHANGED
|
@@ -1,52 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import{
|
|
2
|
+
import"../chunk-79g2tyda.js";import{l}from"../chunk-dz0ym1nb.js";import{existsSync as X}from"node:fs";import B from"node:fs/promises";import L from"node:os";import k from"node:path";import x from"node:process";import{EventEmitter as Z}from"node:events";import w from"node:process";import P from"node:process";import A from"node:process";import _ from"node:process";import O from"node:process";import z from"node:tty";import We,{stdin as ht,stdout as dt}from"node:process";class G{configPath;config=null;events=[];retryCount=0;maxRetries=3;retryDelayMs=1000;constructor(){let e=L.homedir(),t=k.join(e,".config","clapp");this.configPath=k.join(t,"telemetry.json")}async isEnabled(){if(x.env.DO_NOT_TRACK==="1"||x.env.DO_NOT_TRACK==="true")return!1;if(x.env.NO_TELEMETRY==="1"||x.env.NO_TELEMETRY==="true")return!1;return(await this.loadConfig()).enabled}async enable(){let e=await this.loadConfig();if(e.enabled=!0,!e.userId)e.userId=this.generateUserId();await this.saveConfig(e)}async disable(){let e=await this.loadConfig();e.enabled=!1,await this.saveConfig(e)}async track(e,t){if(!await this.isEnabled())return;let s={event:e,...t,timestamp:Date.now(),platform:L.platform(),nodeVersion:x.version};if(this.events.push(s),this.events.length>=10)await this.send()}async trackCommand(e,t){await this.track("command",{command:e,duration:t})}async trackError(e,t){await this.track("error",{error:e,command:t})}async send(){if(!await this.isEnabled()||this.events.length===0)return!0;try{this.events=[],this.retryCount=0;let t=await this.loadConfig();return t.lastSent=Date.now(),await this.saveConfig(t),!0}catch{if(this.retryCount<this.maxRetries){this.retryCount++;let t=this.retryDelayMs*2**(this.retryCount-1);return await this.sleep(t),this.send()}return this.events=[],this.retryCount=0,!1}}async flush(){if(this.events.length===0)return!0;return this.send()}sleep(e){return new Promise((t)=>setTimeout(t,e))}async status(){let e=await this.loadConfig();return{enabled:e.enabled,doNotTrack:x.env.DO_NOT_TRACK==="1"||x.env.DO_NOT_TRACK==="true",eventsQueued:this.events.length,lastSent:e.lastSent}}async loadConfig(){if(this.config)return this.config;try{if(X(this.configPath)){let e=await B.readFile(this.configPath,"utf-8");return this.config=JSON.parse(e),this.config}}catch{}return this.config={enabled:!1},this.config}async saveConfig(e){this.config=e;try{let t=k.dirname(this.configPath);await B.mkdir(t,{recursive:!0}),await B.writeFile(this.configPath,JSON.stringify(e,null,2),"utf-8")}catch{}}generateUserId(){let e=Math.random().toString(36).substring(2,15),t=Date.now().toString(36);return`${e}-${t}`}}var Xe=new G;function ee(e,t={}){let i={_:[]},s=t.alias||{},n=new Set(t.boolean||[]),o={};for(let a of Object.keys(s))for(let r of s[a])o[r]=a;for(let a of n)if(s[a])for(let r of s[a])n.add(r);function c(a,r){let u=o[a]||a;if(i[u]=r,s[u])for(let h of s[u])i[h]=r;if(o[a]&&s[o[a]])for(let h of s[o[a]])i[h]=r;i[a]=r}for(let a=0;a<e.length;a++){let r=e[a];if(r==="--"){i._.push(...e.slice(a+1));break}if(r.startsWith("--")){let u=r.indexOf("=");if(u!==-1){let h=r.slice(2,u),d=r.slice(u+1);c(h,d)}else{let h=r.slice(2);if(h.startsWith("no-")){let m=h.slice(3);c(m,!1);continue}let d=o[h]||h;if(n.has(d)||n.has(h))c(h,!0);else{let m=e[a+1];if(m!==void 0&&!m.startsWith("-"))c(h,m),a++;else c(h,!0)}}}else if(r.startsWith("-")&&r.length>1){let u=r.slice(1);for(let h=0;h<u.length;h++){let d=u[h],m=o[d]||d;if(h===u.length-1&&!n.has(m)&&!n.has(d)){let f=e[a+1];if(f!==void 0&&!f.startsWith("-"))c(d,f),a++;else c(d,!0)}else c(d,!0)}}else i._.push(r)}return i}function M(e){return e.replace(/[<[].+/,"").trim()}function te(e){let t=/<([^>]+)>/g,i=/\[([^\]]+)\]/g,s=[],n=(a)=>{let r=!1,u=a[1];if(u.startsWith("..."))u=u.slice(3),r=!0;return{required:a[0].startsWith("<"),value:u,variadic:r}},o;while(o=t.exec(e))s.push(n(o));let c;while(c=i.exec(e))s.push(n(c));return s}function se(e){let t={alias:{},boolean:[]};for(let[i,s]of e.entries()){if(s.names.length>1)t.alias[s.names[0]]=s.names.slice(1);if(s.isBoolean)if(s.negated){if(!e.some((o,c)=>c!==i&&o.names.some((a)=>s.names.includes(a))&&typeof o.required==="boolean"))t.boolean.push(s.names[0])}else t.boolean.push(s.names[0])}return t}function W(e){return e.reduce((t,i)=>t.length>=i.length?t:i,"")}function T(e,t){return e.length>=t?e:`${e}${" ".repeat(t-e.length)}`}function ie(e){return e.replace(/([a-z])-([a-z])/g,(t,i,s)=>i+s.toUpperCase())}function ne(e,t,i){let s=0,n=t.length,o=e,c;for(;s<n;++s)c=o[t[s]],o=o[t[s]]=s===n-1?i:c!=null?c:!!~t[s+1].indexOf(".")||!(+t[s+1]>-1)?{}:[]}function re(e,t){for(let i of Object.keys(t)){let s=t[i];if(s.shouldTransform){if(e[i]=Array.prototype.concat.call([],e[i]),typeof s.transformFunction==="function")e[i]=e[i].map(s.transformFunction)}}}function oe(e){let t=/([^\\/]+)$/.exec(e);return t?t[1]:""}function U(e){return e.split(".").map((t,i)=>i===0?ie(t):t).join(".")}class I extends Error{exitCode=2;isUsageError=!0;constructor(e){super(e);if(this.name=this.constructor.name,typeof Error.captureStackTrace==="function")Error.captureStackTrace(this,this.constructor);else this.stack=Error(e).stack}format(e=!1){if(e&&this.stack)return`${this.message}
|
|
3
3
|
|
|
4
4
|
Stack trace:
|
|
5
|
-
${this.stack}`;return this.message}}function
|
|
6
|
-
`);let
|
|
7
|
-
|
|
8
|
-
`;
|
|
9
|
-
`,
|
|
10
|
-
`)}
|
|
11
|
-
`)})}let
|
|
12
|
-
`)})}if(this.examples.length>0)
|
|
13
|
-
`)});if(
|
|
14
|
-
${
|
|
15
|
-
|
|
16
|
-
`))}outputVersion(){let{name:
|
|
17
|
-
|
|
18
|
-
Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`)}}checkUnknownOptions(){let{options:
|
|
19
|
-
|
|
20
|
-
Did you mean one of these?`,
|
|
21
|
-
• ${
|
|
22
|
-
|
|
23
|
-
Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`,new I(
|
|
24
|
-
|
|
25
|
-
Example: ${this.cli.name} ${this.rawName} ${
|
|
26
|
-
|
|
27
|
-
Received ${
|
|
28
|
-
✗ Command "${
|
|
29
|
-
`)),this.enableDidYouMean){let
|
|
30
|
-
Run \`${this.name??"cli"} --help\` for usage.`;
|
|
31
|
-
`),
|
|
32
|
-
`)
|
|
33
|
-
|
|
34
|
-
`)
|
|
35
|
-
|
|
36
|
-
`)
|
|
37
|
-
|
|
38
|
-
`);
|
|
39
|
-
`).
|
|
40
|
-
`)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Make sure your watch is:`),console.error(" 1. Connected via USB"),console.error(" 2. In Mass Storage mode (Settings > System > USB Mode)"),console.error(" 3. Garmin Express is closed (it may lock the device)"),process.exit(1);let $=Z[0];console.log(`Found ${$.name} at ${$.path}
|
|
45
|
-
`);let Q={outputDir:z.output,includeActivities:z.monitoring!==!0,includeMonitoring:z.activities!==!0,copyRawFiles:z.raw!==!1,since:z.since?new Date(z.since):void 0,until:z.until?new Date(z.until):void 0};console.log("Downloading data...");let q=await J.downloadData($,Q);if(console.log(`
|
|
46
|
-
Download complete!`),console.log(` Activities: ${q.activities.length}`),console.log(` Monitoring days: ${q.monitoring.size}`),console.log(` Raw files copied: ${q.rawFiles.length}`),q.errors.length>0){if(console.log(` Errors: ${q.errors.length}`),z.verbose)for(let X of q.errors)console.error(` - ${X.message}`)}if(q.activities.length>0&&z.verbose){console.log(`
|
|
47
|
-
Activities:`);for(let X of q.activities.slice(-10)){let j=X.startTime.toLocaleDateString(),Y=Math.round(X.totalTimerTime/60),U=(X.totalDistance/1000).toFixed(2);console.log(` ${j} - ${X.sport} - ${Y}min - ${U}km`)}if(q.activities.length>10)console.log(` ... and ${q.activities.length-10} more`)}});N.command("parse <file>","Parse a FIT file").option("-o, --output <file>","Output file (defaults to stdout)").option("-f, --format <format>","Output format: json, summary",{default:"summary"}).option("--verbose","Enable verbose output").action(async(z,J)=>{let Z=E();try{let $=await Z.parseActivityFile(z);if(!$){let Q=await Z.parseMonitoringFile(z);if(J.format==="json"){let q=JSON.stringify(Q,null,2);if(J.output)await Bun.write(J.output,q),console.log(`Written to ${J.output}`);else console.log(q)}else{if(console.log("Monitoring Data:"),Q.heartRate)console.log(` Heart Rate: avg ${Q.heartRate.avgHeartRate} bpm`);if(Q.sleep)console.log(` Sleep: ${Math.round(Q.sleep.totalSleepTime)} minutes`);if(Q.stress)console.log(` Stress: avg ${Q.stress.avgStressLevel}`);if(Q.spO2)console.log(` SpO2: avg ${Q.spO2.avgSpO2}%`)}return}if(J.format==="json"){let Q=JSON.stringify($,null,2);if(J.output)await Bun.write(J.output,Q),console.log(`Written to ${J.output}`);else console.log(Q)}else{if(console.log("Activity Summary:"),console.log(` Sport: ${$.sport}${$.subSport?` (${$.subSport})`:""}`),console.log(` Date: ${$.startTime.toLocaleString()}`),console.log(` Duration: ${Math.round($.totalTimerTime/60)} minutes`),console.log(` Distance: ${($.totalDistance/1000).toFixed(2)} km`),console.log(` Calories: ${$.totalCalories}`),$.avgHeartRate)console.log(` Avg Heart Rate: ${$.avgHeartRate} bpm`);if($.maxHeartRate)console.log(` Max Heart Rate: ${$.maxHeartRate} bpm`);if($.avgSpeed){let Q=16.666666666666668/$.avgSpeed;console.log(` Avg Pace: ${Math.floor(Q)}:${String(Math.round(Q%1*60)).padStart(2,"0")} /km`)}if($.totalAscent)console.log(` Total Ascent: ${$.totalAscent} m`);if($.avgCadence)console.log(` Avg Cadence: ${$.avgCadence} spm`);if($.avgPower)console.log(` Avg Power: ${$.avgPower} W`);console.log(`
|
|
48
|
-
Laps: ${$.laps.length}`),console.log(` Data Points: ${$.records.length}`)}}catch($){console.error(`Error parsing file: ${$ instanceof Error?$.message:$}`),process.exit(1)}});N.command("watch","Watch for device connection and auto-download").option("-o, --output <dir>","Output directory",{default:"./watch-data"}).option("--interval <ms>","Check interval in milliseconds",{default:2000}).action(async(z)=>{let J=E(),Z=null;console.log("Watching for device connection..."),console.log(`Press Ctrl+C to stop
|
|
49
|
-
`);let $=async()=>{let Q=await J.detectDevices();if(Q.length>0&&Q[0].path!==Z){let q=Q[0];Z=q.path,console.log(`
|
|
50
|
-
Device connected: ${q.name}`),console.log(`Starting download...
|
|
51
|
-
`);let X=await J.downloadData(q,{outputDir:z.output,copyRawFiles:!0});if(console.log("Download complete!"),console.log(` Activities: ${X.activities.length}`),console.log(` Monitoring days: ${X.monitoring.size}`),console.log(` Raw files: ${X.rawFiles.length}`),X.errors.length>0)console.log(` Errors: ${X.errors.length}`);console.log(`
|
|
52
|
-
Continuing to watch...`)}else if(Q.length===0&&Z!==null)console.log("Device disconnected"),Z=null};await $(),setInterval($,z.interval)});N.command("version","Show the version").action(()=>{console.log(u)});N.version(u);N.help();N.parse();
|
|
5
|
+
${this.stack}`;return this.message}}function ae(){let{env:e}=P,{TERM:t,TERM_PROGRAM:i}=e;if(P.platform!=="win32")return t!=="linux";return Boolean(e.WT_SESSION)||Boolean(e.TERMINUS_SUBLIME)||e.ConEmuTask==="{cmd::Cmder}"||i==="Terminus-Sublime"||i==="vscode"||t==="xterm-256color"||t==="alacritty"||t==="rxvt-unicode"||t==="rxvt-unicode-256color"||e.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var E="\x1B",v=`${E}[`;var F={to(e,t){if(!t)return`${v}${e+1}G`;return`${v}${t+1};${e+1}H`},move(e,t){let i="";if(e<0)i+=`${v}${-e}D`;else if(e>0)i+=`${v}${e}C`;if(t<0)i+=`${v}${-t}A`;else if(t>0)i+=`${v}${t}B`;return i},up:(e=1)=>`${v}${e}A`,down:(e=1)=>`${v}${e}B`,forward:(e=1)=>`${v}${e}C`,backward:(e=1)=>`${v}${e}D`,nextLine:(e=1)=>`${v}E`.repeat(e),prevLine:(e=1)=>`${v}F`.repeat(e),left:`${v}G`,hide:`${v}?25l`,show:`${v}?25h`,save:`${E}7`,restore:`${E}8`};var st={screen:`${v}2J`,up:(e=1)=>`${v}1J`.repeat(e),down:(e=1)=>`${v}J`.repeat(e),line:`${v}2K`,lineEnd:`${v}K`,lineStart:`${v}1K`,lines(e){let t="";for(let i=0;i<e;i++)t+=this.line+(i<e-1?F.up():"");if(e)t+=F.left;return t}},it={screen:`${E}c`};function le(e,t){let i=[];for(let s=0;s<=t.length;s++)i[s]=[s];for(let s=0;s<=e.length;s++)i[0][s]=s;for(let s=1;s<=t.length;s++)for(let n=1;n<=e.length;n++)if(t.charAt(s-1)===e.charAt(n-1))i[s][n]=i[s-1][n-1];else i[s][n]=Math.min(i[s-1][n-1]+1,i[s][n-1]+1,i[s-1][n]+1);return i[t.length][e.length]}function K(e,t,i=2,s=3){return t.map((n)=>({cmd:n,distance:le(e,n)})).filter(({distance:n})=>n<=i).sort((n,o)=>n.distance-o.distance).slice(0,s).map(({cmd:n})=>n)}class q{rawName;description;name;names;isBoolean;required;config;negated;constructor(e,t,i){if(this.rawName=e,this.description=t,this.config=Object.assign({},i),e=e.replace(/\.\*/g,""),this.negated=!1,this.names=M(e).split(",").map((s)=>{let n=s.trim().replace(/^-{1,2}/,"");if(n.startsWith("no-"))this.negated=!0,n=n.replace(/^no-/,"");return U(n)}).sort((s,n)=>s.length>n.length?1:-1),this.name=this.names[this.names.length-1],this.negated&&this.config.default==null)this.config.default=!0;if(e.includes("<"))this.required=!0;else if(e.includes("["))this.required=!1;else this.isBoolean=!0}}var rt=A.argv,ce=`${A.platform}-${A.arch} bun-v${typeof Bun<"u"?Bun.version:"unknown"}`,H=_.argv,ue=`${_.platform}-${_.arch} node-${_.version}`;class j{rawName;description;config;cli;options;aliasNames;name;namespace;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;beforeHooks;afterHooks;middleware;constructor(e,t,i,s){this.rawName=e,this.description=t,this.config=i,this.cli=s,this.options=[],this.aliasNames=[],this.name=M(e);let n=M(e),o=n.indexOf(":");if(o>0)this.namespace=n.substring(0,o),this.name=n.substring(o+1);if(this.args=te(e),this.examples=[],this.beforeHooks=[],this.afterHooks=[],this.middleware=[],!i)this.config={}}usage(e){return this.usageText=e,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(e,t="-v, --version"){return this.versionNumber=e,this.option(t,"Display version number"),this}example(e){return this.examples.push(e),this}option(e,t,i){let s=new q(e,t,i);return this.options.push(s),this}alias(e){return this.aliasNames.push(e),this}action(e){return this.commandAction=e,this}before(e){return this.beforeHooks.push(e),this}after(e){return this.afterHooks.push(e),this}use(e){return this.middleware.push(e),this}isMatched(e){if(this.aliasNames.includes(e))return!0;if(this.namespace)return`${this.namespace}:${this.name}`===e;return this.name===e}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof R}get displayName(){return this.namespace?`${this.namespace}:${this.name}`:this.name}hasOption(e){return e=e.split(".")[0],!!this.options.find((t)=>t.names.includes(e))}outputHelp(){let{name:e,commands:t}=this.cli,{versionNumber:i,options:s,helpCallback:n}=this.cli.globalCommand,o=[{body:`${e}${i?`/${i}`:""}`}];if(o.push({title:"Usage",body:` $ ${e} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&t.length>0){let r=W(t.map((f)=>f.rawName)),u=new Map,h=[];for(let f of t)if(f.namespace){if(!u.has(f.namespace))u.set(f.namespace,[]);u.get(f.namespace).push(f)}else h.push(f);let d="";if(h.length>0)d+=h.map((f)=>` ${T(f.rawName,r.length)} ${f.description}`).join(`
|
|
6
|
+
`);let m=Array.from(u.keys()).sort();for(let f of m){let Q=u.get(f);if(d.length>0)d+=`
|
|
7
|
+
|
|
8
|
+
`;d+=` ${f}:
|
|
9
|
+
`,d+=Q.map((N)=>` ${T(N.rawName,r.length-2)} ${N.description}`).join(`
|
|
10
|
+
`)}o.push({title:"Commands",body:d}),o.push({title:"For more info, run any command with the `--help` flag",body:t.map((f)=>` $ ${e}${f.displayName===""?"":` ${f.displayName}`} --help`).join(`
|
|
11
|
+
`)})}let a=this.isGlobalCommand?s:[...this.options,...s||[]];if(!this.isGlobalCommand&&!this.isDefaultCommand)a=a.filter((r)=>r.name!=="version");if(a.length>0){let r=W(a.map((u)=>u.rawName));o.push({title:"Options",body:a.map((u)=>` ${T(u.rawName,r.length)} ${u.description} ${u.config.default===void 0?"":`(default: ${u.config.default})`}`).join(`
|
|
12
|
+
`)})}if(this.examples.length>0)o.push({title:"Examples",body:this.examples.map((r)=>{if(typeof r==="function")return r(e);return r}).join(`
|
|
13
|
+
`)});if(n)o=n(o)||o;console.log(o.map((r)=>r.title?`${r.title}:
|
|
14
|
+
${r.body}`:r.body).join(`
|
|
15
|
+
|
|
16
|
+
`))}outputVersion(){let{name:e}=this.cli,{versionNumber:t}=this.cli.globalCommand;if(t)console.log(`${e}/${t} ${typeof Bun<"u"?ce:ue}`)}checkRequiredArgs(){let e=this.args.filter((t)=>t.required).length;if(this.cli.args.length<e){let i=this.args.filter((n)=>n.required).slice(this.cli.args.length),s=i.map((n)=>`<${n.value}>`).join(" ");throw new I(`Missing required argument${i.length>1?"s":""}: ${s}
|
|
17
|
+
|
|
18
|
+
Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`)}}checkUnknownOptions(){let{options:e,globalCommand:t}=this.cli;if(!this.config.allowUnknownOptions){for(let i of Object.keys(e))if(i!=="--"&&!this.hasOption(i)&&!t.hasOption(i)){let n=[...t.options,...this.options].flatMap((r)=>r.names),o=i.length>1?`--${i}`:`-${i}`,c=K(i,n),a=`Unknown option \`${o}\``;if(c.length>0)a+=`
|
|
19
|
+
|
|
20
|
+
Did you mean one of these?`,c.forEach((r)=>{let u=r.length>1?`--${r}`:`-${r}`;a+=`
|
|
21
|
+
• ${u}`});throw a+=`
|
|
22
|
+
|
|
23
|
+
Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`,new I(a)}}}checkOptionValue(){let{options:e,globalCommand:t}=this.cli,i=[...t.options,...this.options];for(let s of i){let n=e[s.name.split(".")[0]];if(s.required){let o=i.some((c)=>c.negated&&c.names.includes(s.name));if(n===!0||n===!1&&!o)throw new I(`Option \`${s.rawName}\` requires a value.
|
|
24
|
+
|
|
25
|
+
Example: ${this.cli.name} ${this.rawName} ${s.rawName} <value>`)}}}}class R extends j{constructor(e){super("@@global@@","",{},e)}}var he=j,b={red:["\x1B[31m","\x1B[39m"],green:["\x1B[32m","\x1B[39m"],blue:["\x1B[34m","\x1B[39m"],yellow:["\x1B[33m","\x1B[39m"],cyan:["\x1B[36m","\x1B[39m"],magenta:["\x1B[35m","\x1B[39m"],white:["\x1B[37m","\x1B[39m"],gray:["\x1B[90m","\x1B[39m"],bgRed:["\x1B[41m","\x1B[49m"],bgGreen:["\x1B[42m","\x1B[49m"],bgBlue:["\x1B[44m","\x1B[49m"],bgYellow:["\x1B[43m","\x1B[49m"],bgCyan:["\x1B[46m","\x1B[49m"],bgMagenta:["\x1B[45m","\x1B[49m"],bold:["\x1B[1m","\x1B[22m"],italic:["\x1B[3m","\x1B[23m"],underline:["\x1B[4m","\x1B[24m"],dim:["\x1B[2m","\x1B[22m"],inverse:["\x1B[7m","\x1B[27m"],hidden:["\x1B[8m","\x1B[28m"],strikethrough:["\x1B[9m","\x1B[29m"]};var C={primary:"blue",secondary:"cyan",success:"green",warning:"yellow",error:"red",info:"magenta",muted:"gray"};function de(){return!0}function me(){let e={};e.supportsColor=de();function t(s,n=[]){let o=s===""?[]:[...n,s],c=function(r){if(!e.supportsColor)return r;let u="",h="";for(let d of o)if(d in C&&C[d]in b){let m=C[d];u+=b[m][0],h=b[m][1]+h}else if(d in b)u+=b[d][0],h=b[d][1]+h;return u+r+h},a=[...Object.keys(b),...Object.keys(C)];for(let r of a)if(!(r in c))Object.defineProperty(c,r,{get(){return t(r,o)}});return c}let i=[...Object.keys(b),...Object.keys(C)];for(let s of i)if(!(s in e))Object.defineProperty(e,s,{get(){return t(s)}});return e}var S=me();class V extends Z{name;commands;globalCommand;matchedCommand;matchedCommandName;rawArgs;args;options;showHelpOnExit;showVersionOnExit;enableDidYouMean=!0;signalHandlersSet=!1;isVerbose=!1;isQuiet=!1;isDebug=!1;isNoInteraction=!1;environment;isDryRun=!1;isForce=!1;useEmoji=!0;theme;isNoCache=!1;constructor(e=""){super();this.name=e,this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.globalCommand=new R(this),this.globalCommand.usage("<command> [options]")}handleSignals(e){if(this.signalHandlersSet)return this;let t=async(i)=>{if(console.log(`
|
|
26
|
+
|
|
27
|
+
Received ${i}, cleaning up...`),e)try{await e()}catch(s){console.error("Error during cleanup:",s)}w.exit(0)};return w.on("SIGINT",()=>t("SIGINT")),w.on("SIGTERM",()=>t("SIGTERM")),this.signalHandlersSet=!0,this}didYouMean(e=!0){return this.enableDidYouMean=e,this}verbose(){return this.globalCommand.option("-v, --verbose","Enable verbose output"),this}quiet(){return this.globalCommand.option("-q, --quiet","Suppress non-essential output"),this}debug(){return this.globalCommand.option("--debug","Enable debug mode with detailed error information"),this}noInteraction(){return this.globalCommand.option("-n, --no-interaction","Do not ask any interactive questions (for CI/CD)"),this}env(){return this.globalCommand.option("--env <environment>","Target environment (e.g., production, staging, local)"),this}dryRun(){return this.globalCommand.option("--dry-run","Preview actions without executing them"),this}force(){return this.globalCommand.option("-f, --force","Skip confirmation prompts"),this}emoji(){return this.globalCommand.option("--no-emoji","Disable emoji in output"),this}themes(){return this.globalCommand.option("--theme <theme>","Color theme (default, dracula, nord, solarized, monokai)"),this}cache(){return this.globalCommand.option("--no-cache","Disable caching"),this}usage(e){return this.globalCommand.usage(e),this}command(e,t,i){if(!i)i={};let s=new he(e,t||"",i,this);return s.globalCommand=this.globalCommand,this.commands.push(s),s}option(e,t,i){return this.globalCommand.option(e,t,i),this}help(e){return this.globalCommand.option("-h, --help","Display this message"),this.globalCommand.helpCallback=e,this.showHelpOnExit=!0,this}version(e,t="-v, --version"){return this.globalCommand.version(e,t),this.showVersionOnExit=!0,this}example(e){return this.globalCommand.example(e),this}outputHelp(){if(this.matchedCommand)this.matchedCommand.outputHelp();else this.globalCommand.outputHelp()}outputVersion(){this.globalCommand.outputVersion()}setParsedInfo({args:e,options:t},i,s){if(this.args=e,this.options=t,i)this.matchedCommand=i;if(s)this.matchedCommandName=s;return this}unsetMatchedCommand(){this.matchedCommand=void 0,this.matchedCommandName=void 0}showCommandNotFound(e){if(console.log(S.red(`
|
|
28
|
+
✗ Command "${e}" not found.
|
|
29
|
+
`)),this.enableDidYouMean){let t=[];for(let s of this.commands){if(s.displayName)t.push(s.displayName);if(s.aliasNames)t.push(...s.aliasNames)}let i=K(e,t);if(i.length>0)console.log(S.yellow("Did you mean one of these?")),i.forEach((s)=>console.log(` ${S.dim("•")} ${this.name} ${s}`)),console.log("")}console.log(S.dim("Run"),`${this.name} --help`,S.dim("to see all available commands")),w.exit(1)}async parse(e=H,t={}){let{run:i=!0,exitOnError:s=!1}=t;if(s)try{return await this.parse(e,{run:i})}catch(r){throw this.handleUsageError(r),r}if(this.rawArgs=e,!this.name)this.name=e[1]?oe(e[1]):"cli";let n=!0,o=e.slice(2),c=o[0];if(c&&!c.startsWith("-")){for(let r of this.commands)if(r.isMatched(c)){let u=this.mri(o,r);n=!1;let h={...u,args:u.args.slice(1)};this.setParsedInfo(h,r,c),this.emit(`command:${c}`,r);break}}if(n){for(let r of this.commands)if(r.name===""){n=!1;let u=this.mri(o,r);this.setParsedInfo(u,r),this.emit("command:!",r);break}}if(n){let r=this.mri(e.slice(2));this.setParsedInfo(r)}if(this.options.verbose)this.isVerbose=!0;if(this.options.quiet)this.isQuiet=!0;if(this.options.debug)this.isDebug=!0;if(this.options.noInteraction)this.isNoInteraction=!0;if(this.options.env)this.environment=String(this.options.env);if(this.options.dryRun)this.isDryRun=!0;if(this.options.force)this.isForce=!0;if(this.options.noEmoji!==void 0)this.useEmoji=!this.options.noEmoji;if(this.options.theme)this.theme=String(this.options.theme);if(this.options.noCache!==void 0)this.isNoCache=Boolean(this.options.noCache);if(this.options.help&&this.showHelpOnExit)this.outputHelp(),i=!1,this.unsetMatchedCommand();if(this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null)this.outputVersion(),i=!1,this.unsetMatchedCommand();let a={args:this.args,options:this.options};if(i)await this.runMatchedCommand();if(!this.matchedCommand&&this.args[0]){if(this.emit("command:*"),!(this.listenerCount("command:*")>0))this.showCommandNotFound(this.args[0])}return a}mri(e,t){let i=[...this.globalCommand.options,...t?t.options:[]],s=se(i),n=[],o=e.indexOf("--");if(o>-1)n=e.slice(o+1),e=e.slice(0,o);let c=ee(e,s),a={_:c._};for(let m of Object.keys(c))if(m!=="_")a[U(m)]=c[m];let r=a._,u={"--":n},h=t&&t.config.ignoreOptionDefaultValue?t.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,d=Object.create(null);for(let m of i){if(!h&&m.config.default!==void 0)for(let f of m.names)u[f]=m.config.default;if(Array.isArray(m.config.type)){if(d[m.name]===void 0)d[m.name]={shouldTransform:!0,transformFunction:m.config.type[0]}}}for(let m of Object.keys(a))if(m!=="_"){let f=m.split(".");ne(u,f,a[m]),re(u,d)}return{args:r,options:u}}async run(e=H){return this.parse(e,{run:!0,exitOnError:!0})}handleUsageError(e){if(!(!!e&&typeof e==="object"&&e.name==="ClappError"&&e.isUsageError!==!1))return;let s=e,n=s.message??"command-line error",o=this.name?`${this.name}: `:"",c=/--help/.test(n)?"":`
|
|
30
|
+
Run \`${this.name??"cli"} --help\` for usage.`;w.stderr.write(`${o}${n}${c}
|
|
31
|
+
`),w.exit(s.exitCode??2)}async runMatchedCommand(){let{args:e,options:t,matchedCommand:i}=this;if(!i||!i.commandAction)return;i.checkUnknownOptions(),i.checkOptionValue(),i.checkRequiredArgs();let s=[];i.args.forEach((a,r)=>{if(a.variadic)s.push(e.slice(r));else s.push(e[r])}),s.push(t);let n={command:i,args:s,options:t};for(let a of i.beforeHooks)await a(n);let o,c=async()=>{let a=i.commandAction.apply(this,s);if(a instanceof Promise)o=await a;else o=a;return o};if(i.middleware.length>0){let a=0,r=async()=>{if(a<i.middleware.length){let u=i.middleware[a++];await u({...n,next:r})}else await c()};await r()}else await c();for(let a of i.afterHooks)await a(n);return o}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return w.removeAllListeners("SIGINT"),w.removeAllListeners("SIGTERM"),this.signalHandlersSet=!1,this}destroy(){this.removeSignalHandlers(),this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.matchedCommand=void 0,this.matchedCommandName=void 0,this.removeAllListeners()}}class Y{cache;enabled;cleanupInterval=null;hits=0;misses=0;constructor(){this.cache=new Map,this.enabled=!0,this.startCleanupInterval()}startCleanupInterval(){if(this.cleanupInterval)return;this.cleanupInterval=setInterval(()=>{this.cleanup()},30000),this.cleanupInterval.unref()}stopCleanup(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null}isEnabled(){return this.enabled}get(e){if(!this.enabled){this.misses++;return}let t=this.cache.get(e);if(!t){this.misses++;return}if(Date.now()-t.timestamp>t.ttl){this.cache.delete(e),this.misses++;return}return this.hits++,t.value}set(e,t,i=5000){if(!this.enabled)return;this.cache.set(e,{value:t,timestamp:Date.now(),ttl:i})}has(e){if(!this.enabled)return!1;let t=this.cache.get(e);if(!t)return!1;if(Date.now()-t.timestamp>t.ttl)return this.cache.delete(e),!1;return!0}delete(e){this.cache.delete(e)}clear(){this.cache.clear()}disable(){this.enabled=!1,this.clear()}enable(){this.enabled=!0}stats(){return{size:this.cache.size,enabled:this.enabled,hits:this.hits,misses:this.misses}}resetStats(){this.hits=0,this.misses=0}keys(){return Array.from(this.cache.keys())}cleanup(){let e=Date.now();for(let[t,i]of this.cache.entries())if(e-i.timestamp>i.ttl)this.cache.delete(t)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var at=new Y;function fe(){if("FORCE_COLOR"in O.env)return O.env.FORCE_COLOR!=="0";if("NO_COLOR"in O.env||O.env.TERM==="dumb")return!1;if(O.platform==="win32")return!0;return z.isatty(1)&&z.isatty(2)}var J=fe();function g(e,t){if(!J)return(i)=>i;return(i)=>e+i+t}var pe=g("\x1B[0m","\x1B[0m"),ge=g("\x1B[31m","\x1B[39m"),$e=g("\x1B[32m","\x1B[39m"),ve=g("\x1B[33m","\x1B[39m"),we=g("\x1B[34m","\x1B[39m"),be=g("\x1B[35m","\x1B[39m"),ye=g("\x1B[36m","\x1B[39m"),xe=g("\x1B[37m","\x1B[39m"),Ce=g("\x1B[90m","\x1B[39m"),Se=g("\x1B[1m","\x1B[22m"),Oe=g("\x1B[3m","\x1B[23m"),Ie=g("\x1B[4m","\x1B[24m"),_e=g("\x1B[2m","\x1B[22m"),Ee=g("\x1B[7m","\x1B[27m"),Be=g("\x1B[8m","\x1B[28m"),ke=g("\x1B[9m","\x1B[29m"),Te=g("\x1B[41m","\x1B[49m"),Me=g("\x1B[42m","\x1B[49m"),Ae=g("\x1B[43m","\x1B[49m"),je=g("\x1B[44m","\x1B[49m"),Re=g("\x1B[45m","\x1B[49m"),Ve=g("\x1B[46m","\x1B[49m"),De=g("\x1B[47m","\x1B[49m"),Ne=J,Le={reset:pe,red:ge,green:$e,yellow:ve,blue:we,magenta:be,cyan:ye,white:xe,gray:Ce,bold:Se,italic:Oe,underline:Ie,dim:_e,inverse:Ee,hidden:Be,strikethrough:ke,bgRed:Te,bgGreen:Me,bgYellow:Ae,bgBlue:je,bgMagenta:Re,bgCyan:Ve,bgWhite:De,isColorSupported:Ne},Pe=Le,Fe=["up","down","left","right","space","enter","cancel"],mt={actions:new Set(Fe),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var ft=We.platform.startsWith("win"),pt=Symbol("clapp:cancel");var He=ae();var p=(e,t)=>He?e:t,gt=p("◆","*"),$t=p("■","x"),vt=p("▲","x"),wt=p("◇","o"),bt=p("┌","T"),ze=p("│","|"),yt=p("└","—"),xt=p("●",">"),Ct=p("○"," "),St=p("◻","[•]"),Ot=p("◼","[+]"),It=p("◻","[ ]"),_t=p("▪","•"),Et=p("─","-"),Bt=p("╮","+"),kt=p("├","+"),Tt=p("╯","+"),Mt=p("●","•"),At=p("◆","*"),jt=p("▲","!"),Rt=p("■","x");var Vt={light:p("─","-"),heavy:p("━","="),block:p("█","#")};function Ge(){return`${Pe.gray(ze)} `}var Dt=Ge();var D="0.1.2";var y=new V("watch");y.command("detect","Detect connected watch devices").option("--verbose","Enable verbose output").action(async(e)=>{let t=l();if(e.verbose)console.log(`Searching for connected watch devices...
|
|
32
|
+
`);let i=await t.detectDevices();if(i.length===0){console.log("No watch devices found."),console.log(`
|
|
33
|
+
Make sure your watch is:`),console.log(" 1. Connected via USB, or"),console.log(" 2. Synced via Garmin Express (data will be read from cache)");return}console.log(`Found ${i.length} device(s):
|
|
34
|
+
`);for(let s of i){if(console.log(` ${s.name}`),console.log(` Type: ${s.type}`),s.isGarminExpress)console.log(" Source: Garmin Express cache");else console.log(` Path: ${s.path}`);if(s.model)console.log(` Model: ${s.model}`);if(s.serial)console.log(` Serial: ${s.serial}`);if(s.firmware)console.log(` Firmware: ${s.firmware}`);console.log()}});y.command("download","Download data from connected watch").option("-o, --output <dir>","Output directory",{default:"./watch-data"}).option("--since <date>","Only download data after this date (YYYY-MM-DD)").option("--until <date>","Only download data before this date (YYYY-MM-DD)").option("--activities","Download activity data only").option("--monitoring","Download monitoring data only").option("--no-raw","Do not copy raw FIT files").option("--verbose","Enable verbose output").action(async(e)=>{let t=l();console.log("Detecting devices...");let i=await t.detectDevices();if(i.length===0)console.error("No watch devices found."),console.error(`
|
|
35
|
+
Make sure your watch is:`),console.error(" 1. Connected via USB"),console.error(" 2. In Mass Storage mode (Settings > System > USB Mode)"),console.error(" 3. Garmin Express is closed (it may lock the device)"),process.exit(1);let s=i[0];console.log(`Found ${s.name} at ${s.path}
|
|
36
|
+
`);let n={outputDir:e.output,includeActivities:e.monitoring!==!0,includeMonitoring:e.activities!==!0,copyRawFiles:e.raw!==!1,since:e.since?new Date(e.since):void 0,until:e.until?new Date(e.until):void 0};console.log("Downloading data...");let o=await t.downloadData(s,n);if(console.log(`
|
|
37
|
+
Download complete!`),console.log(` Activities: ${o.activities.length}`),console.log(` Monitoring days: ${o.monitoring.size}`),console.log(` Raw files copied: ${o.rawFiles.length}`),o.errors.length>0){if(console.log(` Errors: ${o.errors.length}`),e.verbose)for(let c of o.errors)console.error(` - ${c.message}`)}if(o.activities.length>0&&e.verbose){console.log(`
|
|
38
|
+
Activities:`);for(let c of o.activities.slice(-10)){let a=c.startTime.toLocaleDateString(),r=Math.round(c.totalTimerTime/60),u=(c.totalDistance/1000).toFixed(2);console.log(` ${a} - ${c.sport} - ${r}min - ${u}km`)}if(o.activities.length>10)console.log(` ... and ${o.activities.length-10} more`)}});y.command("parse <file>","Parse a FIT file").option("-o, --output <file>","Output file (defaults to stdout)").option("-f, --format <format>","Output format: json, summary",{default:"summary"}).option("--verbose","Enable verbose output").action(async(e,t)=>{let i=l();try{let s=await i.parseActivityFile(e);if(!s){let n=await i.parseMonitoringFile(e);if(t.format==="json"){let o=JSON.stringify(n,null,2);if(t.output)await Bun.write(t.output,o),console.log(`Written to ${t.output}`);else console.log(o)}else{if(console.log("Monitoring Data:"),n.heartRate)console.log(` Heart Rate: avg ${n.heartRate.avgHeartRate} bpm`);if(n.sleep)console.log(` Sleep: ${Math.round(n.sleep.totalSleepTime)} minutes`);if(n.stress)console.log(` Stress: avg ${n.stress.avgStressLevel}`);if(n.spO2)console.log(` SpO2: avg ${n.spO2.avgSpO2}%`)}return}if(t.format==="json"){let n=JSON.stringify(s,null,2);if(t.output)await Bun.write(t.output,n),console.log(`Written to ${t.output}`);else console.log(n)}else{if(console.log("Activity Summary:"),console.log(` Sport: ${s.sport}${s.subSport?` (${s.subSport})`:""}`),console.log(` Date: ${s.startTime.toLocaleString()}`),console.log(` Duration: ${Math.round(s.totalTimerTime/60)} minutes`),console.log(` Distance: ${(s.totalDistance/1000).toFixed(2)} km`),console.log(` Calories: ${s.totalCalories}`),s.avgHeartRate)console.log(` Avg Heart Rate: ${s.avgHeartRate} bpm`);if(s.maxHeartRate)console.log(` Max Heart Rate: ${s.maxHeartRate} bpm`);if(s.avgSpeed){let n=16.666666666666668/s.avgSpeed;console.log(` Avg Pace: ${Math.floor(n)}:${String(Math.round(n%1*60)).padStart(2,"0")} /km`)}if(s.totalAscent)console.log(` Total Ascent: ${s.totalAscent} m`);if(s.avgCadence)console.log(` Avg Cadence: ${s.avgCadence} spm`);if(s.avgPower)console.log(` Avg Power: ${s.avgPower} W`);console.log(`
|
|
39
|
+
Laps: ${s.laps.length}`),console.log(` Data Points: ${s.records.length}`)}}catch(s){console.error(`Error parsing file: ${s instanceof Error?s.message:s}`),process.exit(1)}});y.command("watch","Watch for device connection and auto-download").option("-o, --output <dir>","Output directory",{default:"./watch-data"}).option("--interval <ms>","Check interval in milliseconds",{default:2000}).action(async(e)=>{let t=l(),i=null;console.log("Watching for device connection..."),console.log(`Press Ctrl+C to stop
|
|
40
|
+
`);let s=async()=>{let n=await t.detectDevices();if(n.length>0&&n[0].path!==i){let o=n[0];i=o.path,console.log(`
|
|
41
|
+
Device connected: ${o.name}`),console.log(`Starting download...
|
|
42
|
+
`);let c=await t.downloadData(o,{outputDir:e.output,copyRawFiles:!0});if(console.log("Download complete!"),console.log(` Activities: ${c.activities.length}`),console.log(` Monitoring days: ${c.monitoring.size}`),console.log(` Raw files: ${c.rawFiles.length}`),c.errors.length>0)console.log(` Errors: ${c.errors.length}`);console.log(`
|
|
43
|
+
Continuing to watch...`)}else if(n.length===0&&i!==null)console.log("Device disconnected"),i=null};await s(),setInterval(s,e.interval)});y.command("version","Show the version").action(()=>{console.log(D)});y.version(D);y.help();y.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var d=14,p=12,v=".FIT",t={ENUM:0,SINT8:1,UINT8:2,SINT16:131,UINT16:132,SINT32:133,UINT32:134,STRING:7,FLOAT32:136,FLOAT64:137,UINT8Z:10,UINT16Z:139,UINT32Z:140,BYTE:13,SINT64:142,UINT64:143,UINT64Z:144},C={[t.ENUM]:1,[t.SINT8]:1,[t.UINT8]:1,[t.SINT16]:2,[t.UINT16]:2,[t.SINT32]:4,[t.UINT32]:4,[t.STRING]:1,[t.FLOAT32]:4,[t.FLOAT64]:8,[t.UINT8Z]:1,[t.UINT16Z]:2,[t.UINT32Z]:4,[t.BYTE]:1,[t.SINT64]:8,[t.UINT64]:8,[t.UINT64Z]:8},r={FILE_ID:0,CAPABILITIES:1,DEVICE_SETTINGS:2,USER_PROFILE:3,HRM_PROFILE:4,SDM_PROFILE:5,BIKE_PROFILE:6,ZONES_TARGET:7,HR_ZONE:8,POWER_ZONE:9,MET_ZONE:10,SPORT:12,GOAL:15,SESSION:18,LAP:19,RECORD:20,EVENT:21,DEVICE_INFO:23,WORKOUT:26,WORKOUT_STEP:27,SCHEDULE:28,WEIGHT_SCALE:30,COURSE:31,COURSE_POINT:32,TOTALS:33,ACTIVITY:34,SOFTWARE:35,FILE_CAPABILITIES:37,MESG_CAPABILITIES:38,FIELD_CAPABILITIES:39,FILE_CREATOR:49,BLOOD_PRESSURE:51,SPEED_ZONE:53,MONITORING:55,TRAINING_FILE:72,HRV:78,ANT_RX:80,ANT_TX:81,ANT_CHANNEL_ID:82,LENGTH:101,MONITORING_INFO:103,PAD:105,SLAVE_DEVICE:106,CONNECTIVITY:127,WEATHER_CONDITIONS:128,WEATHER_ALERT:129,CADENCE_ZONE:131,HR:132,SEGMENT_LAP:142,MEMO_GLOB:145,SEGMENT_ID:148,SEGMENT_LEADERBOARD_ENTRY:149,SEGMENT_POINT:150,SEGMENT_FILE:151,WORKOUT_SESSION:158,WATCHFACE_SETTINGS:159,GPS_METADATA:160,CAMERA_EVENT:161,TIMESTAMP_CORRELATION:162,GYROSCOPE_DATA:164,ACCELEROMETER_DATA:165,THREE_D_SENSOR_CALIBRATION:167,VIDEO_FRAME:169,OBDII_DATA:174,NMEA_SENTENCE:177,AVIATION_ATTITUDE:178,VIDEO:184,VIDEO_TITLE:185,VIDEO_DESCRIPTION:186,VIDEO_CLIP:187,OHR_SETTINGS:188,EXD_SCREEN_CONFIGURATION:200,EXD_DATA_FIELD_CONFIGURATION:201,EXD_DATA_CONCEPT_CONFIGURATION:202,FIELD_DESCRIPTION:206,DEVELOPER_DATA_ID:207,MAGNETOMETER_DATA:208,BAROMETER_DATA:209,ONE_D_SENSOR_CALIBRATION:210,MONITORING_HR_DATA:211,TIME_IN_ZONE:216,SET:225,STRESS_LEVEL:227,MAX_MET_DATA:229,DIVE_SETTINGS:258,DIVE_GAS:259,DIVE_ALARM:262,EXERCISE_TITLE:264,DIVE_SUMMARY:268,SPO2_DATA:269,SLEEP_LEVEL:275,JUMP:285,BEAT_INTERVALS:290,RESPIRATION_RATE:297,SPLIT:312,CLIMB_PRO:317,TANK_UPDATE:319,TANK_SUMMARY:323,SLEEP_ASSESSMENT:346,HRV_STATUS_SUMMARY:370,HRV_VALUE:371,DEVICE_AUX_BATTERY_INFO:375,DIVE_APNEA_ALARM:393,SKIN_TEMP_OVERNIGHT:398},e={FILE_ID:{TYPE:0,MANUFACTURER:1,PRODUCT:2,SERIAL_NUMBER:3,TIME_CREATED:4,NUMBER:5,PRODUCT_NAME:8},SESSION:{TIMESTAMP:253,START_TIME:2,START_POSITION_LAT:3,START_POSITION_LONG:4,TOTAL_ELAPSED_TIME:7,TOTAL_TIMER_TIME:8,TOTAL_DISTANCE:9,TOTAL_CYCLES:10,TOTAL_CALORIES:11,TOTAL_FAT_CALORIES:13,AVG_SPEED:14,MAX_SPEED:15,AVG_HEART_RATE:16,MAX_HEART_RATE:17,AVG_CADENCE:18,MAX_CADENCE:19,AVG_POWER:20,MAX_POWER:21,TOTAL_ASCENT:22,TOTAL_DESCENT:23,SPORT:5,SUB_SPORT:6,EVENT:0,EVENT_TYPE:1,FIRST_LAP_INDEX:25,NUM_LAPS:26,NORMALIZED_POWER:34,TRAINING_STRESS_SCORE:35,INTENSITY_FACTOR:36,AVG_TEMPERATURE:57,MAX_TEMPERATURE:58},LAP:{TIMESTAMP:253,EVENT:0,EVENT_TYPE:1,START_TIME:2,START_POSITION_LAT:3,START_POSITION_LONG:4,END_POSITION_LAT:5,END_POSITION_LONG:6,TOTAL_ELAPSED_TIME:7,TOTAL_TIMER_TIME:8,TOTAL_DISTANCE:9,TOTAL_CYCLES:10,TOTAL_CALORIES:11,TOTAL_FAT_CALORIES:12,AVG_SPEED:13,MAX_SPEED:14,AVG_HEART_RATE:15,MAX_HEART_RATE:16,AVG_CADENCE:17,MAX_CADENCE:18,AVG_POWER:19,MAX_POWER:20,TOTAL_ASCENT:21,TOTAL_DESCENT:22,SPORT:23,SUB_SPORT:24,AVG_TEMPERATURE:50,MAX_TEMPERATURE:51},RECORD:{TIMESTAMP:253,POSITION_LAT:0,POSITION_LONG:1,ALTITUDE:2,HEART_RATE:3,CADENCE:4,DISTANCE:5,SPEED:6,POWER:7,COMPRESSED_SPEED_DISTANCE:8,GRADE:9,RESISTANCE:10,TIME_FROM_COURSE:11,CYCLE_LENGTH:12,TEMPERATURE:13,SPEED_1S:17,CYCLES:18,TOTAL_CYCLES:19,COMPRESSED_ACCUMULATED_POWER:28,ACCUMULATED_POWER:29,LEFT_RIGHT_BALANCE:30,GPS_ACCURACY:31,VERTICAL_SPEED:32,CALORIES:33,VERTICAL_OSCILLATION:39,STANCE_TIME_PERCENT:40,STANCE_TIME:41,ACTIVITY_TYPE:42,LEFT_TORQUE_EFFECTIVENESS:43,RIGHT_TORQUE_EFFECTIVENESS:44,LEFT_PEDAL_SMOOTHNESS:45,RIGHT_PEDAL_SMOOTHNESS:46,COMBINED_PEDAL_SMOOTHNESS:47,TIME128:48,STROKE_TYPE:49,ZONE:50,BALL_SPEED:51,CADENCE256:52,FRACTIONAL_CADENCE:53,TOTAL_HEMOGLOBIN_CONC:54,TOTAL_HEMOGLOBIN_CONC_MIN:55,TOTAL_HEMOGLOBIN_CONC_MAX:56,SATURATED_HEMOGLOBIN_PERCENT:57,SATURATED_HEMOGLOBIN_PERCENT_MIN:58,SATURATED_HEMOGLOBIN_PERCENT_MAX:59,DEVICE_INDEX:62,LEFT_PCO:67,RIGHT_PCO:68,LEFT_POWER_PHASE:69,LEFT_POWER_PHASE_PEAK:70,RIGHT_POWER_PHASE:71,RIGHT_POWER_PHASE_PEAK:72,ENHANCED_SPEED:73,ENHANCED_ALTITUDE:78,BATTERY_SOC:81,MOTOR_POWER:82,VERTICAL_RATIO:83,STANCE_TIME_BALANCE:84,STEP_LENGTH:85,ABSOLUTE_PRESSURE:91,DEPTH:92,NEXT_STOP_DEPTH:93,NEXT_STOP_TIME:94,TIME_TO_SURFACE:95,NDL_TIME:96,CNS_LOAD:97,N2_LOAD:98,RESPIRATION_RATE:99,ENHANCED_RESPIRATION_RATE:108,GRIT:114,FLOW:115,CURRENT_STRESS:116,EBIKE_TRAVEL_RANGE:117,EBIKE_BATTERY_LEVEL:118,EBIKE_ASSIST_MODE:119,EBIKE_ASSIST_LEVEL_PERCENT:120,AIR_TIME_REMAINING:123,PRESSURE_SAC:124,VOLUME_SAC:125,RMV:126,ASCENT_RATE:127,PO2:129,CORE_TEMPERATURE:139},ACTIVITY:{TIMESTAMP:253,TOTAL_TIMER_TIME:0,NUM_SESSIONS:1,TYPE:2,EVENT:3,EVENT_TYPE:4,LOCAL_TIMESTAMP:5,EVENT_GROUP:6},MONITORING:{TIMESTAMP:253,DEVICE_INDEX:0,CALORIES:1,DISTANCE:2,CYCLES:3,ACTIVE_TIME:4,ACTIVITY_TYPE:5,ACTIVITY_SUBTYPE:6,ACTIVITY_LEVEL:7,DISTANCE_16:8,CYCLES_16:9,ACTIVE_TIME_16:10,LOCAL_TIMESTAMP:11,TEMPERATURE:12,TEMPERATURE_MIN:14,TEMPERATURE_MAX:15,ACTIVITY_TIME:16,ACTIVE_CALORIES:19,CURRENT_ACTIVITY_TYPE_INTENSITY:24,TIMESTAMP_MIN_8:25,TIMESTAMP_16:26,HEART_RATE:27,INTENSITY:28,DURATION_MIN:29,DURATION:30,ASCENT:31,DESCENT:32,MODERATE_ACTIVITY_MINUTES:33,VIGOROUS_ACTIVITY_MINUTES:34},HRV:{TIME:0},STRESS_LEVEL:{STRESS_LEVEL_VALUE:0,STRESS_LEVEL_TIME:1},SPO2_DATA:{TIMESTAMP:253,READING_SPO2:0,READING_CONFIDENCE:1,MODE:2},SLEEP_LEVEL:{TIMESTAMP:253,SLEEP_LEVEL:0},RESPIRATION_RATE:{TIMESTAMP:253,RESPIRATION_RATE:0},DEVICE_INFO:{TIMESTAMP:253,DEVICE_INDEX:0,DEVICE_TYPE:1,MANUFACTURER:2,SERIAL_NUMBER:3,PRODUCT:4,SOFTWARE_VERSION:5,HARDWARE_VERSION:6,CUM_OPERATING_TIME:7,BATTERY_VOLTAGE:10,BATTERY_STATUS:11,SENSOR_POSITION:18,DESCRIPTOR:19,ANT_TRANSMISSION_TYPE:20,ANT_DEVICE_NUMBER:21,ANT_NETWORK:22,SOURCE_TYPE:25,PRODUCT_NAME:27,BATTERY_LEVEL:32}},a={GENERIC:0,RUNNING:1,CYCLING:2,TRANSITION:3,FITNESS_EQUIPMENT:4,SWIMMING:5,BASKETBALL:6,SOCCER:7,TENNIS:8,AMERICAN_FOOTBALL:9,TRAINING:10,WALKING:11,CROSS_COUNTRY_SKIING:12,ALPINE_SKIING:13,SNOWBOARDING:14,ROWING:15,MOUNTAINEERING:16,HIKING:17,MULTISPORT:18,PADDLING:19,FLYING:20,E_BIKING:21,MOTORCYCLING:22,BOATING:23,DRIVING:24,GOLF:25,HANG_GLIDING:26,HORSEBACK_RIDING:27,HUNTING:28,FISHING:29,INLINE_SKATING:30,ROCK_CLIMBING:31,SAILING:32,ICE_SKATING:33,SKY_DIVING:34,SNOWSHOEING:35,SNOWMOBILING:36,STAND_UP_PADDLEBOARDING:37,SURFING:38,WAKEBOARDING:39,WATER_SKIING:40,KAYAKING:41,RAFTING:42,WINDSURFING:43,KITESURFING:44,TACTICAL:45,JUMPMASTER:46,BOXING:47,FLOOR_CLIMBING:48,DIVING:53,HIIT:62,RACKET:64,WHEELCHAIR_PUSH_WALK:65,WHEELCHAIR_PUSH_RUN:66,MEDITATION:67,DISC_GOLF:69,CRICKET:82,RUGBY:83,HOCKEY:84,LACROSSE:85,VOLLEYBALL:86,WATER_TUBING:87,WAKESURFING:88,MIXED_MARTIAL_ARTS:105,SNORKELING:109,YOGA:123,PILATES:124,INDOOR_RUNNING:125,INDOOR_CYCLING:126,BREATHWORK:134,ALL:254},n={GENERIC:0,TREADMILL:1,STREET:2,TRAIL:3,TRACK:4,SPIN:5,INDOOR_CYCLING:6,ROAD:7,MOUNTAIN:8,DOWNHILL:9,RECUMBENT:10,CYCLOCROSS:11,HAND_CYCLING:12,TRACK_CYCLING:13,INDOOR_ROWING:14,ELLIPTICAL:15,STAIR_CLIMBING:16,LAP_SWIMMING:17,OPEN_WATER:18,FLEXIBILITY_TRAINING:19,STRENGTH_TRAINING:20,WARM_UP:21,MATCH:22,EXERCISE:23,CHALLENGE:24,INDOOR_SKIING:25,CARDIO_TRAINING:26,INDOOR_WALKING:27,E_BIKE_FITNESS:28,BMX:29,CASUAL_WALKING:30,SPEED_WALKING:31,BIKE_TO_RUN_TRANSITION:32,RUN_TO_BIKE_TRANSITION:33,SWIM_TO_BIKE_TRANSITION:34,ATV:35,MOTOCROSS:36,BACKCOUNTRY:37,RESORT:38,RC_DRONE:39,WINGSUIT:40,WHITEWATER:41,SKATE_SKIING:42,YOGA:43,PILATES:44,INDOOR_RUNNING:45,GRAVEL_CYCLING:46,E_BIKE_MOUNTAIN:47,COMMUTING:48,MIXED_SURFACE:49,NAVIGATE:50,TRACK_ME:51,MAP:52,SINGLE_GAS_DIVING:53,MULTI_GAS_DIVING:54,GAUGE_DIVING:55,APNEA_DIVING:56,APNEA_HUNTING:57,VIRTUAL_ACTIVITY:58,OBSTACLE:59,BREATHING:62,SAIL_RACE:65,ULTRA:67,INDOOR_CLIMBING:68,BOULDERING:69,HIIT:70,AMRAP:73,EMOM:74,TABATA:75,PICKLEBALL:84,PADEL:85,INDOOR_WHEELCHAIR_WALK:86,INDOOR_WHEELCHAIR_RUN:87,INDOOR_HAND_CYCLING:88,FLY:110,ALL:254},u={UNMEASURABLE:0,AWAKE:1,LIGHT:2,DEEP:3,REM:4},T=Date.UTC(1989,11,31,0,0,0),D={GARMIN:1,GARMIN_FR405_ANTFS:2,ZEPHYR:3,DAYTON:4,IDT:5,SRM:6,QUARQ:7,IBIKE:8,SARIS:9,SPARK_HK:10,TANITA:11,ECHOWELL:12,DYNASTREAM_OEM:13,NAUTILUS:14,DYNASTREAM:15,TIMEX:16,METRIGEAR:17,XELIC:18,BEURER:19,CARDIOSPORT:20,A_AND_D:21,HMM:22,SUUNTO:23,THITA_ELEKTRONIK:24,GPULSE:25,CLEAN_MOBILE:26,PEDAL_BRAIN:27,PEAKSWARE:28,SAXONAR:29,LEMOND_FITNESS:30,DEXCOM:31,WAHOO_FITNESS:32,OCTANE_FITNESS:33,ARCHINOETICS:34,THE_HURT_BOX:35,CITIZEN_SYSTEMS:36,MAGELLAN:37,OSYNCE:38,HOLUX:39,CONCEPT2:40,SHIMANO:72,PIONEER:86,BRYTON:267,ZWIFT:260,WAHOO:32,TACX:89};class m extends Error{offset;constructor(A,I){super(I!==void 0?`${A} at offset ${I}`:A);this.offset=I;this.name="FitParseError"}}class i{buffer;offset=0;definitions=new Map;littleEndian=!0;constructor(A){if(A instanceof ArrayBuffer)this.buffer=Buffer.from(A);else if(A instanceof Uint8Array)this.buffer=Buffer.from(A);else this.buffer=A}parse(){let A=this.parseHeader(),I=[],f=A.headerSize+A.dataSize;while(this.offset<f)try{let l=this.parseRecord();if(l)I.push(l)}catch(l){if(l instanceof m)this.offset++;else throw l}return{header:A,messages:I}}parseHeader(){if(this.buffer.length<p)throw new m("File too small to contain FIT header");let A=this.buffer.readUInt8(0);if(A!==d&&A!==p)throw new m(`Invalid header size: ${A}`);let I=this.buffer.readUInt8(1),f=this.buffer.readUInt16LE(2),l=this.buffer.readUInt32LE(4),N=this.buffer.toString("ascii",8,12);if(N!==v)throw new m(`Invalid FIT signature: ${N}`);let E;if(A===d)E=this.buffer.readUInt16LE(12);return this.offset=A,{headerSize:A,protocolVersion:I,profileVersion:f,dataSize:l,dataType:N,crc:E}}parseRecord(){if(this.offset>=this.buffer.length)return null;let A=this.buffer.readUInt8(this.offset);if(this.offset++,(A&128)===128){let E=A>>5&3,L=A&31;return this.parseDataMessage(E,L)}let f=(A&64)===64,l=(A&32)===32,N=A&15;if(f)return this.parseDefinitionMessage(N,l),null;return this.parseDataMessage(N)}parseDefinitionMessage(A,I){let f=this.buffer.readUInt8(this.offset);this.offset++;let l=this.buffer.readUInt8(this.offset);this.offset++,this.littleEndian=l===0;let N=this.readUInt16(),E=this.buffer.readUInt8(this.offset);this.offset++;let L=[];for(let S=0;S<E;S++){let O=this.buffer.readUInt8(this.offset);this.offset++;let _=this.buffer.readUInt8(this.offset);this.offset++;let P=this.buffer.readUInt8(this.offset);this.offset++,L.push({fieldDefNum:O,size:_,baseType:P})}if(I){let S=this.buffer.readUInt8(this.offset);this.offset++,this.offset+=S*3}this.definitions.set(A,{reserved:f,arch:this.littleEndian?"little":"big",globalMsgNum:N,numFields:E,fields:L})}parseDataMessage(A,I){let f=this.definitions.get(A);if(!f)return null;let l={};for(let N of f.fields){let E=this.readFieldValue(N);if(E!==null&&E!==void 0)l[N.fieldDefNum]=E}return{globalMsgNum:f.globalMsgNum,fields:l}}readFieldValue(A){let{size:I,baseType:f}=A;switch(f&31){case t.ENUM:case t.UINT8:case t.UINT8Z:{if(I===1){let E=this.buffer.readUInt8(this.offset);return this.offset++,E===255?null:E}let N=[];for(let E=0;E<I;E++)N.push(this.buffer.readUInt8(this.offset)),this.offset++;return N}case t.SINT8:{if(I===1){let E=this.buffer.readInt8(this.offset);return this.offset++,E===127?null:E}let N=[];for(let E=0;E<I;E++)N.push(this.buffer.readInt8(this.offset)),this.offset++;return N}case t.UINT16:case t.UINT16Z:{if(I===2){let E=this.readUInt16();return E===65535?null:E}let N=[];for(let E=0;E<I/2;E++)N.push(this.readUInt16());return N}case t.SINT16:{if(I===2){let E=this.readInt16();return E===32767?null:E}let N=[];for(let E=0;E<I/2;E++)N.push(this.readInt16());return N}case t.UINT32:case t.UINT32Z:{if(I===4){let E=this.readUInt32();return E===4294967295?null:E}let N=[];for(let E=0;E<I/4;E++)N.push(this.readUInt32());return N}case t.SINT32:{if(I===4){let E=this.readInt32();return E===2147483647?null:E}let N=[];for(let E=0;E<I/4;E++)N.push(this.readInt32());return N}case t.FLOAT32:{if(I===4){let E=this.readFloat32();return Number.isNaN(E)?null:E}let N=[];for(let E=0;E<I/4;E++)N.push(this.readFloat32());return N}case t.FLOAT64:{if(I===8){let E=this.readFloat64();return Number.isNaN(E)?null:E}let N=[];for(let E=0;E<I/8;E++)N.push(this.readFloat64());return N}case t.STRING:{let N=this.buffer.slice(this.offset,this.offset+I);this.offset+=I;let E=N.indexOf(0),L=N.toString("utf8",0,E>=0?E:I);return L.length>0?L:null}case t.UINT64:case t.UINT64Z:{if(I===8)return this.readUInt64();return this.offset+=I,null}case t.SINT64:{if(I===8)return this.readInt64();return this.offset+=I,null}case t.BYTE:default:{let N=this.buffer.slice(this.offset,this.offset+I);return this.offset+=I,N}}}readUInt16(){let A=this.littleEndian?this.buffer.readUInt16LE(this.offset):this.buffer.readUInt16BE(this.offset);return this.offset+=2,A}readInt16(){let A=this.littleEndian?this.buffer.readInt16LE(this.offset):this.buffer.readInt16BE(this.offset);return this.offset+=2,A}readUInt32(){let A=this.littleEndian?this.buffer.readUInt32LE(this.offset):this.buffer.readUInt32BE(this.offset);return this.offset+=4,A}readInt32(){let A=this.littleEndian?this.buffer.readInt32LE(this.offset):this.buffer.readInt32BE(this.offset);return this.offset+=4,A}readFloat32(){let A=this.littleEndian?this.buffer.readFloatLE(this.offset):this.buffer.readFloatBE(this.offset);return this.offset+=4,A}readFloat64(){let A=this.littleEndian?this.buffer.readDoubleLE(this.offset):this.buffer.readDoubleBE(this.offset);return this.offset+=8,A}readUInt64(){let A=this.littleEndian?this.buffer.readBigUInt64LE(this.offset):this.buffer.readBigUInt64BE(this.offset);return this.offset+=8,A}readInt64(){let A=this.littleEndian?this.buffer.readBigInt64LE(this.offset):this.buffer.readBigInt64BE(this.offset);return this.offset+=8,A}}function o(A){return new Date(T+A*1000)}function c(A){return A*(180/Math.pow(2,31))}function R(A){return A*(Math.pow(2,31)/180)}class s{messages;fileType="unknown";deviceInfo=null;constructor(A){this.messages=A.messages,this.extractFileInfo()}extractFileInfo(){let A=this.messages.find((f)=>f.globalMsgNum===r.FILE_ID);if(A){let f=A.fields[e.FILE_ID.TYPE];this.fileType=this.resolveFileType(f)}let I=this.messages.find((f)=>f.globalMsgNum===r.DEVICE_INFO);if(I)this.deviceInfo={manufacturer:I.fields[e.DEVICE_INFO.MANUFACTURER],product:I.fields[e.DEVICE_INFO.PRODUCT],serialNumber:I.fields[e.DEVICE_INFO.SERIAL_NUMBER],softwareVersion:I.fields[e.DEVICE_INFO.SOFTWARE_VERSION],productName:I.fields[e.DEVICE_INFO.PRODUCT_NAME]}}resolveFileType(A){return{1:"device",2:"settings",3:"sport",4:"activity",5:"workout",6:"course",7:"schedules",9:"weight",10:"totals",11:"goals",14:"blood_pressure",15:"monitoring_a",20:"activity_summary",28:"monitoring_daily",32:"monitoring_b",34:"segment",35:"segment_list",40:"exd_configuration"}[A]||"unknown"}getFileType(){return this.fileType}getDeviceInfo(){return this.deviceInfo}decodeActivity(){if(!["activity","activity_summary"].includes(this.fileType))return null;let A=this.messages.filter((_)=>_.globalMsgNum===r.SESSION),I=this.messages.filter((_)=>_.globalMsgNum===r.LAP),f=this.messages.filter((_)=>_.globalMsgNum===r.RECORD),l=this.messages.find((_)=>_.globalMsgNum===r.ACTIVITY);if(A.length===0)return null;let E=A[0].fields,L=E[e.SESSION.START_TIME],S=E[e.SESSION.TIMESTAMP],O={id:this.generateActivityId(L),sport:this.mapSport(E[e.SESSION.SPORT]),subSport:this.mapSubSport(E[e.SESSION.SUB_SPORT]),startTime:o(L),endTime:o(S),totalElapsedTime:E[e.SESSION.TOTAL_ELAPSED_TIME]/1000,totalTimerTime:E[e.SESSION.TOTAL_TIMER_TIME]/1000,totalDistance:E[e.SESSION.TOTAL_DISTANCE]/100,totalCalories:E[e.SESSION.TOTAL_CALORIES],avgHeartRate:E[e.SESSION.AVG_HEART_RATE],maxHeartRate:E[e.SESSION.MAX_HEART_RATE],avgSpeed:E[e.SESSION.AVG_SPEED]!=null?E[e.SESSION.AVG_SPEED]/1000:void 0,maxSpeed:E[e.SESSION.MAX_SPEED]!=null?E[e.SESSION.MAX_SPEED]/1000:void 0,avgCadence:E[e.SESSION.AVG_CADENCE],maxCadence:E[e.SESSION.MAX_CADENCE],avgPower:E[e.SESSION.AVG_POWER],maxPower:E[e.SESSION.MAX_POWER],normalizedPower:E[e.SESSION.NORMALIZED_POWER],totalAscent:E[e.SESSION.TOTAL_ASCENT],totalDescent:E[e.SESSION.TOTAL_DESCENT],trainingStressScore:E[e.SESSION.TRAINING_STRESS_SCORE]!=null?E[e.SESSION.TRAINING_STRESS_SCORE]/10:void 0,intensityFactor:E[e.SESSION.INTENSITY_FACTOR]!=null?E[e.SESSION.INTENSITY_FACTOR]/1000:void 0,laps:I.map((_)=>this.decodeLap(_)),records:f.map((_)=>this.decodeRecord(_)),source:"garmin"};return this.cleanUndefined(O)}decodeLap(A){let I=A.fields,f=I[e.LAP.START_TIME],l=I[e.LAP.TIMESTAMP],N=I[e.LAP.START_POSITION_LAT],E=I[e.LAP.START_POSITION_LONG],L=I[e.LAP.END_POSITION_LAT],S=I[e.LAP.END_POSITION_LONG],O={startTime:o(f),endTime:o(l),totalElapsedTime:I[e.LAP.TOTAL_ELAPSED_TIME]/1000,totalTimerTime:I[e.LAP.TOTAL_TIMER_TIME]/1000,totalDistance:I[e.LAP.TOTAL_DISTANCE]/100,totalCalories:I[e.LAP.TOTAL_CALORIES],avgHeartRate:I[e.LAP.AVG_HEART_RATE],maxHeartRate:I[e.LAP.MAX_HEART_RATE],avgSpeed:I[e.LAP.AVG_SPEED]!=null?I[e.LAP.AVG_SPEED]/1000:void 0,maxSpeed:I[e.LAP.MAX_SPEED]!=null?I[e.LAP.MAX_SPEED]/1000:void 0,avgCadence:I[e.LAP.AVG_CADENCE],maxCadence:I[e.LAP.MAX_CADENCE],avgPower:I[e.LAP.AVG_POWER],maxPower:I[e.LAP.MAX_POWER],totalAscent:I[e.LAP.TOTAL_ASCENT],totalDescent:I[e.LAP.TOTAL_DESCENT],startPosition:N!=null&&E!=null?{lat:c(N),lng:c(E)}:void 0,endPosition:L!=null&&S!=null?{lat:c(L),lng:c(S)}:void 0};return this.cleanUndefined(O)}decodeRecord(A){let I=A.fields,f=I[e.RECORD.TIMESTAMP],l=I[e.RECORD.POSITION_LAT],N=I[e.RECORD.POSITION_LONG],E=I[e.RECORD.ENHANCED_ALTITUDE];if(E==null){if(E=I[e.RECORD.ALTITUDE],E!=null)E=E/5-500}else E=E/5-500;let L=I[e.RECORD.ENHANCED_SPEED];if(L==null){if(L=I[e.RECORD.SPEED],L!=null)L=L/1000}else L=L/1000;let S={timestamp:o(f),position:l!=null&&N!=null?{lat:c(l),lng:c(N),altitude:E}:void 0,heartRate:I[e.RECORD.HEART_RATE],cadence:I[e.RECORD.CADENCE],speed:L,power:I[e.RECORD.POWER],altitude:E,distance:I[e.RECORD.DISTANCE]!=null?I[e.RECORD.DISTANCE]/100:void 0,temperature:I[e.RECORD.TEMPERATURE],grade:I[e.RECORD.GRADE]!=null?I[e.RECORD.GRADE]/100:void 0,calories:I[e.RECORD.CALORIES]};return this.cleanUndefined(S)}decodeMonitoring(){let A={},I=this.messages.filter((S)=>S.globalMsgNum===r.MONITORING);if(I.length>0){let S=[],O=1/0,_=0,P=0,h=0;for(let M of I){let b=M.fields[e.MONITORING.HEART_RATE],U=M.fields[e.MONITORING.TIMESTAMP];if(b!=null&&U!=null)S.push({timestamp:o(U),heartRate:b}),O=Math.min(O,b),_=Math.max(_,b),P+=b,h++}if(S.length>0)A.heartRate={date:S[0].timestamp,minHeartRate:O,maxHeartRate:_,avgHeartRate:Math.round(P/h),samples:S}}let f=this.messages.filter((S)=>S.globalMsgNum===r.SLEEP_LEVEL);if(f.length>0){let S=[],O=null;for(let _ of f){let P=_.fields[e.SLEEP_LEVEL.SLEEP_LEVEL],h=_.fields[e.SLEEP_LEVEL.TIMESTAMP];if(P!=null&&h!=null){let M=this.mapSleepLevel(P),b=o(h);if(O&&O.stage!==M)O.endTime=b,S.push(O),O={stage:M,startTime:b,endTime:b};else if(!O)O={stage:M,startTime:b,endTime:b}}}if(O)S.push(O);if(S.length>0){let _=S[0].startTime,P=S[S.length-1].endTime,h=0,M=0,b=0,U=0;for(let g of S){let G=(g.endTime.getTime()-g.startTime.getTime())/60000;switch(g.stage){case"deep":h+=G;break;case"light":M+=G;break;case"rem":b+=G;break;case"awake":U+=G;break}}A.sleep={date:_,startTime:_,endTime:P,totalSleepTime:h+M+b,deepSleepTime:h,lightSleepTime:M,remSleepTime:b,awakeTime:U,stages:S}}}let l=this.messages.filter((S)=>S.globalMsgNum===r.STRESS_LEVEL);if(l.length>0){let S=[],O=0,_=0;for(let P of l){let h=P.fields[e.STRESS_LEVEL.STRESS_LEVEL_VALUE],M=P.fields[e.STRESS_LEVEL.STRESS_LEVEL_TIME];if(h!=null&&h>=0&&h<=100&&M!=null)S.push({timestamp:o(M),stressLevel:h}),O+=h,_=Math.max(_,h)}if(S.length>0)A.stress={date:S[0].timestamp,avgStressLevel:Math.round(O/S.length),maxStressLevel:_,restStressDuration:0,lowStressDuration:0,mediumStressDuration:0,highStressDuration:0,samples:S}}let N=this.messages.filter((S)=>S.globalMsgNum===r.SPO2_DATA);if(N.length>0){let S=[],O=100,_=0,P=0;for(let h of N){let M=h.fields[e.SPO2_DATA.READING_SPO2],b=h.fields[e.SPO2_DATA.TIMESTAMP];if(M!=null&&b!=null&&M>0&&M<=100)S.push({timestamp:o(b),spO2:M}),O=Math.min(O,M),_=Math.max(_,M),P+=M}if(S.length>0)A.spO2={date:S[0].timestamp,avgSpO2:Math.round(P/S.length),minSpO2:O,maxSpO2:_,samples:S}}let E=this.messages.filter((S)=>S.globalMsgNum===r.RESPIRATION_RATE);if(E.length>0){let S=[],O=1/0,_=0,P=0;for(let h of E){let M=h.fields[e.RESPIRATION_RATE.RESPIRATION_RATE],b=h.fields[e.RESPIRATION_RATE.TIMESTAMP];if(M!=null&&b!=null&&M>0){let U=M/100;S.push({timestamp:o(b),respirationRate:U}),O=Math.min(O,U),_=Math.max(_,U),P+=U}}if(S.length>0)A.respiration={date:S[0].timestamp,avgRespirationRate:P/S.length,minRespirationRate:O,maxRespirationRate:_,samples:S}}let L=this.messages.filter((S)=>S.globalMsgNum===r.HRV);if(L.length>0){let S=[];for(let O of L){let _=O.fields[e.HRV.TIME];if(Array.isArray(_)){for(let P of _)if(P!=null&&P!==65535)S.push({timestamp:new Date,hrv:P/1024*1000})}else if(_!=null&&_!==65535)S.push({timestamp:new Date,hrv:_/1024*1000})}if(S.length>0)A.hrv={date:new Date,samples:S}}return A}mapSport(A){return{[a.RUNNING]:"running",[a.CYCLING]:"cycling",[a.SWIMMING]:"swimming",[a.HIKING]:"hiking",[a.WALKING]:"walking",[a.TRAINING]:"strength_training",[a.FITNESS_EQUIPMENT]:"cardio",[a.YOGA]:"yoga",[a.GENERIC]:"generic"}[A]||"other"}mapSubSport(A){return{[n.GENERIC]:"generic",[n.TREADMILL]:"treadmill",[n.STREET]:"street",[n.TRAIL]:"trail",[n.TRACK]:"track",[n.SPIN]:"spin",[n.INDOOR_CYCLING]:"indoor_cycling",[n.ROAD]:"road",[n.MOUNTAIN]:"mountain",[n.GRAVEL_CYCLING]:"gravel",[n.LAP_SWIMMING]:"lap_swimming",[n.OPEN_WATER]:"open_water"}[A]}mapSleepLevel(A){return{[u.AWAKE]:"awake",[u.LIGHT]:"light",[u.DEEP]:"deep",[u.REM]:"rem"}[A]||"light"}generateActivityId(A){return`garmin_${A}`}cleanUndefined(A){let I={};for(let[f,l]of Object.entries(A))if(l!==void 0&&l!==null)I[f]=l;return I}}export{d,p,v,t,C,r,e,a,n,u,T,D,m,i,o,c,R,s};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i,s}from"./chunk-79g2tyda.js";import{existsSync as A,readdirSync as E,statSync as N,mkdirSync as T,copyFileSync as P,readFileSync as O}from"fs";import{join as t,extname as L}from"path";import{homedir as U}from"os";var F="/Volumes",x=t(U(),"Library/Application Support/Garmin/Express/RegisteredDevices"),W=["GARMIN","ENDURO","FENIX","FORERUNNER","VENU","INSTINCT","VIVOACTIVE","EPIX","TACTIX","MARQ","QUATIX","DESCENT","APPROACH","LILY","VIVOSMART","VIVOMOVE"],C={ACTIVITY:"GARMIN/Activity",MONITOR:"GARMIN/Monitor",SPORTS:"GARMIN/Sports",SETTINGS:"GARMIN/Settings",TOTALS:"GARMIN/Totals",RECORDS:"GARMIN/Records",METRICS:"GARMIN/Metrics",SLEEP:"GARMIN/Sleep",WORKOUTS:"GARMIN/Workouts",COURSES:"GARMIN/Courses",LOCATIONS:"GARMIN/Locations",GOALS:"GARMIN/Goals",SCHEDULES:"GARMIN/Schedules"};class b{name="Garmin";type="garmin";async detectDevices(){let a=[];if(A(F)){let n=E(F);for(let u of n){let S=t(F,u);try{if(!N(S).isDirectory())continue}catch{continue}let v=u.toUpperCase(),g=W.some((p)=>v.includes(p));if(!g){let p=t(S,"GARMIN");g=A(p)}if(g){let p=await this.getDeviceInfo(S,u);if(p)a.push(p)}}}let D=await this.detectGarminExpressDevices();return a.push(...D),a}async detectGarminExpressDevices(){let a=[];if(!A(x))return a;let D=E(x);for(let n of D){let u=t(x,n);try{if(!N(u).isDirectory())continue}catch{continue}let S=t(u,"GarminDevice.xml");if(!A(S))continue;try{let v=O(S,"utf8"),g=v.match(/<Description>([^<]+)<\/Description>/i),p=v.match(/<Id>([^<]+)<\/Id>/i),I=v.match(/<DisplayName>([^<]+)<\/DisplayName>/i),r=v.match(/<PartNumber>([^<]+)<\/PartNumber>/i),w=v.match(/<SoftwareVersion>([^<]+)<\/SoftwareVersion>/i),M=I?.[1]||g?.[1]||n,m=p?.[1]||n,o=w?.[1],e=t(u,"PendingSyncUploads/Garmin");if(!A(e))continue;a.push({name:M,path:u,type:"garmin",model:M,serial:n,unitId:m,firmware:o,partNumber:r?.[1],isGarminExpress:!0,garminExpressPath:e})}catch{}}return a}async getDeviceInfo(a,D){let n=t(a,"GARMIN");if(!A(n))return null;let u=D,S,v,g=t(n,"GarminDevice.xml");if(A(g))try{let I=O(g,"utf8"),r=I.match(/<Model>([^<]+)<\/Model>/i),w=I.match(/<Id>([^<]+)<\/Id>/i);if(r)u=r[1];if(w)v=w[1]}catch{}let p=t(a,C.ACTIVITY);if(A(p)){let I=E(p).filter((r)=>r.toLowerCase().endsWith(".fit"));if(I.length>0)try{let r=t(p,I[0]),w=O(r),m=new i(w).parse(),e=new s(m).getDeviceInfo();if(e?.serialNumber)S=e.serialNumber.toString();if(e?.productName)u=e.productName}catch{}}return{name:u,path:a,type:"garmin",model:u,serial:S,unitId:v}}async downloadData(a,D={}){let n=a,{outputDir:u="./garmin-data",includeActivities:S=!0,includeMonitoring:v=!0,since:g,until:p,copyRawFiles:I=!0}=D,r={device:a,activities:[],monitoring:new Map,rawFiles:[],errors:[]},w=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),M=t(u,w);if(I)T(M,{recursive:!0});if(n.isGarminExpress&&n.garminExpressPath)return this.downloadFromGarminExpress(n,D,r,M);if(S){let m=t(a.path,C.ACTIVITY);if(A(m)){let o=E(m).filter((e)=>e.toLowerCase().endsWith(".fit"));for(let e of o){let c=t(m,e);try{let f=N(c).mtime;if(g&&f<g)continue;if(p&&f>p)continue;let d=await this.parseActivityFile(c);if(d)d.rawFilePath=c,r.activities.push(d);if(I){let y=t(M,"Activity");T(y,{recursive:!0});let R=t(y,e);P(c,R),r.rawFiles.push(R)}}catch(h){r.errors.push(h instanceof Error?h:Error(String(h)))}}}}if(v){let m=t(a.path,C.MONITOR);if(A(m)){let e=E(m).filter((c)=>c.toLowerCase().endsWith(".fit"));for(let c of e){let h=t(m,c);try{let d=N(h).mtime;if(g&&d<g)continue;if(p&&d>p)continue;let y=await this.parseMonitoringFile(h);if(y){let R=d.toISOString().slice(0,10),G=r.monitoring.get(R)||{};r.monitoring.set(R,{...G,...y})}if(I){let R=t(M,"Monitor");T(R,{recursive:!0});let G=t(R,c);P(h,G),r.rawFiles.push(G)}}catch(f){r.errors.push(f instanceof Error?f:Error(String(f)))}}}let o=t(a.path,"GARMIN/Sleep");if(A(o)){let e=E(o).filter((c)=>c.toLowerCase().endsWith(".fit"));for(let c of e){let h=t(o,c);try{let f=await this.parseMonitoringFile(h);if(f?.sleep){let d=f.sleep.date.toISOString().slice(0,10),y=r.monitoring.get(d)||{};r.monitoring.set(d,{...y,sleep:f.sleep})}if(I){let d=t(M,"Sleep");T(d,{recursive:!0});let y=t(d,c);P(h,y),r.rawFiles.push(y)}}catch(f){r.errors.push(f instanceof Error?f:Error(String(f)))}}}}return r.activities.sort((m,o)=>m.startTime.getTime()-o.startTime.getTime()),r}async downloadFromGarminExpress(a,D,n,u){let{includeActivities:S=!0,includeMonitoring:v=!0,since:g,until:p,copyRawFiles:I=!0}=D,r=a.garminExpressPath,w={Activity:t(r,"Activity"),Monitor:t(r,"Monitor"),Sleep:t(r,"Sleep"),HRVStatus:t(r,"HRVStatus"),SkinTemp:t(r,"SkinTemp"),Metrics:t(r,"Metrics"),Coach:t(r,"Coach")},M=async(m,o,e)=>{if(!A(m))return;let c=E(m).filter((h)=>h.toLowerCase().endsWith(".fit"));for(let h of c){let f=t(m,h);try{let y=N(f).mtime;if(g&&y<g)continue;if(p&&y>p)continue;let R=await this.parseMonitoringFile(f);if(R)o(f,R);if(I){let G=t(u,e);T(G,{recursive:!0});let V=t(G,h);P(f,V),n.rawFiles.push(V)}}catch(d){n.errors.push(d instanceof Error?d:Error(String(d)))}}};if(S&&A(w.Activity)){let m=E(w.Activity).filter((o)=>o.toLowerCase().endsWith(".fit"));for(let o of m){let e=t(w.Activity,o);try{let h=N(e).mtime;if(g&&h<g)continue;if(p&&h>p)continue;let f=await this.parseActivityFile(e);if(f)f.rawFilePath=e,n.activities.push(f);if(I){let d=t(u,"Activity");T(d,{recursive:!0});let y=t(d,o);P(e,y),n.rawFiles.push(y)}}catch(c){n.errors.push(c instanceof Error?c:Error(String(c)))}}}if(v)await M(w.Monitor,(m,o)=>{let e=new Date().toISOString().slice(0,10),c=n.monitoring.get(e)||{};n.monitoring.set(e,{...c,...o})},"Monitor"),await M(w.HRVStatus,(m,o)=>{if(o.hrv){let e=new Date().toISOString().slice(0,10),c=n.monitoring.get(e)||{};n.monitoring.set(e,{...c,hrv:o.hrv})}},"HRVStatus"),await M(w.SkinTemp,(m,o)=>{let e=new Date().toISOString().slice(0,10),c=n.monitoring.get(e)||{};n.monitoring.set(e,{...c})},"SkinTemp"),await M(w.Sleep,(m,o)=>{if(o.sleep){let e=o.sleep.date?.toISOString().slice(0,10)||new Date().toISOString().slice(0,10),c=n.monitoring.get(e)||{};n.monitoring.set(e,{...c,sleep:o.sleep})}},"Sleep");return n.activities.sort((m,o)=>m.startTime.getTime()-o.startTime.getTime()),n}async parseActivityFile(a){let D=L(a).toLowerCase();if(D!==".fit")throw Error(`Unsupported file format: ${D}`);let n=O(a),S=new i(n).parse();return new s(S).decodeActivity()}async parseMonitoringFile(a){let D=L(a).toLowerCase();if(D!==".fit")throw Error(`Unsupported file format: ${D}`);let n=O(a),S=new i(n).parse();return new s(S).decodeMonitoring()}}function l(){return new b}
|
|
2
|
+
export{b,l};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i,s}from"./chunk-79g2tyda.js";import{existsSync as Pt,readdirSync as ot,statSync as at,readFileSync as j,mkdirSync as Ft,copyFileSync as st}from"fs";import{join as b,extname as Mt}from"path";var Q="/Volumes",Ht=["POLAR","VANTAGE","GRIT","IGNITE","PACER","UNITE"];function ct(e){let t=e.split(`
|
|
2
|
+
`).map((T)=>T.trim()),r=new Map,o="";for(let T of t)if(T.startsWith("[")&&T.endsWith("]"))o=T.slice(1,-1),r.set(o,[]);else if(o&&T)r.get(o)?.push(T);let c=r.get("Params")||[],p=r.get("HRData")||[];if(c.length===0)return null;let n=new Map;for(let T of c){let[R,k]=T.split("=");if(R&&k)n.set(R.trim(),k.trim())}let a=n.get("Date"),F=n.get("StartTime"),d=parseInt(n.get("Interval")||"1",10),u=n.get("Length");if(!a||!F)return null;let l=parseInt(a.slice(0,4),10),m=parseInt(a.slice(4,6),10)-1,D=parseInt(a.slice(6,8),10),[P,M,U]=F.split(":"),W=parseFloat(U||"0"),C=new Date(l,m,D,parseInt(P,10),parseInt(M,10),Math.floor(W)),O=0;if(u){let[T,R,k]=u.split(":").map(Number);O=T*3600+R*60+k}let L=[],K=new Date(C);for(let T of p){let R=T.split("\t").map((B)=>parseInt(B,10)),k=R[0];if(k>0)L.push({timestamp:new Date(K),heartRate:k,speed:R[1]?R[1]/10:void 0,cadence:R[2],altitude:R[3],power:R[4]});K=new Date(K.getTime()+d*1000)}let V=L.filter((T)=>T.heartRate).map((T)=>T.heartRate),v=V.length>0?Math.round(V.reduce((T,R)=>T+R,0)/V.length):void 0,H=V.length>0?Math.max(...V):void 0;return{id:`polar_${C.getTime()}`,sport:"other",startTime:C,endTime:new Date(C.getTime()+O*1000),totalElapsedTime:O,totalTimerTime:O,totalDistance:0,totalCalories:parseInt(n.get("Calories")||"0",10),avgHeartRate:v,maxHeartRate:H,laps:[],records:L,source:"garmin"}}function lt(e){try{let t=JSON.parse(e);if(!t.startTime)return null;let r=new Date(t.startTime),o=t.stopTime?new Date(t.stopTime):new Date(r.getTime()+(t.duration||0)),c=[];if(t.samples)for(let n of t.samples)c.push({timestamp:new Date(n.dateTime||r),heartRate:n.heartRate,speed:n.speed,cadence:n.cadence,altitude:n.altitude,distance:n.distance,power:n.power,position:n.latitude&&n.longitude?{lat:n.latitude,lng:n.longitude,altitude:n.altitude}:void 0});let p=[];if(t.laps)for(let n of t.laps)p.push({startTime:new Date(n.startTime||r),endTime:new Date(n.stopTime||o),totalElapsedTime:n.duration||0,totalTimerTime:n.duration||0,totalDistance:n.distance||0,totalCalories:n.calories||0,avgHeartRate:n.heartRate?.average,maxHeartRate:n.heartRate?.maximum,avgSpeed:n.speed?.average,maxSpeed:n.speed?.maximum,avgCadence:n.cadence?.average,maxCadence:n.cadence?.maximum});return{id:`polar_${t.id||r.getTime()}`,name:t.name,sport:Wt(t.sport),startTime:r,endTime:o,totalElapsedTime:t.duration||(o.getTime()-r.getTime())/1000,totalTimerTime:t.duration||(o.getTime()-r.getTime())/1000,totalDistance:t.distance||0,totalCalories:t.calories||0,avgHeartRate:t.heartRate?.average,maxHeartRate:t.heartRate?.maximum,avgSpeed:t.speed?.average,maxSpeed:t.speed?.maximum,avgCadence:t.cadence?.average,maxCadence:t.cadence?.maximum,avgPower:t.power?.average,maxPower:t.power?.maximum,totalAscent:t.ascent,totalDescent:t.descent,laps:p,records:c,source:"garmin"}}catch{return null}}function Wt(e){if(!e)return"other";return{RUNNING:"running",CYCLING:"cycling",SWIMMING:"swimming",WALKING:"walking",HIKING:"hiking",STRENGTH_TRAINING:"strength_training",OTHER:"other"}[e.toUpperCase()]||"other"}class h{name="Polar";type="polar";async detectDevices(){let e=[];if(!Pt(Q))return e;let t=ot(Q);for(let r of t){let o=b(Q,r);try{if(!at(o).isDirectory())continue}catch{continue}let c=r.toUpperCase();if(Ht.some((n)=>c.includes(n)))e.push({name:r,path:o,type:"polar",model:r})}return e}async downloadData(e,t={}){let{outputDir:r="./polar-data",copyRawFiles:o=!0}=t,c={device:e,activities:[],monitoring:new Map,rawFiles:[],errors:[]},p=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),n=b(r,p);if(o)Ft(n,{recursive:!0});let a=this.findFiles(e.path,".hrm");for(let d of a)try{let u=j(d,"utf-8"),l=ct(u);if(l)l.rawFilePath=d,c.activities.push(l);if(o){let m=b(n,d.split("/").pop());st(d,m),c.rawFiles.push(m)}}catch(u){c.errors.push(u instanceof Error?u:Error(String(u)))}let F=this.findFiles(e.path,".json");for(let d of F)try{let u=j(d,"utf-8"),l=lt(u);if(l)l.rawFilePath=d,c.activities.push(l);if(o){let m=b(n,d.split("/").pop());st(d,m),c.rawFiles.push(m)}}catch(u){c.errors.push(u instanceof Error?u:Error(String(u)))}return c.activities.sort((d,u)=>d.startTime.getTime()-u.startTime.getTime()),c}async parseActivityFile(e){let t=Mt(e).toLowerCase();if(t===".hrm"){let r=j(e,"utf-8");return ct(r)}if(t===".json"){let r=j(e,"utf-8");return lt(r)}throw Error(`Unsupported file format: ${t}`)}async parseMonitoringFile(e){return{}}findFiles(e,t){let r=[];try{let o=ot(e);for(let c of o){let p=b(e,c);try{if(at(p).isDirectory())r.push(...this.findFiles(p,t));else if(c.toLowerCase().endsWith(t))r.push(p)}catch{}}}catch{}return r}}function S(){return new h}import{existsSync as kt,readdirSync as pt,statSync as ut,readFileSync as J,mkdirSync as Ct,copyFileSync as dt}from"fs";import{join as G,extname as Ot}from"path";var Y="/Volumes",Ut=["SUUNTO","AMBIT","SPARTAN","SUUNTO 9","SUUNTO 7","SUUNTO 5","VERTICAL","RACE"];function mt(e){let t=(v,H)=>{let T=new RegExp(`<${H}[^>]*>([\\s\\S]*?)</${H}>`,"i"),R=v.match(T);return R?R[1].trim():null},r=(v,H)=>{let T=new RegExp(`<${H}[^>]*>([\\s\\S]*?)</${H}>`,"gi"),R=[],k;while((k=T.exec(v))!==null)R.push(k[1].trim());return R},o=t(e,"Header");if(!o)return null;let c=t(o,"DateTime"),p=parseFloat(t(o,"Duration")||"0"),n=parseFloat(t(o,"Distance")||"0"),a=parseInt(t(o,"Energy")||"0",10)/4184,F=parseFloat(t(o,"Ascent")||"0"),d=parseFloat(t(o,"Descent")||"0"),u=t(o,"ActivityType");if(!c)return null;let l=new Date(c),m=new Date(l.getTime()+p*1000),D=[],P=r(e,"Sample");for(let v of P){let H=t(v,"UTC"),T=parseInt(t(v,"HR")||"0",10),R=parseFloat(t(v,"Speed")||"0"),k=parseInt(t(v,"Cadence")||"0",10),B=parseFloat(t(v,"Altitude")||"0"),it=parseFloat(t(v,"Latitude")||"0"),rt=parseFloat(t(v,"Longitude")||"0"),nt=parseInt(t(v,"BikePower")||"0",10);if(H)D.push({timestamp:new Date(H),heartRate:T>0?T*60:void 0,speed:R>0?R:void 0,cadence:k>0?k*60:void 0,altitude:B!==0?B:void 0,power:nt>0?nt:void 0,position:it!==0&&rt!==0?{lat:it*(180/Math.PI),lng:rt*(180/Math.PI),altitude:B}:void 0})}let M=[],U=r(e,"Mark");for(let v of U){let H=t(v,"Time");if(t(v,"Type")==="lap"&&H){let R=parseFloat(H);M.push({startTime:new Date(l.getTime()+R*1000),endTime:new Date(l.getTime()+R*1000),totalElapsedTime:0,totalTimerTime:0,totalDistance:0,totalCalories:0})}}let W=D.filter((v)=>v.heartRate).map((v)=>v.heartRate),C=W.length>0?Math.round(W.reduce((v,H)=>v+H,0)/W.length):void 0,O=W.length>0?Math.max(...W):void 0,L=D.filter((v)=>v.speed).map((v)=>v.speed),K=L.length>0?L.reduce((v,H)=>v+H,0)/L.length:void 0,V=L.length>0?Math.max(...L):void 0;return{id:`suunto_${l.getTime()}`,sport:yt(u??void 0),startTime:l,endTime:m,totalElapsedTime:p,totalTimerTime:p,totalDistance:n,totalCalories:Math.round(a),avgHeartRate:C,maxHeartRate:O,avgSpeed:K,maxSpeed:V,totalAscent:F,totalDescent:d,laps:M,records:D,source:"garmin"}}function gt(e){try{let t=JSON.parse(e),r=new Date(t.StartTime||t.startTime),o=t.Duration||t.duration||0,c=new Date(r.getTime()+o*1000),p=[],n=t.TrackPoints||t.trackPoints||t.Samples||t.samples||[];for(let a of n)p.push({timestamp:new Date(a.Time||a.time||a.Timestamp),heartRate:a.HR||a.hr||a.HeartRate,speed:a.Speed||a.speed,cadence:a.Cadence||a.cadence,altitude:a.Altitude||a.altitude,distance:a.Distance||a.distance,power:a.Power||a.power,position:(a.Latitude||a.latitude)&&(a.Longitude||a.longitude)?{lat:a.Latitude||a.latitude,lng:a.Longitude||a.longitude,altitude:a.Altitude||a.altitude}:void 0});return{id:`suunto_${t.MoveId||t.moveId||r.getTime()}`,name:t.Name||t.name,sport:yt(t.ActivityType||t.activityType||t.Sport||t.sport),startTime:r,endTime:c,totalElapsedTime:o,totalTimerTime:o,totalDistance:t.Distance||t.distance||0,totalCalories:Math.round((t.Energy||t.energy||0)/4.184),avgHeartRate:t.AvgHR||t.avgHR||t.HeartRateAvg,maxHeartRate:t.MaxHR||t.maxHR||t.HeartRateMax,avgSpeed:t.AvgSpeed||t.avgSpeed,maxSpeed:t.MaxSpeed||t.maxSpeed,totalAscent:t.Ascent||t.ascent||t.AscentAltitude,totalDescent:t.Descent||t.descent||t.DescentAltitude,laps:[],records:p,source:"garmin"}}catch{return null}}function yt(e){if(!e)return"other";let t=typeof e==="number"?e.toString():e.toLowerCase();return{running:"running",run:"running","1":"running",cycling:"cycling",bike:"cycling","2":"cycling",swimming:"swimming",swim:"swimming","5":"swimming",hiking:"hiking",trekking:"hiking","11":"hiking",walking:"walking","3":"walking"}[t]||"other"}class g{name="Suunto";type="suunto";async detectDevices(){let e=[];if(!kt(Y))return e;let t=pt(Y);for(let r of t){let o=G(Y,r);try{if(!ut(o).isDirectory())continue}catch{continue}let c=r.toUpperCase();if(Ut.some((n)=>c.includes(n)))e.push({name:r,path:o,type:"suunto",model:r})}return e}async downloadData(e,t={}){let{outputDir:r="./suunto-data",copyRawFiles:o=!0}=t,c={device:e,activities:[],monitoring:new Map,rawFiles:[],errors:[]},p=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),n=G(r,p);if(o)Ct(n,{recursive:!0});let a=this.findFiles(e.path,".sml");for(let d of a)try{let u=J(d,"utf-8"),l=mt(u);if(l)l.rawFilePath=d,c.activities.push(l);if(o){let m=G(n,d.split("/").pop());dt(d,m),c.rawFiles.push(m)}}catch(u){c.errors.push(u instanceof Error?u:Error(String(u)))}let F=this.findFiles(e.path,".json");for(let d of F)try{let u=J(d,"utf-8"),l=gt(u);if(l)l.rawFilePath=d,c.activities.push(l);if(o){let m=G(n,d.split("/").pop());dt(d,m),c.rawFiles.push(m)}}catch(u){c.errors.push(u instanceof Error?u:Error(String(u)))}return c.activities.sort((d,u)=>d.startTime.getTime()-u.startTime.getTime()),c}async parseActivityFile(e){let t=Ot(e).toLowerCase();if(t===".sml"){let r=J(e,"utf-8");return mt(r)}if(t===".json"){let r=J(e,"utf-8");return gt(r)}throw Error(`Unsupported file format: ${t}`)}async parseMonitoringFile(e){return{}}findFiles(e,t){let r=[];try{let o=pt(e);for(let c of o){let p=G(e,c);try{if(ut(p).isDirectory())r.push(...this.findFiles(p,t));else if(c.toLowerCase().endsWith(t))r.push(p)}catch{}}}catch{}return r}}function E(){return new g}import{existsSync as q,readdirSync as ht,statSync as ft,mkdirSync as vt,copyFileSync as It,readFileSync as Dt}from"fs";import{join as N,extname as wt}from"path";var z="/Volumes",Lt=["COROS","PACE","APEX","VERTIX","DURA"];class f{name="Coros";type="coros";async detectDevices(){let e=[];if(!q(z))return e;let t=ht(z);for(let r of t){let o=N(z,r);try{if(!ft(o).isDirectory())continue}catch{continue}let c=r.toUpperCase(),p=Lt.some((n)=>c.includes(n));if(!p){let n=N(o,"COROS");if(q(n)){e.push({name:r,path:o,type:"coros",model:r});continue}}if(p)e.push({name:r,path:o,type:"coros",model:r})}return e}async downloadData(e,t={}){let{outputDir:r="./coros-data",includeActivities:o=!0,since:c,until:p,copyRawFiles:n=!0}=t,a={device:e,activities:[],monitoring:new Map,rawFiles:[],errors:[]},F=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),d=N(r,F);if(n)vt(d,{recursive:!0});let u=[N(e.path,"Activity"),N(e.path,"COROS","Activity"),N(e.path,"GARMIN","Activity")];if(o)for(let l of u){if(!q(l))continue;let m=ht(l).filter((D)=>D.toLowerCase().endsWith(".fit"));for(let D of m){let P=N(l,D);try{let U=ft(P).mtime;if(c&&U<c)continue;if(p&&U>p)continue;let W=await this.parseActivityFile(P);if(W)W.rawFilePath=P,a.activities.push(W);if(n){let C=N(d,"Activity");vt(C,{recursive:!0});let O=N(C,D);It(P,O),a.rawFiles.push(O)}}catch(M){a.errors.push(M instanceof Error?M:Error(String(M)))}}}return a.activities.sort((l,m)=>l.startTime.getTime()-m.startTime.getTime()),a}async parseActivityFile(e){let t=wt(e).toLowerCase();if(t!==".fit")throw Error(`Unsupported file format: ${t}`);let r=Dt(e),c=new i(r).parse();return new s(c).decodeActivity()}async parseMonitoringFile(e){let t=wt(e).toLowerCase();if(t!==".fit")throw Error(`Unsupported file format: ${t}`);let r=Dt(e),c=new i(r).parse();return new s(c).decodeMonitoring()}}function A(){return new f}import{existsSync as Z,readdirSync as tt,statSync as Tt,mkdirSync as At,copyFileSync as Nt,readFileSync as St}from"fs";import{join as _,extname as Rt}from"path";var et="/Volumes",_t=["WAHOO","ELEMNT","BOLT","ROAM","RIVAL"];class y{name="Wahoo";type="wahoo";async detectDevices(){let e=[];if(!Z(et))return e;let t=tt(et);for(let r of t){let o=_(et,r);try{if(!Tt(o).isDirectory())continue}catch{continue}let c=r.toUpperCase(),p=_t.some((n)=>c.includes(n));if(!p){let n=_(o,"activities");if(Z(n)){if(tt(n).some((F)=>F.toLowerCase().endsWith(".fit"))){e.push({name:r,path:o,type:"wahoo",model:r});continue}}}if(p)e.push({name:r,path:o,type:"wahoo",model:r})}return e}async downloadData(e,t={}){let{outputDir:r="./wahoo-data",includeActivities:o=!0,since:c,until:p,copyRawFiles:n=!0}=t,a={device:e,activities:[],monitoring:new Map,rawFiles:[],errors:[]},F=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),d=_(r,F);if(n)At(d,{recursive:!0});let u=[_(e.path,"activities"),_(e.path,"Activity"),_(e.path,"workouts")];if(o)for(let l of u){if(!Z(l))continue;let m=tt(l).filter((D)=>D.toLowerCase().endsWith(".fit"));for(let D of m){let P=_(l,D);try{let U=Tt(P).mtime;if(c&&U<c)continue;if(p&&U>p)continue;let W=await this.parseActivityFile(P);if(W)W.rawFilePath=P,a.activities.push(W);if(n){let C=_(d,"activities");At(C,{recursive:!0});let O=_(C,D);Nt(P,O),a.rawFiles.push(O)}}catch(M){a.errors.push(M instanceof Error?M:Error(String(M)))}}}return a.activities.sort((l,m)=>l.startTime.getTime()-m.startTime.getTime()),a}async parseActivityFile(e){let t=Rt(e).toLowerCase();if(t!==".fit")throw Error(`Unsupported file format: ${t}`);let r=St(e),c=new i(r).parse();return new s(c).decodeActivity()}async parseMonitoringFile(e){let t=Rt(e).toLowerCase();if(t!==".fit")throw Error(`Unsupported file format: ${t}`);let r=St(e),c=new i(r).parse();return new s(c).decodeMonitoring()}}function x(){return new y}import{existsSync as xt,readFileSync as Vt}from"fs";function Kt(e){let t=[],r=[],o=/<Workout([^>]+)>/g,c;while((c=o.exec(e))!==null){let a=Et(c[1]);t.push({workoutActivityType:a.workoutActivityType||"",duration:a.duration||"0",durationUnit:a.durationUnit||"min",totalDistance:a.totalDistance||"0",totalDistanceUnit:a.totalDistanceUnit||"km",totalEnergyBurned:a.totalEnergyBurned||"0",totalEnergyBurnedUnit:a.totalEnergyBurnedUnit||"kcal",sourceName:a.sourceName||"",startDate:a.startDate||"",endDate:a.endDate||""})}let p=/<Record([^>]+)\/>/g,n;while((n=p.exec(e))!==null){let a=Et(n[1]);r.push({type:a.type||"",sourceName:a.sourceName||"",unit:a.unit||"",value:a.value||"0",startDate:a.startDate||"",endDate:a.endDate||"",creationDate:a.creationDate})}return{workouts:t,records:r}}function Et(e){let t={},r=/(\w+)="([^"]*)"/g,o;while((o=r.exec(e))!==null)t[o[1]]=o[2];return t}function X(e){return new Date(e.replace(" ","T").replace(" ",""))}function $t(e){return{HKWorkoutActivityTypeRunning:"running",HKWorkoutActivityTypeCycling:"cycling",HKWorkoutActivityTypeSwimming:"swimming",HKWorkoutActivityTypeHiking:"hiking",HKWorkoutActivityTypeWalking:"walking",HKWorkoutActivityTypeFunctionalStrengthTraining:"strength_training",HKWorkoutActivityTypeTraditionalStrengthTraining:"strength_training",HKWorkoutActivityTypeYoga:"yoga",HKWorkoutActivityTypeCrossTraining:"cardio",HKWorkoutActivityTypeElliptical:"cardio",HKWorkoutActivityTypeRowing:"cardio",HKWorkoutActivityTypeStairClimbing:"cardio"}[e]||"other"}class w{name="Apple";type="apple";async detectDevices(){return[]}async downloadData(e,t={}){return{device:e,activities:[],monitoring:new Map,rawFiles:[],errors:[Error("Apple Watch data must be imported from Health app export. Use parseHealthExport() instead.")]}}async parseHealthExport(e){let t=xt(e)?e:`${e}/apple_health_export/export.xml`;if(!xt(t))throw Error(`Health export not found at ${t}`);let r=Vt(t,"utf-8"),{workouts:o,records:c}=Kt(r),p=o.filter((l)=>l.sourceName.includes("Watch")).map((l)=>this.workoutToActivity(l)),n=new Map,a=c.filter((l)=>l.type==="HKQuantityTypeIdentifierHeartRate"),F=new Map;for(let l of a){let m=X(l.startDate),D=m.toISOString().slice(0,10),P=F.get(D)||[];P.push({timestamp:m,heartRate:parseInt(l.value,10)}),F.set(D,P)}for(let[l,m]of F){let D=n.get(l)||{},P=m.map((M)=>M.heartRate);D.heartRate={date:new Date(l),minHeartRate:Math.min(...P),maxHeartRate:Math.max(...P),avgHeartRate:Math.round(P.reduce((M,U)=>M+U,0)/P.length),samples:m},n.set(l,D)}let d=c.filter((l)=>l.type==="HKQuantityTypeIdentifierStepCount"),u=new Map;for(let l of d){let D=X(l.startDate).toISOString().slice(0,10),P=u.get(D)||0;u.set(D,P+parseInt(l.value,10))}for(let[l,m]of u){let D=n.get(l)||{};D.steps={date:new Date(l),totalSteps:m,goal:1e4,distance:0,calories:0,activeMinutes:0},n.set(l,D)}return{activities:p,monitoring:n}}workoutToActivity(e){let t=X(e.startDate),r=X(e.endDate),o=parseFloat(e.duration);if(e.durationUnit==="min")o*=60;else if(e.durationUnit==="hr")o*=3600;let c=parseFloat(e.totalDistance);if(e.totalDistanceUnit==="km")c*=1000;else if(e.totalDistanceUnit==="mi")c*=1609.34;let p=parseFloat(e.totalEnergyBurned);if(e.totalEnergyBurnedUnit==="kJ")p/=4.184;return{id:`apple_${t.getTime()}`,sport:$t(e.workoutActivityType),startTime:t,endTime:r,totalElapsedTime:o,totalTimerTime:o,totalDistance:c,totalCalories:Math.round(p),laps:[],records:[],source:"garmin"}}async parseActivityFile(e){throw Error("Apple Watch activities must be imported via Health export")}async parseMonitoringFile(e){throw Error("Apple Watch monitoring data must be imported via Health export")}}function I(){return new w}export{h,S,g,E,f,A,y,x,w,I};
|
|
@@ -38,7 +38,26 @@ export declare function signState(payload: string, secret: string): string;
|
|
|
38
38
|
/** Verify and decode a value produced by {@link signState}, or null if it was altered. */
|
|
39
39
|
export declare function verifyState(token: string | undefined, secret: string): string | null;
|
|
40
40
|
/** Is a token past, or nearly past, its expiry? */
|
|
41
|
-
export declare function isTokenExpired(tokens: GarminTokens, skewSeconds?: number, now?:
|
|
41
|
+
export declare function isTokenExpired(tokens: GarminTokens, skewSeconds?: number, now?: number): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Seal an in-flight attempt into a token safe to hand to a browser.
|
|
44
|
+
*
|
|
45
|
+
* The callback is a top-level navigation from Garmin carrying no session, so
|
|
46
|
+
* the app has to stash who was connecting somewhere the browser holds - which
|
|
47
|
+
* means the value is under the visitor's control and must be signed. Without a
|
|
48
|
+
* signature someone edits the subject in their own cookie and attaches their
|
|
49
|
+
* watch to another person's account.
|
|
50
|
+
*
|
|
51
|
+
* This is {@link signState} with the shape the flow actually needs: a subject,
|
|
52
|
+
* the state parameter, the PKCE verifier, and an issue time so a stale attempt
|
|
53
|
+
* can be refused instead of silently resumed.
|
|
54
|
+
*/
|
|
55
|
+
export declare function sealOAuthAttempt<TSubject>(attempt: OAuthAttempt<TSubject>, secret: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Open a sealed attempt, or return null when it was tampered with, malformed,
|
|
58
|
+
* or has expired. Null always means "start again", never "trust it anyway".
|
|
59
|
+
*/
|
|
60
|
+
export declare function openOAuthAttempt<TSubject = string>(token: string | undefined, secret: string, options?: { now?: number, ttlMs?: number }): OAuthAttempt<TSubject> | null;
|
|
42
61
|
/** Endpoints from Garmin's OAuth 2.0 PKCE specification. */
|
|
43
62
|
export declare const GARMIN_ENDPOINTS: {
|
|
44
63
|
authorize: 'https://connect.garmin.com/oauth2Confirm';
|
|
@@ -48,6 +67,8 @@ export declare const GARMIN_ENDPOINTS: {
|
|
|
48
67
|
registration: 'https://apis.garmin.com/wellness-api/rest/user/registration';
|
|
49
68
|
backfillActivities: 'https://apis.garmin.com/wellness-api/rest/backfill/activities'
|
|
50
69
|
};
|
|
70
|
+
/** How long an in-flight attempt stays valid, in milliseconds. */
|
|
71
|
+
export declare const OAUTH_ATTEMPT_TTL_MS: number;
|
|
51
72
|
export declare interface GarminActivityApiConfig {
|
|
52
73
|
clientId: string
|
|
53
74
|
clientSecret: string
|
|
@@ -109,6 +130,13 @@ export declare interface GarminDeregistration {
|
|
|
109
130
|
userId: string
|
|
110
131
|
userAccessToken?: string
|
|
111
132
|
}
|
|
133
|
+
/** An in-flight authorization attempt, as it survives the round trip to Garmin. */
|
|
134
|
+
export declare interface OAuthAttempt<TSubject = string> {
|
|
135
|
+
subject: TSubject
|
|
136
|
+
state: string
|
|
137
|
+
verifier: string
|
|
138
|
+
issuedAt: number
|
|
139
|
+
}
|
|
112
140
|
/**
|
|
113
141
|
* A client for the official Garmin Activity API.
|
|
114
142
|
*
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { Activity, SleepData, StressData, BodyBattery, HRVData, WeightData } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Garmin Connect's activity details as plain samples. The endpoint sends one
|
|
4
|
+
* array of values per sample, in the order `metricDescriptors` gives; timer
|
|
5
|
+
* time is `sumDuration` (seconds), falling back to wall time from
|
|
6
|
+
* `directTimestamp` (milliseconds) with any gap over a minute counted as a
|
|
7
|
+
* pause. Samples with no time at all are dropped.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseActivityDetails(details: unknown): ActivityDetailSample[];
|
|
2
10
|
export declare function createGarminConnectClient(config?: GarminConnectConfig): GarminConnectClient;
|
|
3
11
|
export declare interface GarminConnectConfig {
|
|
4
12
|
username: string
|
|
@@ -49,12 +57,20 @@ export declare interface DailySummary {
|
|
|
49
57
|
calories: number
|
|
50
58
|
}
|
|
51
59
|
}
|
|
60
|
+
/** One recorded sample from an activity's details. */
|
|
61
|
+
export declare interface ActivityDetailSample {
|
|
62
|
+
seconds: number
|
|
63
|
+
heartRate: number | null
|
|
64
|
+
power: number | null
|
|
65
|
+
speed: number | null
|
|
66
|
+
}
|
|
52
67
|
export declare class GarminConnectClient {
|
|
53
68
|
constructor(config?: GarminConnectConfig);
|
|
54
69
|
login(username?: string, password?: string): Promise<void>;
|
|
55
70
|
getUserProfile(): Promise<{ displayName: string, profileId: string }>;
|
|
56
71
|
getActivities(start?: number, limit?: number): Promise<GarminActivitySummary[]>;
|
|
57
72
|
getActivity(activityId: number): Promise<Activity>;
|
|
73
|
+
getActivityDetails(activityId: number, maxSamples?: number): Promise<ActivityDetailSample[]>;
|
|
58
74
|
downloadActivityFit(activityId: number, outputDir: string): Promise<void>;
|
|
59
75
|
getDailySummary(date: Date): Promise<DailySummary>;
|
|
60
76
|
getHeartRateData(date: Date): Promise<DailyHeartRate | undefined>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './garmin-activity-api';
|
|
2
|
+
export {
|
|
3
|
+
createGarminConnectClient,
|
|
4
|
+
GarminConnectClient,
|
|
5
|
+
parseActivityDetails,
|
|
6
|
+
type ActivityDetailSample,
|
|
7
|
+
type DailyHeartRate,
|
|
8
|
+
type DailySummary,
|
|
9
|
+
type GarminActivitySummary as GarminConnectActivitySummary,
|
|
10
|
+
type GarminConnectConfig,
|
|
11
|
+
} from './garmin-connect';
|
|
12
|
+
export * from './strava';
|
|
13
|
+
export * from './trainingpeaks';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{h,S,g,E,f,A,y,x,w,I}from"../../chunk-e2wpg6tp.js";import{b,l}from"../../chunk-dz0ym1nb.js";import"../../chunk-79g2tyda.js";export{w as AppleDriver,f as CorosDriver,b as GarminDriver,h as PolarDriver,g as SuuntoDriver,y as WahooDriver,I as createAppleDriver,A as createCorosDriver,l as createGarminDriver,S as createPolarDriver,E as createSuuntoDriver,x as createWahooDriver};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d,p,v,t,C,r,e,a,n,u,T,D,m,i,o,c,R,s}from"../../chunk-79g2tyda.js";export{t as BASE_TYPES,C as BASE_TYPE_SIZES,e as FIELD_DEF,T as FIT_EPOCH,d as FIT_HEADER_SIZE,p as FIT_HEADER_SIZE_LEGACY,v as FIT_SIGNATURE,s as FitDecoder,m as FitParseError,i as FitParser,D as MANUFACTURER,r as MESG_NUM,u as SLEEP_LEVEL,a as SPORT,n as SUB_SPORT,R as degreesToSemicircles,o as fitTimestampToDate,c as semicirclesToDegrees};
|