ts-watches 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +358 -0
  3. package/dist/analysis/index.d.ts +5 -0
  4. package/dist/analysis/personal-records.d.ts +58 -0
  5. package/dist/analysis/race-predictor.d.ts +53 -0
  6. package/dist/analysis/running-dynamics.d.ts +66 -0
  7. package/dist/analysis/training-load.d.ts +40 -0
  8. package/dist/analysis/zones.d.ts +69 -0
  9. package/dist/bin/cli.js +52 -0
  10. package/dist/chunk-gsgt8r0z.js +2 -0
  11. package/dist/cloud/garmin-activity-api.d.ts +129 -0
  12. package/dist/cloud/garmin-connect.d.ts +67 -0
  13. package/dist/cloud/index.d.ts +4 -0
  14. package/dist/cloud/strava.d.ts +88 -0
  15. package/dist/cloud/trainingpeaks.d.ts +48 -0
  16. package/dist/config.d.ts +4 -0
  17. package/dist/drivers/apple.d.ts +17 -0
  18. package/dist/drivers/coros.d.ts +13 -0
  19. package/dist/drivers/garmin.d.ts +12 -0
  20. package/dist/drivers/index.d.ts +6 -0
  21. package/dist/drivers/polar.d.ts +14 -0
  22. package/dist/drivers/suunto.d.ts +13 -0
  23. package/dist/drivers/wahoo.d.ts +13 -0
  24. package/dist/export/csv.d.ts +15 -0
  25. package/dist/export/geojson.d.ts +23 -0
  26. package/dist/export/gpx.d.ts +10 -0
  27. package/dist/export/index.d.ts +4 -0
  28. package/dist/export/tcx.d.ts +10 -0
  29. package/dist/fit/constants.d.ts +606 -0
  30. package/dist/fit/decoder.d.ts +16 -0
  31. package/dist/fit/index.d.ts +3 -0
  32. package/dist/fit/parser.d.ts +21 -0
  33. package/dist/index.d.ts +40 -0
  34. package/dist/realtime/ant-plus.d.ts +201 -0
  35. package/dist/realtime/bluetooth.d.ts +153 -0
  36. package/dist/realtime/index.d.ts +3 -0
  37. package/dist/realtime/live-tracking.d.ts +70 -0
  38. package/dist/src/index.js +324 -0
  39. package/dist/types.d.ts +307 -0
  40. package/dist/workouts/builder.d.ts +184 -0
  41. package/dist/workouts/course.d.ts +74 -0
  42. package/dist/workouts/index.d.ts +3 -0
  43. package/dist/workouts/training-plan.d.ts +77 -0
  44. package/package.json +92 -0
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env bun
2
+ import{u as E}from"../chunk-gsgt8r0z.js";import{existsSync as q6}from"node:fs";import h from"node:fs/promises";import p from"node:os";import k from"node:path";import P from"node:process";import{EventEmitter as j6}from"node:events";import L from"node:process";import n from"node:process";import g from"node:process";import b from"node:process";import B from"node:process";import r from"node:tty";import r6,{stdin as bz,stdout as Az}from"node:process";import s,{stdin as t6,stdout as o6}from"node:process";import e6 from"node:readline";class o{configPath;config=null;events=[];retryCount=0;maxRetries=3;retryDelayMs=1000;constructor(){let z=p.homedir(),J=k.join(z,".config","clapp");this.configPath=k.join(J,"telemetry.json")}async isEnabled(){if(P.env.DO_NOT_TRACK==="1"||P.env.DO_NOT_TRACK==="true")return!1;if(P.env.NO_TELEMETRY==="1"||P.env.NO_TELEMETRY==="true")return!1;return(await this.loadConfig()).enabled}async enable(){let z=await this.loadConfig();if(z.enabled=!0,!z.userId)z.userId=this.generateUserId();await this.saveConfig(z)}async disable(){let z=await this.loadConfig();z.enabled=!1,await this.saveConfig(z)}async track(z,J){if(!await this.isEnabled())return;let $={event:z,...J,timestamp:Date.now(),platform:p.platform(),nodeVersion:P.version};if(this.events.push($),this.events.length>=10)await this.send()}async trackCommand(z,J){await this.track("command",{command:z,duration:J})}async trackError(z,J){await this.track("error",{error:z,command:J})}async send(){if(!await this.isEnabled()||this.events.length===0)return!0;try{this.events=[],this.retryCount=0;let J=await this.loadConfig();return J.lastSent=Date.now(),await this.saveConfig(J),!0}catch{if(this.retryCount<this.maxRetries){this.retryCount++;let J=this.retryDelayMs*2**(this.retryCount-1);return await this.sleep(J),this.send()}return this.events=[],this.retryCount=0,!1}}async flush(){if(this.events.length===0)return!0;return this.send()}sleep(z){return new Promise((J)=>setTimeout(J,z))}async status(){let z=await this.loadConfig();return{enabled:z.enabled,doNotTrack:P.env.DO_NOT_TRACK==="1"||P.env.DO_NOT_TRACK==="true",eventsQueued:this.events.length,lastSent:z.lastSent}}async loadConfig(){if(this.config)return this.config;try{if(q6(this.configPath)){let z=await h.readFile(this.configPath,"utf-8");return this.config=JSON.parse(z),this.config}}catch{}return this.config={enabled:!1},this.config}async saveConfig(z){this.config=z;try{let J=k.dirname(this.configPath);await h.mkdir(J,{recursive:!0}),await h.writeFile(this.configPath,JSON.stringify(z,null,2),"utf-8")}catch{}}generateUserId(){let z=Math.random().toString(36).substring(2,15),J=Date.now().toString(36);return`${z}-${J}`}}var wz=new o;function X6(z,J={}){let Z={_:[]},$=J.alias||{},Q=new Set(J.boolean||[]),q={};for(let j of Object.keys($))for(let Y of $[j])q[Y]=j;for(let j of Q)if($[j])for(let Y of $[j])Q.add(Y);function X(j,Y){let U=q[j]||j;if(Z[U]=Y,$[U])for(let F of $[U])Z[F]=Y;if(q[j]&&$[q[j]])for(let F of $[q[j]])Z[F]=Y;Z[j]=Y}for(let j=0;j<z.length;j++){let Y=z[j];if(Y==="--"){Z._.push(...z.slice(j+1));break}if(Y.startsWith("--")){let U=Y.indexOf("=");if(U!==-1){let F=Y.slice(2,U),G=Y.slice(U+1);X(F,G)}else{let F=Y.slice(2);if(F.startsWith("no-")){let H=F.slice(3);X(H,!1);continue}let G=q[F]||F;if(Q.has(G)||Q.has(F))X(F,!0);else{let H=z[j+1];if(H!==void 0&&!H.startsWith("-"))X(F,H),j++;else X(F,!0)}}}else if(Y.startsWith("-")&&Y.length>1){let U=Y.slice(1);for(let F=0;F<U.length;F++){let G=U[F],H=q[G]||G;if(F===U.length-1&&!Q.has(H)&&!Q.has(G)){let K=z[j+1];if(K!==void 0&&!K.startsWith("-"))X(G,K),j++;else X(G,!0)}else X(G,!0)}}else Z._.push(Y)}return Z}function v(z){return z.replace(/[<[].+/,"").trim()}function U6(z){let J=/<([^>]+)>/g,Z=/\[([^\]]+)\]/g,$=[],Q=(j)=>{let Y=!1,U=j[1];if(U.startsWith("..."))U=U.slice(3),Y=!0;return{required:j[0].startsWith("<"),value:U,variadic:Y}},q;while(q=J.exec(z))$.push(Q(q));let X;while(X=Z.exec(z))$.push(Q(X));return $}function F6(z){let J={alias:{},boolean:[]};for(let[Z,$]of z.entries()){if($.names.length>1)J.alias[$.names[0]]=$.names.slice(1);if($.isBoolean)if($.negated){if(!z.some((q,X)=>{return X!==Z&&q.names.some((j)=>$.names.includes(j))&&typeof q.required==="boolean"}))J.boolean.push($.names[0])}else J.boolean.push($.names[0])}return J}function i(z){return z.reduce((J,Z)=>J.length>=Z.length?J:Z,"")}function S(z,J){return z.length>=J?z:`${z}${" ".repeat(J-z.length)}`}function G6(z){return z.replace(/([a-z])-([a-z])/g,(J,Z,$)=>{return Z+$.toUpperCase()})}function H6(z,J,Z){let $=0,Q=J.length,q=z,X;for(;$<Q;++$)X=q[J[$]],q=q[J[$]]=$===Q-1?Z:X!=null?X:!!~J[$+1].indexOf(".")||!(+J[$+1]>-1)?{}:[]}function K6(z,J){for(let Z of Object.keys(J)){let $=J[Z];if($.shouldTransform){if(z[Z]=Array.prototype.concat.call([],z[Z]),typeof $.transformFunction==="function")z[Z]=z[Z].map($.transformFunction)}}}function V6(z){let J=/([^\\/]+)$/.exec(z);return J?J[1]:""}function e(z){return z.split(".").map((J,Z)=>{return Z===0?G6(J):J}).join(".")}class I extends Error{exitCode=2;isUsageError=!0;constructor(z){super(z);if(this.name=this.constructor.name,typeof Error.captureStackTrace==="function")Error.captureStackTrace(this,this.constructor);else this.stack=Error(z).stack}format(z=!1){if(z&&this.stack)return`${this.message}
3
+
4
+ Stack trace:
5
+ ${this.stack}`;return this.message}}function W6(){let{env:z}=n,{TERM:J,TERM_PROGRAM:Z}=z;if(n.platform!=="win32")return J!=="linux";return Boolean(z.WT_SESSION)||Boolean(z.TERMINUS_SUBLIME)||z.ConEmuTask==="{cmd::Cmder}"||Z==="Terminus-Sublime"||Z==="vscode"||J==="xterm-256color"||J==="alacritty"||J==="rxvt-unicode"||J==="rxvt-unicode-256color"||z.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var C="\x1B",w=`${C}[`;var D={to(z,J){if(!J)return`${w}${z+1}G`;return`${w}${J+1};${z+1}H`},move(z,J){let Z="";if(z<0)Z+=`${w}${-z}D`;else if(z>0)Z+=`${w}${z}C`;if(J<0)Z+=`${w}${-J}A`;else if(J>0)Z+=`${w}${J}B`;return Z},up:(z=1)=>`${w}${z}A`,down:(z=1)=>`${w}${z}B`,forward:(z=1)=>`${w}${z}C`,backward:(z=1)=>`${w}${z}D`,nextLine:(z=1)=>`${w}E`.repeat(z),prevLine:(z=1)=>`${w}F`.repeat(z),left:`${w}G`,hide:`${w}?25l`,show:`${w}?25h`,save:`${C}7`,restore:`${C}8`};var y={screen:`${w}2J`,up:(z=1)=>`${w}1J`.repeat(z),down:(z=1)=>`${w}J`.repeat(z),line:`${w}2K`,lineEnd:`${w}K`,lineStart:`${w}1K`,lines(z){let J="";for(let Z=0;Z<z;Z++)J+=this.line+(Z<z-1?D.up():"");if(z)J+=D.left;return J}},Nz={screen:`${C}c`};function w6(z,J){let Z=[];for(let $=0;$<=J.length;$++)Z[$]=[$];for(let $=0;$<=z.length;$++)Z[0][$]=$;for(let $=1;$<=J.length;$++)for(let Q=1;Q<=z.length;Q++)if(J.charAt($-1)===z.charAt(Q-1))Z[$][Q]=Z[$-1][Q-1];else Z[$][Q]=Math.min(Z[$-1][Q-1]+1,Z[$][Q-1]+1,Z[$-1][Q]+1);return Z[J.length][z.length]}function z6(z,J,Z=2,$=3){return J.map((Q)=>({cmd:Q,distance:w6(z,Q)})).filter(({distance:Q})=>Q<=Z).sort((Q,q)=>Q.distance-q.distance).slice(0,$).map(({cmd:Q})=>Q)}class J6{rawName;description;name;names;isBoolean;required;config;negated;constructor(z,J,Z){if(this.rawName=z,this.description=J,this.config=Object.assign({},Z),z=z.replace(/\.\*/g,""),this.negated=!1,this.names=v(z).split(",").map(($)=>{let Q=$.trim().replace(/^-{1,2}/,"");if(Q.startsWith("no-"))this.negated=!0,Q=Q.replace(/^no-/,"");return e(Q)}).sort(($,Q)=>$.length>Q.length?1:-1),this.name=this.names[this.names.length-1],this.negated&&this.config.default==null)this.config.default=!0;if(z.includes("<"))this.required=!0;else if(z.includes("["))this.required=!1;else this.isBoolean=!0}}var Oz=g.argv,D6=`${g.platform}-${g.arch} bun-v${typeof Bun<"u"?Bun.version:"unknown"}`,l=b.argv,L6=`${b.platform}-${b.arch} node-${b.version}`;class _{rawName;description;config;cli;options;aliasNames;name;namespace;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;beforeHooks;afterHooks;middleware;constructor(z,J,Z,$){this.rawName=z,this.description=J,this.config=Z,this.cli=$,this.options=[],this.aliasNames=[],this.name=v(z);let Q=v(z),q=Q.indexOf(":");if(q>0)this.namespace=Q.substring(0,q),this.name=Q.substring(q+1);if(this.args=U6(z),this.examples=[],this.beforeHooks=[],this.afterHooks=[],this.middleware=[],!Z)this.config={}}usage(z){return this.usageText=z,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(z,J="-v, --version"){return this.versionNumber=z,this.option(J,"Display version number"),this}example(z){return this.examples.push(z),this}option(z,J,Z){let $=new J6(z,J,Z);return this.options.push($),this}alias(z){return this.aliasNames.push(z),this}action(z){return this.commandAction=z,this}before(z){return this.beforeHooks.push(z),this}after(z){return this.afterHooks.push(z),this}use(z){return this.middleware.push(z),this}isMatched(z){if(this.aliasNames.includes(z))return!0;if(this.namespace)return`${this.namespace}:${this.name}`===z;return this.name===z}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof f}get displayName(){return this.namespace?`${this.namespace}:${this.name}`:this.name}hasOption(z){return z=z.split(".")[0],!!this.options.find((J)=>{return J.names.includes(z)})}outputHelp(){let{name:z,commands:J}=this.cli,{versionNumber:Z,options:$,helpCallback:Q}=this.cli.globalCommand,q=[{body:`${z}${Z?`/${Z}`:""}`}];if(q.push({title:"Usage",body:` $ ${z} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&J.length>0){let Y=i(J.map((K)=>K.rawName)),U=new Map,F=[];for(let K of J)if(K.namespace){if(!U.has(K.namespace))U.set(K.namespace,[]);U.get(K.namespace).push(K)}else F.push(K);let G="";if(F.length>0)G+=F.map((K)=>{return` ${S(K.rawName,Y.length)} ${K.description}`}).join(`
6
+ `);let H=Array.from(U.keys()).sort();for(let K of H){let Y6=U.get(K);if(G.length>0)G+=`
7
+
8
+ `;G+=` ${K}:
9
+ `,G+=Y6.map((c)=>{return` ${S(c.rawName,Y.length-2)} ${c.description}`}).join(`
10
+ `)}q.push({title:"Commands",body:G}),q.push({title:"For more info, run any command with the `--help` flag",body:J.map((K)=>` $ ${z}${K.displayName===""?"":` ${K.displayName}`} --help`).join(`
11
+ `)})}let j=this.isGlobalCommand?$:[...this.options,...$||[]];if(!this.isGlobalCommand&&!this.isDefaultCommand)j=j.filter((Y)=>Y.name!=="version");if(j.length>0){let Y=i(j.map((U)=>U.rawName));q.push({title:"Options",body:j.map((U)=>{return` ${S(U.rawName,Y.length)} ${U.description} ${U.config.default===void 0?"":`(default: ${U.config.default})`}`}).join(`
12
+ `)})}if(this.examples.length>0)q.push({title:"Examples",body:this.examples.map((Y)=>{if(typeof Y==="function")return Y(z);return Y}).join(`
13
+ `)});if(Q)q=Q(q)||q;console.log(q.map((Y)=>{return Y.title?`${Y.title}:
14
+ ${Y.body}`:Y.body}).join(`
15
+
16
+ `))}outputVersion(){let{name:z}=this.cli,{versionNumber:J}=this.cli.globalCommand;if(J)console.log(`${z}/${J} ${typeof Bun<"u"?D6:L6}`)}checkRequiredArgs(){let z=this.args.filter((J)=>J.required).length;if(this.cli.args.length<z){let Z=this.args.filter((Q)=>Q.required).slice(this.cli.args.length),$=Z.map((Q)=>`<${Q.value}>`).join(" ");throw new I(`Missing required argument${Z.length>1?"s":""}: ${$}
17
+
18
+ Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`)}}checkUnknownOptions(){let{options:z,globalCommand:J}=this.cli;if(!this.config.allowUnknownOptions){for(let Z of Object.keys(z))if(Z!=="--"&&!this.hasOption(Z)&&!J.hasOption(Z)){let Q=[...J.options,...this.options].flatMap((Y)=>Y.names),q=Z.length>1?`--${Z}`:`-${Z}`,X=z6(Z,Q),j=`Unknown option \`${q}\``;if(X.length>0)j+=`
19
+
20
+ Did you mean one of these?`,X.forEach((Y)=>{let U=Y.length>1?`--${Y}`:`-${Y}`;j+=`
21
+ • ${U}`});throw j+=`
22
+
23
+ Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`,new I(j)}}}checkOptionValue(){let{options:z,globalCommand:J}=this.cli,Z=[...J.options,...this.options];for(let $ of Z){let Q=z[$.name.split(".")[0]];if($.required){let q=Z.some((X)=>X.negated&&X.names.includes($.name));if(Q===!0||Q===!1&&!q)throw new I(`Option \`${$.rawName}\` requires a value.
24
+
25
+ Example: ${this.cli.name} ${this.rawName} ${$.rawName} <value>`)}}}}class f extends _{constructor(z){super("@@global@@","",{},z)}}var M6=_,M={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 O={primary:"blue",secondary:"cyan",success:"green",warning:"yellow",error:"red",info:"magenta",muted:"gray"};function N6(){return!0}function P6(){let z={};z.supportsColor=N6();function J($,Q=[]){let q=$===""?[]:[...Q,$],X=function(Y){if(!z.supportsColor)return Y;let U="",F="";for(let G of q)if(G in O&&O[G]in M){let H=O[G];U+=M[H][0],F=M[H][1]+F}else if(G in M)U+=M[G][0],F=M[G][1]+F;return U+Y+F},j=[...Object.keys(M),...Object.keys(O)];for(let Y of j)if(!(Y in X))Object.defineProperty(X,Y,{get(){return J(Y,q)}});return X}let Z=[...Object.keys(M),...Object.keys(O)];for(let $ of Z)if(!($ in z))Object.defineProperty(z,$,{get(){return J($)}});return z}var T=P6();class m extends j6{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(z=""){super();this.name=z,this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.globalCommand=new f(this),this.globalCommand.usage("<command> [options]")}handleSignals(z){if(this.signalHandlersSet)return this;let J=async(Z)=>{if(console.log(`
26
+
27
+ Received ${Z}, cleaning up...`),z)try{await z()}catch($){console.error("Error during cleanup:",$)}L.exit(0)};return L.on("SIGINT",()=>J("SIGINT")),L.on("SIGTERM",()=>J("SIGTERM")),this.signalHandlersSet=!0,this}didYouMean(z=!0){return this.enableDidYouMean=z,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(z){return this.globalCommand.usage(z),this}command(z,J,Z){if(!Z)Z={};let $=new M6(z,J||"",Z,this);return $.globalCommand=this.globalCommand,this.commands.push($),$}option(z,J,Z){return this.globalCommand.option(z,J,Z),this}help(z){return this.globalCommand.option("-h, --help","Display this message"),this.globalCommand.helpCallback=z,this.showHelpOnExit=!0,this}version(z,J="-v, --version"){return this.globalCommand.version(z,J),this.showVersionOnExit=!0,this}example(z){return this.globalCommand.example(z),this}outputHelp(){if(this.matchedCommand)this.matchedCommand.outputHelp();else this.globalCommand.outputHelp()}outputVersion(){this.globalCommand.outputVersion()}setParsedInfo({args:z,options:J},Z,$){if(this.args=z,this.options=J,Z)this.matchedCommand=Z;if($)this.matchedCommandName=$;return this}unsetMatchedCommand(){this.matchedCommand=void 0,this.matchedCommandName=void 0}showCommandNotFound(z){if(console.log(T.red(`
28
+ ✗ Command "${z}" not found.
29
+ `)),this.enableDidYouMean){let J=[];for(let $ of this.commands){if($.displayName)J.push($.displayName);if($.aliasNames)J.push(...$.aliasNames)}let Z=z6(z,J);if(Z.length>0)console.log(T.yellow("Did you mean one of these?")),Z.forEach(($)=>console.log(` ${T.dim("•")} ${this.name} ${$}`)),console.log("")}console.log(T.dim("Run"),`${this.name} --help`,T.dim("to see all available commands")),L.exit(1)}async parse(z=l,J={}){let{run:Z=!0,exitOnError:$=!1}=J;if($)try{return await this.parse(z,{run:Z})}catch(Y){throw this.handleUsageError(Y),Y}if(this.rawArgs=z,!this.name)this.name=z[1]?V6(z[1]):"cli";let Q=!0,q=z.slice(2),X=q[0];if(X&&!X.startsWith("-")){for(let Y of this.commands)if(Y.isMatched(X)){let U=this.mri(q,Y);Q=!1;let F={...U,args:U.args.slice(1)};this.setParsedInfo(F,Y,X),this.emit(`command:${X}`,Y);break}}if(Q){for(let Y of this.commands)if(Y.name===""){Q=!1;let U=this.mri(q,Y);this.setParsedInfo(U,Y),this.emit("command:!",Y);break}}if(Q){let Y=this.mri(z.slice(2));this.setParsedInfo(Y)}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(),Z=!1,this.unsetMatchedCommand();if(this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null)this.outputVersion(),Z=!1,this.unsetMatchedCommand();let j={args:this.args,options:this.options};if(Z)await this.runMatchedCommand();if(!this.matchedCommand&&this.args[0]){if(this.emit("command:*"),!(this.listenerCount("command:*")>0))this.showCommandNotFound(this.args[0])}return j}mri(z,J){let Z=[...this.globalCommand.options,...J?J.options:[]],$=F6(Z),Q=[],q=z.indexOf("--");if(q>-1)Q=z.slice(q+1),z=z.slice(0,q);let X=X6(z,$),j={_:X._};for(let H of Object.keys(X))if(H!=="_")j[e(H)]=X[H];let Y=j._,U={"--":Q},F=J&&J.config.ignoreOptionDefaultValue?J.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,G=Object.create(null);for(let H of Z){if(!F&&H.config.default!==void 0)for(let K of H.names)U[K]=H.config.default;if(Array.isArray(H.config.type)){if(G[H.name]===void 0)G[H.name]={shouldTransform:!0,transformFunction:H.config.type[0]}}}for(let H of Object.keys(j))if(H!=="_"){let K=H.split(".");H6(U,K,j[H]),K6(U,G)}return{args:Y,options:U}}async run(z=l){return this.parse(z,{run:!0,exitOnError:!0})}handleUsageError(z){if(!(!!z&&typeof z==="object"&&z.name==="ClappError"&&z.isUsageError!==!1))return;let $=z,Q=$.message??"command-line error",q=this.name?`${this.name}: `:"",X=/--help/.test(Q)?"":`
30
+ Run \`${this.name??"cli"} --help\` for usage.`;L.stderr.write(`${q}${Q}${X}
31
+ `),L.exit($.exitCode??2)}async runMatchedCommand(){let{args:z,options:J,matchedCommand:Z}=this;if(!Z||!Z.commandAction)return;Z.checkUnknownOptions(),Z.checkOptionValue(),Z.checkRequiredArgs();let $=[];Z.args.forEach((j,Y)=>{if(j.variadic)$.push(z.slice(Y));else $.push(z[Y])}),$.push(J);let Q={command:Z,args:$,options:J};for(let j of Z.beforeHooks)await j(Q);let q,X=async()=>{let j=Z.commandAction.apply(this,$);if(j instanceof Promise)q=await j;else q=j;return q};if(Z.middleware.length>0){let j=0,Y=async()=>{if(j<Z.middleware.length){let U=Z.middleware[j++];await U({...Q,next:Y})}else await X()};await Y()}else await X();for(let j of Z.afterHooks)await j(Q);return q}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return L.removeAllListeners("SIGINT"),L.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 Z6{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(z){if(!this.enabled){this.misses++;return}let J=this.cache.get(z);if(!J){this.misses++;return}if(Date.now()-J.timestamp>J.ttl){this.cache.delete(z),this.misses++;return}return this.hits++,J.value}set(z,J,Z=5000){if(!this.enabled)return;this.cache.set(z,{value:J,timestamp:Date.now(),ttl:Z})}has(z){if(!this.enabled)return!1;let J=this.cache.get(z);if(!J)return!1;if(Date.now()-J.timestamp>J.ttl)return this.cache.delete(z),!1;return!0}delete(z){this.cache.delete(z)}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 z=Date.now();for(let[J,Z]of this.cache.entries())if(z-Z.timestamp>Z.ttl)this.cache.delete(J)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var Bz=new Z6;function O6(){if("FORCE_COLOR"in B.env)return B.env.FORCE_COLOR!=="0";if("NO_COLOR"in B.env||B.env.TERM==="dumb")return!1;if(B.platform==="win32")return!0;return r.isatty(1)&&r.isatty(2)}var $6=O6();function W(z,J){if(!$6)return(Z)=>Z;return(Z)=>z+Z+J}var T6=W("\x1B[0m","\x1B[0m"),B6=W("\x1B[31m","\x1B[39m"),E6=W("\x1B[32m","\x1B[39m"),x6=W("\x1B[33m","\x1B[39m"),I6=W("\x1B[34m","\x1B[39m"),b6=W("\x1B[35m","\x1B[39m"),A6=W("\x1B[36m","\x1B[39m"),R6=W("\x1B[37m","\x1B[39m"),C6=W("\x1B[90m","\x1B[39m"),h6=W("\x1B[1m","\x1B[22m"),k6=W("\x1B[3m","\x1B[23m"),S6=W("\x1B[4m","\x1B[24m"),y6=W("\x1B[2m","\x1B[22m"),v6=W("\x1B[7m","\x1B[27m"),g6=W("\x1B[8m","\x1B[28m"),_6=W("\x1B[9m","\x1B[29m"),f6=W("\x1B[41m","\x1B[49m"),m6=W("\x1B[42m","\x1B[49m"),d6=W("\x1B[43m","\x1B[49m"),u6=W("\x1B[44m","\x1B[49m"),c6=W("\x1B[45m","\x1B[49m"),p6=W("\x1B[46m","\x1B[49m"),n6=W("\x1B[47m","\x1B[49m"),i6=$6,l6={reset:T6,red:B6,green:E6,yellow:x6,blue:I6,magenta:b6,cyan:A6,white:R6,gray:C6,bold:h6,italic:k6,underline:S6,dim:y6,inverse:v6,hidden:g6,strikethrough:_6,bgRed:f6,bgGreen:m6,bgYellow:d6,bgBlue:u6,bgMagenta:c6,bgCyan:p6,bgWhite:n6,isColorSupported:i6},A=l6,a6=["up","down","left","right","space","enter","cancel"],R={actions:new Set(a6),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};function Q6(z,J){if(typeof z==="string")return R.aliases.get(z)===J;for(let Z of z){if(Z===void 0)continue;if(Q6(Z,J))return!0}return!1}function s6(z,J){if(z===J)return[];let Z=z.split(`
32
+ `),$=J.split(`
33
+ `),Q=[];for(let q=0;q<Math.max(Z.length,$.length);q++)if(Z[q]!==$[q])Q.push(q);return Q}var Rz=r6.platform.startsWith("win"),a=Symbol("clapp:cancel");function x(z,J){let Z=z;if(Z.isTTY)Z.setRawMode(J)}function zz(z){return z.replace(/\x1b\[[0-9;]*m/g,"")}function Jz(z){return zz(z).length}function t(z,J,Z){if(!J||J<1)return z;let $=Z?.hard??!1,Q=Z?.trim??!0,q=z.split(`
34
+ `),X=[];for(let j of q){if(Jz(j)<=J){X.push(Q?j.trimEnd():j);continue}if($){let Y="",U=0,F=0;while(F<j.length){let G=j.slice(F).match(/^\x1b\[[0-9;]*m/);if(G){Y+=G[0],F+=G[0].length;continue}if(U>=J)X.push(Q?Y.trimEnd():Y),Y="",U=0;Y+=j[F],U++,F++}if(Y)X.push(Q?Y.trimEnd():Y)}else X.push(Q?j.trimEnd():j)}return X.join(`
35
+ `)}class d{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;_manualLine="";state="initial";error="";value;userInput="";constructor(z,J=!0){let{input:Z=t6,output:$=o6,render:Q,signal:q,...X}=z;this.opts=X,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=Q.bind(this),this._track=J,this._abortSignal=q,this.input=Z,this.output=$}unsubscribe(){this._subscribers.clear()}setSubscriber(z,J){let Z=this._subscribers.get(z)??[];Z.push(J),this._subscribers.set(z,Z)}on(z,J){return this.setSubscriber(z,{cb:J}),this}once(z,J){return this.setSubscriber(z,{cb:J,once:!0}),this}emit(z,...J){let Z=this._subscribers.get(z)??[],$=[];for(let Q of Z)if(Q.cb(...J),Q.once)$.push(()=>Z.splice(Z.indexOf(Q),1));for(let Q of $)Q();return this}prompt(){return new Promise((z)=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),z(a);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}if(this.rl=e6.createInterface({input:this.input,tabSize:2,prompt:"",escapeCodeTimeout:50,terminal:!0}),this.rl.prompt(),this.opts.initialUserInput!==void 0)this._setUserInput(this.opts.initialUserInput,!0);this.input.on("keypress",this.onKeypress),x(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(D.show),this.output.off("resize",this.render),x(this.input,!1),z(this.value)}),this.once("cancel",()=>{this.output.write(D.show),this.output.off("resize",this.render),x(this.input,!1),z(a)})})}_isActionKey(z,J){return z==="\t"}_setValue(z){this.value=z,this.emit("value",this.value)}_setUserInput(z,J){if(this.userInput=z??"",this.emit("userInput",this.userInput),J&&this._track&&this.rl)this.rl.write(this.userInput),this._cursor=this.rl.cursor}onKeypress(z,J){if(this._track&&J.name!=="return"){if(J.name&&this._isActionKey(z,J))this.rl?.write(null,{ctrl:!0,name:"h"});this._cursor=this.rl?.cursor??0;let Z=J.name==="tab"||J.name==="escape"||J.name==="backspace"||J.name==="delete"||J.name==="enter"||J.name==="return"||J.name&&["up","down","left","right"].includes(J.name);if(z&&!Z&&z.length===1&&z>=" ")this._manualLine+=z;else if(J.name==="backspace"&&this._manualLine.length>0)this._manualLine=this._manualLine.slice(0,-1);let $=this._manualLine.length>=(this.rl?.line?.length||0)?this._manualLine:this.rl?.line;this._setUserInput($)}if(this.state==="error")this.state="active",this.error="";if(J?.name){if(!this._track&&R.aliases.has(J.name))this.emit("cursor",R.aliases.get(J.name));if(R.actions.has(J.name))this.emit("cursor",J.name)}if(z&&(z.toLowerCase()==="y"||z.toLowerCase()==="n"))this.emit("confirm",z.toLowerCase()==="y");if(this.emit("key",z?.toLowerCase(),J),J?.name==="return"){if(this.opts.validate){let Z=this.opts.validate(this.value);if(Z)this.error=Z instanceof Error?Z.message:Z,this.state="error"}if(this.state!=="error")this.state="submit"}if(Q6([z,J?.name,J?.sequence],"cancel"))this.state="cancel";if(this.state==="submit"||this.state==="cancel")this.emit("finalize");if(this.render(),this.state==="submit"||this.state==="cancel")this.close()}close(){if(this.input.unpipe(),this.input.removeListener("keypress",this.onKeypress),this.output.write(`
36
+ `),x(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.state==="cancel")setTimeout(()=>{this.unsubscribe()},10);else this.unsubscribe()}restoreCursor(){let z=t(this._prevFrame,s.stdout.columns,{hard:!0,trim:!1}).split(`
37
+ `).length-1;this.output.write(D.move(-999,z*-1))}render(){let z=t(this._render(this)??"",s.stdout.columns,{hard:!0,trim:!1});if(z===this._prevFrame)return;if(this.state==="initial")this.output.write(D.hide);else{let J=s6(this._prevFrame,z);if(this.restoreCursor(),J&&J?.length===1){let Z=J[0];this.output.write(D.move(0,Z)),this.output.write(y.lines(1));let $=z.split(`
38
+ `);this.output.write($[Z]),this._prevFrame=z,this.output.write(D.move(0,$.length-Z-1));return}if(J&&J?.length>1){let Z=J[0];this.output.write(D.move(0,Z)),this.output.write(y.down());let Q=z.split(`
39
+ `).slice(Z);this.output.write(Q.join(`
40
+ `)),this._prevFrame=z;return}this.output.write(y.down())}if(this.output.write(z),this.state==="initial")this.state="active";this._prevFrame=z}}function Zz(z,J){if(z===void 0)return 0;if(J.length===0)return 0;let $=J.findIndex((Q)=>Q.value===z);return $!==-1?$:0}function $z(z,J){return(J.label??String(J.value)).toLowerCase().includes(z.toLowerCase())}function Qz(z,J){if(!J)return;if(z)return J;return J[0]}class Yz extends d{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#z=0;#Z="";#$;#J;get cursor(){return this.#z}get userInputWithCursor(){if(!this.userInput)return A.inverse(A.hidden("_"));if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let z=this.userInput.slice(0,this._cursor),[J,...Z]=this.userInput.slice(this._cursor);return`${z}${A.inverse(J)}${Z.join("")}`}get options(){if(typeof this.#J==="function")return this.#J();return this.#J}constructor(z){super(z);this.#J=z.options;let J=this.options;this.filteredOptions=[...J],this.multiple=z.multiple===!0,this.#$=z.filter??$z;let Z;if(z.initialValue&&Array.isArray(z.initialValue))if(this.multiple)Z=z.initialValue;else Z=z.initialValue.slice(0,1);else if(!this.multiple&&this.options.length>0)Z=[this.options[0].value];if(Z)for(let $ of Z){let Q=J.findIndex((q)=>q.value===$);if(Q!==-1)this.toggleSelected($),this.#z=Q}this.focusedValue=this.options[this.#z]?.value,this.on("key",($,Q)=>this.#Q($,Q)),this.on("userInput",($)=>this.#Y($))}_isActionKey(z,J){return z==="\t"||this.multiple&&this.isNavigating&&J.name==="space"&&z!==void 0&&z!==""}#Q(z,J){let Z=J.name==="up",$=J.name==="down",Q=J.name==="return";if(Z||$){if(this.#z=Math.max(0,Math.min(this.#z+(Z?-1:1),this.filteredOptions.length-1)),this.focusedValue=this.filteredOptions[this.#z]?.value,!this.multiple)this.selectedValues=[this.focusedValue];this.isNavigating=!0}else if(Q)this.value=Qz(this.multiple,this.selectedValues);else if(this.multiple)if(this.focusedValue!==void 0&&(J.name==="tab"||this.isNavigating&&J.name==="space"))this.toggleSelected(this.focusedValue);else this.isNavigating=!1;else{if(this.focusedValue)this.selectedValues=[this.focusedValue];this.isNavigating=!1}}deselectAll(){this.selectedValues=[]}toggleSelected(z){if(this.filteredOptions.length===0)return;if(this.multiple)if(this.selectedValues.includes(z))this.selectedValues=this.selectedValues.filter((J)=>J!==z);else this.selectedValues=[...this.selectedValues,z];else this.selectedValues=[z]}#Y(z){if(z!==this.#Z){this.#Z=z;let J=this.options;if(z)this.filteredOptions=J.filter((Z)=>this.#$(z,Z));else this.filteredOptions=[...J];if(this.#z=Zz(this.focusedValue,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#z]?.value,!this.multiple)if(this.focusedValue!==void 0)this.toggleSelected(this.focusedValue);else this.deselectAll()}}}class qz extends d{options;cursor=0;#z;getGroupItems(z){return this.options.filter((J)=>J.group===z)}isGroupSelected(z){let J=this.getGroupItems(z),Z=this.value;if(Z===void 0)return!1;return J.every(($)=>Z.includes($.value))}toggleValue(){let z=this.options[this.cursor];if(this.value===void 0)this.value=[];if(z.group===!0){let J=String(z.value),Z=this.getGroupItems(J);if(this.isGroupSelected(J))this.value=this.value.filter(($)=>Z.findIndex((Q)=>Q.value===$)===-1);else this.value=[...this.value,...Z.map(($)=>$.value)];this.value=Array.from(new Set(this.value))}else{let J=this.value.includes(z.value);this.value=J?this.value.filter((Z)=>Z!==z.value):[...this.value,z.value]}}constructor(z){super(z,!1);let{options:J}=z;this.#z=z.selectableGroups!==!1,this.options=Object.entries(J).flatMap(([Z,$])=>[{value:Z,group:!0,label:Z},...$.map((Q)=>({...Q,group:Z}))]),this.value=[...z.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:Z})=>Z===z.cursorAt),this.#z?0:1),this.on("cursor",(Z)=>{switch(Z){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let $=this.options[this.cursor]?.group===!0;if(!this.#z&&$)this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let $=this.options[this.cursor]?.group===!0;if(!this.#z&&$)this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;break}case"space":this.toggleValue();break}})}}var jz=W6();var V=(z,J)=>jz?z:J,kz=V("◆","*"),Sz=V("■","x"),yz=V("▲","x"),vz=V("◇","o"),gz=V("┌","T"),Xz=V("│","|"),_z=V("└","—"),fz=V("●",">"),mz=V("○"," "),dz=V("◻","[•]"),uz=V("◼","[+]"),cz=V("◻","[ ]"),pz=V("▪","•"),nz=V("─","-"),iz=V("╮","+"),lz=V("├","+"),rz=V("╯","+"),az=V("●","•"),sz=V("◆","*"),tz=V("▲","!"),oz=V("■","x");var ez={light:V("─","-"),heavy:V("━","="),block:V("█","#")};function Uz(){return`${A.gray(Xz)} `}var z4=Uz();var u="0.1.0";var N=new m("watch");N.command("detect","Detect connected watch devices").option("--verbose","Enable verbose output").action(async(z)=>{let J=E();if(z.verbose)console.log(`Searching for connected watch devices...
41
+ `);let Z=await J.detectDevices();if(Z.length===0){console.log("No watch devices found."),console.log(`
42
+ 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 ${Z.length} device(s):
43
+ `);for(let $ of Z){if(console.log(` ${$.name}`),console.log(` Type: ${$.type}`),$.isGarminExpress)console.log(" Source: Garmin Express cache");else console.log(` Path: ${$.path}`);if($.model)console.log(` Model: ${$.model}`);if($.serial)console.log(` Serial: ${$.serial}`);if($.firmware)console.log(` Firmware: ${$.firmware}`);console.log()}});N.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(z)=>{let J=E();console.log("Detecting devices...");let Z=await J.detectDevices();if(Z.length===0)console.error("No watch devices found."),console.error(`
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();
@@ -0,0 +1,2 @@
1
+ import{createRequire as s}from"node:module";var qq=s(import.meta.url);var S=14,D=12,E=".FIT",O={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},Qq={[O.ENUM]:1,[O.SINT8]:1,[O.UINT8]:1,[O.SINT16]:2,[O.UINT16]:2,[O.SINT32]:4,[O.UINT32]:4,[O.STRING]:1,[O.FLOAT32]:4,[O.FLOAT64]:8,[O.UINT8Z]:1,[O.UINT16Z]:2,[O.UINT32Z]:4,[O.BYTE]:1,[O.SINT64]:8,[O.UINT64]:8,[O.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},$={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}},B={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},M={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},v={UNMEASURABLE:0,AWAKE:1,LIGHT:2,DEEP:3,REM:4},l=Date.UTC(1989,11,31,0,0,0),Wq={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 P extends Error{offset;constructor(Q,J){super(J!==void 0?`${Q} at offset ${J}`:Q);this.offset=J;this.name="FitParseError"}}class _{buffer;offset=0;definitions=new Map;littleEndian=!0;constructor(Q){if(Q instanceof ArrayBuffer)this.buffer=Buffer.from(Q);else if(Q instanceof Uint8Array)this.buffer=Buffer.from(Q);else this.buffer=Q}parse(){let Q=this.parseHeader(),J=[],k=Q.headerSize+Q.dataSize;while(this.offset<k)try{let X=this.parseRecord();if(X)J.push(X)}catch(X){if(X instanceof P)this.offset++;else throw X}return{header:Q,messages:J}}parseHeader(){if(this.buffer.length<D)throw new P("File too small to contain FIT header");let Q=this.buffer.readUInt8(0);if(Q!==S&&Q!==D)throw new P(`Invalid header size: ${Q}`);let J=this.buffer.readUInt8(1),k=this.buffer.readUInt16LE(2),X=this.buffer.readUInt32LE(4),C=this.buffer.toString("ascii",8,12);if(C!==E)throw new P(`Invalid FIT signature: ${C}`);let q;if(Q===S)q=this.buffer.readUInt16LE(12);return this.offset=Q,{headerSize:Q,protocolVersion:J,profileVersion:k,dataSize:X,dataType:C,crc:q}}parseRecord(){if(this.offset>=this.buffer.length)return null;let Q=this.buffer.readUInt8(this.offset);if(this.offset++,(Q&128)===128){let q=Q>>5&3,H=Q&31;return this.parseDataMessage(q,H)}let k=(Q&64)===64,X=(Q&32)===32,C=Q&15;if(k)return this.parseDefinitionMessage(C,X),null;return this.parseDataMessage(C)}parseDefinitionMessage(Q,J){let k=this.buffer.readUInt8(this.offset);this.offset++;let X=this.buffer.readUInt8(this.offset);this.offset++,this.littleEndian=X===0;let C=this.readUInt16(),q=this.buffer.readUInt8(this.offset);this.offset++;let H=[];for(let W=0;W<q;W++){let b=this.buffer.readUInt8(this.offset);this.offset++;let Z=this.buffer.readUInt8(this.offset);this.offset++;let G=this.buffer.readUInt8(this.offset);this.offset++,H.push({fieldDefNum:b,size:Z,baseType:G})}if(J){let W=this.buffer.readUInt8(this.offset);this.offset++,this.offset+=W*3}this.definitions.set(Q,{reserved:k,arch:this.littleEndian?"little":"big",globalMsgNum:C,numFields:q,fields:H})}parseDataMessage(Q,J){let k=this.definitions.get(Q);if(!k)return null;let X={};for(let C of k.fields){let q=this.readFieldValue(C);if(q!==null&&q!==void 0)X[C.fieldDefNum]=q}return{globalMsgNum:k.globalMsgNum,fields:X}}readFieldValue(Q){let{size:J,baseType:k}=Q;switch(k&31){case O.ENUM:case O.UINT8:case O.UINT8Z:{if(J===1){let q=this.buffer.readUInt8(this.offset);return this.offset++,q===255?null:q}let C=[];for(let q=0;q<J;q++)C.push(this.buffer.readUInt8(this.offset)),this.offset++;return C}case O.SINT8:{if(J===1){let q=this.buffer.readInt8(this.offset);return this.offset++,q===127?null:q}let C=[];for(let q=0;q<J;q++)C.push(this.buffer.readInt8(this.offset)),this.offset++;return C}case O.UINT16:case O.UINT16Z:{if(J===2){let q=this.readUInt16();return q===65535?null:q}let C=[];for(let q=0;q<J/2;q++)C.push(this.readUInt16());return C}case O.SINT16:{if(J===2){let q=this.readInt16();return q===32767?null:q}let C=[];for(let q=0;q<J/2;q++)C.push(this.readInt16());return C}case O.UINT32:case O.UINT32Z:{if(J===4){let q=this.readUInt32();return q===4294967295?null:q}let C=[];for(let q=0;q<J/4;q++)C.push(this.readUInt32());return C}case O.SINT32:{if(J===4){let q=this.readInt32();return q===2147483647?null:q}let C=[];for(let q=0;q<J/4;q++)C.push(this.readInt32());return C}case O.FLOAT32:{if(J===4){let q=this.readFloat32();return Number.isNaN(q)?null:q}let C=[];for(let q=0;q<J/4;q++)C.push(this.readFloat32());return C}case O.FLOAT64:{if(J===8){let q=this.readFloat64();return Number.isNaN(q)?null:q}let C=[];for(let q=0;q<J/8;q++)C.push(this.readFloat64());return C}case O.STRING:{let C=this.buffer.slice(this.offset,this.offset+J);this.offset+=J;let q=C.indexOf(0),H=C.toString("utf8",0,q>=0?q:J);return H.length>0?H:null}case O.UINT64:case O.UINT64Z:{if(J===8)return this.readUInt64();return this.offset+=J,null}case O.SINT64:{if(J===8)return this.readInt64();return this.offset+=J,null}case O.BYTE:default:{let C=this.buffer.slice(this.offset,this.offset+J);return this.offset+=J,C}}}readUInt16(){let Q=this.littleEndian?this.buffer.readUInt16LE(this.offset):this.buffer.readUInt16BE(this.offset);return this.offset+=2,Q}readInt16(){let Q=this.littleEndian?this.buffer.readInt16LE(this.offset):this.buffer.readInt16BE(this.offset);return this.offset+=2,Q}readUInt32(){let Q=this.littleEndian?this.buffer.readUInt32LE(this.offset):this.buffer.readUInt32BE(this.offset);return this.offset+=4,Q}readInt32(){let Q=this.littleEndian?this.buffer.readInt32LE(this.offset):this.buffer.readInt32BE(this.offset);return this.offset+=4,Q}readFloat32(){let Q=this.littleEndian?this.buffer.readFloatLE(this.offset):this.buffer.readFloatBE(this.offset);return this.offset+=4,Q}readFloat64(){let Q=this.littleEndian?this.buffer.readDoubleLE(this.offset):this.buffer.readDoubleBE(this.offset);return this.offset+=8,Q}readUInt64(){let Q=this.littleEndian?this.buffer.readBigUInt64LE(this.offset):this.buffer.readBigUInt64BE(this.offset);return this.offset+=8,Q}readInt64(){let Q=this.littleEndian?this.buffer.readBigInt64LE(this.offset):this.buffer.readBigInt64BE(this.offset);return this.offset+=8,Q}}function h(Q){return new Date(l+Q*1000)}function g(Q){return Q*(180/Math.pow(2,31))}function Cq(Q){return Q*(Math.pow(2,31)/180)}class f{messages;fileType="unknown";deviceInfo=null;constructor(Q){this.messages=Q.messages,this.extractFileInfo()}extractFileInfo(){let Q=this.messages.find((k)=>k.globalMsgNum===R.FILE_ID);if(Q){let k=Q.fields[$.FILE_ID.TYPE];this.fileType=this.resolveFileType(k)}let J=this.messages.find((k)=>k.globalMsgNum===R.DEVICE_INFO);if(J)this.deviceInfo={manufacturer:J.fields[$.DEVICE_INFO.MANUFACTURER],product:J.fields[$.DEVICE_INFO.PRODUCT],serialNumber:J.fields[$.DEVICE_INFO.SERIAL_NUMBER],softwareVersion:J.fields[$.DEVICE_INFO.SOFTWARE_VERSION],productName:J.fields[$.DEVICE_INFO.PRODUCT_NAME]}}resolveFileType(Q){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"}[Q]||"unknown"}getFileType(){return this.fileType}getDeviceInfo(){return this.deviceInfo}decodeActivity(){if(!["activity","activity_summary"].includes(this.fileType))return null;let Q=this.messages.filter((Z)=>Z.globalMsgNum===R.SESSION),J=this.messages.filter((Z)=>Z.globalMsgNum===R.LAP),k=this.messages.filter((Z)=>Z.globalMsgNum===R.RECORD),X=this.messages.find((Z)=>Z.globalMsgNum===R.ACTIVITY);if(Q.length===0)return null;let q=Q[0].fields,H=q[$.SESSION.START_TIME],W=q[$.SESSION.TIMESTAMP],b={id:this.generateActivityId(H),sport:this.mapSport(q[$.SESSION.SPORT]),subSport:this.mapSubSport(q[$.SESSION.SUB_SPORT]),startTime:h(H),endTime:h(W),totalElapsedTime:q[$.SESSION.TOTAL_ELAPSED_TIME]/1000,totalTimerTime:q[$.SESSION.TOTAL_TIMER_TIME]/1000,totalDistance:q[$.SESSION.TOTAL_DISTANCE]/100,totalCalories:q[$.SESSION.TOTAL_CALORIES],avgHeartRate:q[$.SESSION.AVG_HEART_RATE],maxHeartRate:q[$.SESSION.MAX_HEART_RATE],avgSpeed:q[$.SESSION.AVG_SPEED]!=null?q[$.SESSION.AVG_SPEED]/1000:void 0,maxSpeed:q[$.SESSION.MAX_SPEED]!=null?q[$.SESSION.MAX_SPEED]/1000:void 0,avgCadence:q[$.SESSION.AVG_CADENCE],maxCadence:q[$.SESSION.MAX_CADENCE],avgPower:q[$.SESSION.AVG_POWER],maxPower:q[$.SESSION.MAX_POWER],normalizedPower:q[$.SESSION.NORMALIZED_POWER],totalAscent:q[$.SESSION.TOTAL_ASCENT],totalDescent:q[$.SESSION.TOTAL_DESCENT],trainingStressScore:q[$.SESSION.TRAINING_STRESS_SCORE]!=null?q[$.SESSION.TRAINING_STRESS_SCORE]/10:void 0,intensityFactor:q[$.SESSION.INTENSITY_FACTOR]!=null?q[$.SESSION.INTENSITY_FACTOR]/1000:void 0,laps:J.map((Z)=>this.decodeLap(Z)),records:k.map((Z)=>this.decodeRecord(Z)),source:"garmin"};return this.cleanUndefined(b)}decodeLap(Q){let J=Q.fields,k=J[$.LAP.START_TIME],X=J[$.LAP.TIMESTAMP],C=J[$.LAP.START_POSITION_LAT],q=J[$.LAP.START_POSITION_LONG],H=J[$.LAP.END_POSITION_LAT],W=J[$.LAP.END_POSITION_LONG],b={startTime:h(k),endTime:h(X),totalElapsedTime:J[$.LAP.TOTAL_ELAPSED_TIME]/1000,totalTimerTime:J[$.LAP.TOTAL_TIMER_TIME]/1000,totalDistance:J[$.LAP.TOTAL_DISTANCE]/100,totalCalories:J[$.LAP.TOTAL_CALORIES],avgHeartRate:J[$.LAP.AVG_HEART_RATE],maxHeartRate:J[$.LAP.MAX_HEART_RATE],avgSpeed:J[$.LAP.AVG_SPEED]!=null?J[$.LAP.AVG_SPEED]/1000:void 0,maxSpeed:J[$.LAP.MAX_SPEED]!=null?J[$.LAP.MAX_SPEED]/1000:void 0,avgCadence:J[$.LAP.AVG_CADENCE],maxCadence:J[$.LAP.MAX_CADENCE],avgPower:J[$.LAP.AVG_POWER],maxPower:J[$.LAP.MAX_POWER],totalAscent:J[$.LAP.TOTAL_ASCENT],totalDescent:J[$.LAP.TOTAL_DESCENT],startPosition:C!=null&&q!=null?{lat:g(C),lng:g(q)}:void 0,endPosition:H!=null&&W!=null?{lat:g(H),lng:g(W)}:void 0};return this.cleanUndefined(b)}decodeRecord(Q){let J=Q.fields,k=J[$.RECORD.TIMESTAMP],X=J[$.RECORD.POSITION_LAT],C=J[$.RECORD.POSITION_LONG],q=J[$.RECORD.ENHANCED_ALTITUDE];if(q==null){if(q=J[$.RECORD.ALTITUDE],q!=null)q=q/5-500}else q=q/5-500;let H=J[$.RECORD.ENHANCED_SPEED];if(H==null){if(H=J[$.RECORD.SPEED],H!=null)H=H/1000}else H=H/1000;let W={timestamp:h(k),position:X!=null&&C!=null?{lat:g(X),lng:g(C),altitude:q}:void 0,heartRate:J[$.RECORD.HEART_RATE],cadence:J[$.RECORD.CADENCE],speed:H,power:J[$.RECORD.POWER],altitude:q,distance:J[$.RECORD.DISTANCE]!=null?J[$.RECORD.DISTANCE]/100:void 0,temperature:J[$.RECORD.TEMPERATURE],grade:J[$.RECORD.GRADE]!=null?J[$.RECORD.GRADE]/100:void 0,calories:J[$.RECORD.CALORIES]};return this.cleanUndefined(W)}decodeMonitoring(){let Q={},J=this.messages.filter((W)=>W.globalMsgNum===R.MONITORING);if(J.length>0){let W=[],b=1/0,Z=0,G=0,U=0;for(let K of J){let V=K.fields[$.MONITORING.HEART_RATE],w=K.fields[$.MONITORING.TIMESTAMP];if(V!=null&&w!=null)W.push({timestamp:h(w),heartRate:V}),b=Math.min(b,V),Z=Math.max(Z,V),G+=V,U++}if(W.length>0)Q.heartRate={date:W[0].timestamp,minHeartRate:b,maxHeartRate:Z,avgHeartRate:Math.round(G/U),samples:W}}let k=this.messages.filter((W)=>W.globalMsgNum===R.SLEEP_LEVEL);if(k.length>0){let W=[],b=null;for(let Z of k){let G=Z.fields[$.SLEEP_LEVEL.SLEEP_LEVEL],U=Z.fields[$.SLEEP_LEVEL.TIMESTAMP];if(G!=null&&U!=null){let K=this.mapSleepLevel(G),V=h(U);if(b&&b.stage!==K)b.endTime=V,W.push(b),b={stage:K,startTime:V,endTime:V};else if(!b)b={stage:K,startTime:V,endTime:V}}}if(b)W.push(b);if(W.length>0){let Z=W[0].startTime,G=W[W.length-1].endTime,U=0,K=0,V=0,w=0;for(let Y of W){let x=(Y.endTime.getTime()-Y.startTime.getTime())/60000;switch(Y.stage){case"deep":U+=x;break;case"light":K+=x;break;case"rem":V+=x;break;case"awake":w+=x;break}}Q.sleep={date:Z,startTime:Z,endTime:G,totalSleepTime:U+K+V,deepSleepTime:U,lightSleepTime:K,remSleepTime:V,awakeTime:w,stages:W}}}let X=this.messages.filter((W)=>W.globalMsgNum===R.STRESS_LEVEL);if(X.length>0){let W=[],b=0,Z=0;for(let G of X){let U=G.fields[$.STRESS_LEVEL.STRESS_LEVEL_VALUE],K=G.fields[$.STRESS_LEVEL.STRESS_LEVEL_TIME];if(U!=null&&U>=0&&U<=100&&K!=null)W.push({timestamp:h(K),stressLevel:U}),b+=U,Z=Math.max(Z,U)}if(W.length>0)Q.stress={date:W[0].timestamp,avgStressLevel:Math.round(b/W.length),maxStressLevel:Z,restStressDuration:0,lowStressDuration:0,mediumStressDuration:0,highStressDuration:0,samples:W}}let C=this.messages.filter((W)=>W.globalMsgNum===R.SPO2_DATA);if(C.length>0){let W=[],b=100,Z=0,G=0;for(let U of C){let K=U.fields[$.SPO2_DATA.READING_SPO2],V=U.fields[$.SPO2_DATA.TIMESTAMP];if(K!=null&&V!=null&&K>0&&K<=100)W.push({timestamp:h(V),spO2:K}),b=Math.min(b,K),Z=Math.max(Z,K),G+=K}if(W.length>0)Q.spO2={date:W[0].timestamp,avgSpO2:Math.round(G/W.length),minSpO2:b,maxSpO2:Z,samples:W}}let q=this.messages.filter((W)=>W.globalMsgNum===R.RESPIRATION_RATE);if(q.length>0){let W=[],b=1/0,Z=0,G=0;for(let U of q){let K=U.fields[$.RESPIRATION_RATE.RESPIRATION_RATE],V=U.fields[$.RESPIRATION_RATE.TIMESTAMP];if(K!=null&&V!=null&&K>0){let w=K/100;W.push({timestamp:h(V),respirationRate:w}),b=Math.min(b,w),Z=Math.max(Z,w),G+=w}}if(W.length>0)Q.respiration={date:W[0].timestamp,avgRespirationRate:G/W.length,minRespirationRate:b,maxRespirationRate:Z,samples:W}}let H=this.messages.filter((W)=>W.globalMsgNum===R.HRV);if(H.length>0){let W=[];for(let b of H){let Z=b.fields[$.HRV.TIME];if(Array.isArray(Z)){for(let G of Z)if(G!=null&&G!==65535)W.push({timestamp:new Date,hrv:G/1024*1000})}else if(Z!=null&&Z!==65535)W.push({timestamp:new Date,hrv:Z/1024*1000})}if(W.length>0)Q.hrv={date:new Date,samples:W}}return Q}mapSport(Q){return{[B.RUNNING]:"running",[B.CYCLING]:"cycling",[B.SWIMMING]:"swimming",[B.HIKING]:"hiking",[B.WALKING]:"walking",[B.TRAINING]:"strength_training",[B.FITNESS_EQUIPMENT]:"cardio",[B.YOGA]:"yoga",[B.GENERIC]:"generic"}[Q]||"other"}mapSubSport(Q){return{[M.GENERIC]:"generic",[M.TREADMILL]:"treadmill",[M.STREET]:"street",[M.TRAIL]:"trail",[M.TRACK]:"track",[M.SPIN]:"spin",[M.INDOOR_CYCLING]:"indoor_cycling",[M.ROAD]:"road",[M.MOUNTAIN]:"mountain",[M.GRAVEL_CYCLING]:"gravel",[M.LAP_SWIMMING]:"lap_swimming",[M.OPEN_WATER]:"open_water"}[Q]}mapSleepLevel(Q){return{[v.AWAKE]:"awake",[v.LIGHT]:"light",[v.DEEP]:"deep",[v.REM]:"rem"}[Q]||"light"}generateActivityId(Q){return`garmin_${Q}`}cleanUndefined(Q){let J={};for(let[k,X]of Object.entries(Q))if(X!==void 0&&X!==null)J[k]=X;return J}}import{existsSync as y,readdirSync as T,statSync as u,mkdirSync as p,copyFileSync as c,readFileSync as F}from"fs";import{join as N,extname as r}from"path";import{homedir as i}from"os";var m="/Volumes",d=N(i(),"Library/Application Support/Garmin/Express/RegisteredDevices"),t=["GARMIN","ENDURO","FENIX","FORERUNNER","VENU","INSTINCT","VIVOACTIVE","EPIX","TACTIX","MARQ","QUATIX","DESCENT","APPROACH","LILY","VIVOSMART","VIVOMOVE"],n={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 a{name="Garmin";type="garmin";async detectDevices(){let Q=[];if(y(m)){let k=T(m);for(let X of k){let C=N(m,X);try{if(!u(C).isDirectory())continue}catch{continue}let q=X.toUpperCase(),H=t.some((W)=>q.includes(W));if(!H){let W=N(C,"GARMIN");H=y(W)}if(H){let W=await this.getDeviceInfo(C,X);if(W)Q.push(W)}}}let J=await this.detectGarminExpressDevices();return Q.push(...J),Q}async detectGarminExpressDevices(){let Q=[];if(!y(d))return Q;let J=T(d);for(let k of J){let X=N(d,k);try{if(!u(X).isDirectory())continue}catch{continue}let C=N(X,"GarminDevice.xml");if(!y(C))continue;try{let q=F(C,"utf8"),H=q.match(/<Description>([^<]+)<\/Description>/i),W=q.match(/<Id>([^<]+)<\/Id>/i),b=q.match(/<DisplayName>([^<]+)<\/DisplayName>/i),Z=q.match(/<PartNumber>([^<]+)<\/PartNumber>/i),G=q.match(/<SoftwareVersion>([^<]+)<\/SoftwareVersion>/i),U=b?.[1]||H?.[1]||k,K=W?.[1]||k,V=G?.[1],w=N(X,"PendingSyncUploads/Garmin");if(!y(w))continue;Q.push({name:U,path:X,type:"garmin",model:U,serial:k,unitId:K,firmware:V,partNumber:Z?.[1],isGarminExpress:!0,garminExpressPath:w})}catch{}}return Q}async getDeviceInfo(Q,J){let k=N(Q,"GARMIN");if(!y(k))return null;let X=J,C,q,H=N(k,"GarminDevice.xml");if(y(H))try{let b=F(H,"utf8"),Z=b.match(/<Model>([^<]+)<\/Model>/i),G=b.match(/<Id>([^<]+)<\/Id>/i);if(Z)X=Z[1];if(G)q=G[1]}catch{}let W=N(Q,n.ACTIVITY);if(y(W)){let b=T(W).filter((Z)=>Z.toLowerCase().endsWith(".fit"));if(b.length>0)try{let Z=N(W,b[0]),G=F(Z),K=new _(G).parse(),w=new f(K).getDeviceInfo();if(w?.serialNumber)C=w.serialNumber.toString();if(w?.productName)X=w.productName}catch{}}return{name:X,path:Q,type:"garmin",model:X,serial:C,unitId:q}}async downloadData(Q,J={}){let k=Q,{outputDir:X="./garmin-data",includeActivities:C=!0,includeMonitoring:q=!0,since:H,until:W,copyRawFiles:b=!0}=J,Z={device:Q,activities:[],monitoring:new Map,rawFiles:[],errors:[]},G=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),U=N(X,G);if(b)p(U,{recursive:!0});if(k.isGarminExpress&&k.garminExpressPath)return this.downloadFromGarminExpress(k,J,Z,U);if(C){let K=N(Q.path,n.ACTIVITY);if(y(K)){let V=T(K).filter((w)=>w.toLowerCase().endsWith(".fit"));for(let w of V){let Y=N(K,w);try{let z=u(Y).mtime;if(H&&z<H)continue;if(W&&z>W)continue;let j=await this.parseActivityFile(Y);if(j)j.rawFilePath=Y,Z.activities.push(j);if(b){let A=N(U,"Activity");p(A,{recursive:!0});let L=N(A,w);c(Y,L),Z.rawFiles.push(L)}}catch(x){Z.errors.push(x instanceof Error?x:Error(String(x)))}}}}if(q){let K=N(Q.path,n.MONITOR);if(y(K)){let w=T(K).filter((Y)=>Y.toLowerCase().endsWith(".fit"));for(let Y of w){let x=N(K,Y);try{let j=u(x).mtime;if(H&&j<H)continue;if(W&&j>W)continue;let A=await this.parseMonitoringFile(x);if(A){let L=j.toISOString().slice(0,10),I=Z.monitoring.get(L)||{};Z.monitoring.set(L,{...I,...A})}if(b){let L=N(U,"Monitor");p(L,{recursive:!0});let I=N(L,Y);c(x,I),Z.rawFiles.push(I)}}catch(z){Z.errors.push(z instanceof Error?z:Error(String(z)))}}}let V=N(Q.path,"GARMIN/Sleep");if(y(V)){let w=T(V).filter((Y)=>Y.toLowerCase().endsWith(".fit"));for(let Y of w){let x=N(V,Y);try{let z=await this.parseMonitoringFile(x);if(z?.sleep){let j=z.sleep.date.toISOString().slice(0,10),A=Z.monitoring.get(j)||{};Z.monitoring.set(j,{...A,sleep:z.sleep})}if(b){let j=N(U,"Sleep");p(j,{recursive:!0});let A=N(j,Y);c(x,A),Z.rawFiles.push(A)}}catch(z){Z.errors.push(z instanceof Error?z:Error(String(z)))}}}}return Z.activities.sort((K,V)=>K.startTime.getTime()-V.startTime.getTime()),Z}async downloadFromGarminExpress(Q,J,k,X){let{includeActivities:C=!0,includeMonitoring:q=!0,since:H,until:W,copyRawFiles:b=!0}=J,Z=Q.garminExpressPath,G={Activity:N(Z,"Activity"),Monitor:N(Z,"Monitor"),Sleep:N(Z,"Sleep"),HRVStatus:N(Z,"HRVStatus"),SkinTemp:N(Z,"SkinTemp"),Metrics:N(Z,"Metrics"),Coach:N(Z,"Coach")},U=async(K,V,w)=>{if(!y(K))return;let Y=T(K).filter((x)=>x.toLowerCase().endsWith(".fit"));for(let x of Y){let z=N(K,x);try{let A=u(z).mtime;if(H&&A<H)continue;if(W&&A>W)continue;let L=await this.parseMonitoringFile(z);if(L)V(z,L);if(b){let I=N(X,w);p(I,{recursive:!0});let o=N(I,x);c(z,o),k.rawFiles.push(o)}}catch(j){k.errors.push(j instanceof Error?j:Error(String(j)))}}};if(C&&y(G.Activity)){let K=T(G.Activity).filter((V)=>V.toLowerCase().endsWith(".fit"));for(let V of K){let w=N(G.Activity,V);try{let x=u(w).mtime;if(H&&x<H)continue;if(W&&x>W)continue;let z=await this.parseActivityFile(w);if(z)z.rawFilePath=w,k.activities.push(z);if(b){let j=N(X,"Activity");p(j,{recursive:!0});let A=N(j,V);c(w,A),k.rawFiles.push(A)}}catch(Y){k.errors.push(Y instanceof Error?Y:Error(String(Y)))}}}if(q)await U(G.Monitor,(K,V)=>{let w=new Date().toISOString().slice(0,10),Y=k.monitoring.get(w)||{};k.monitoring.set(w,{...Y,...V})},"Monitor"),await U(G.HRVStatus,(K,V)=>{if(V.hrv){let w=new Date().toISOString().slice(0,10),Y=k.monitoring.get(w)||{};k.monitoring.set(w,{...Y,hrv:V.hrv})}},"HRVStatus"),await U(G.SkinTemp,(K,V)=>{let w=new Date().toISOString().slice(0,10),Y=k.monitoring.get(w)||{};k.monitoring.set(w,{...Y})},"SkinTemp"),await U(G.Sleep,(K,V)=>{if(V.sleep){let w=V.sleep.date?.toISOString().slice(0,10)||new Date().toISOString().slice(0,10),Y=k.monitoring.get(w)||{};k.monitoring.set(w,{...Y,sleep:V.sleep})}},"Sleep");return k.activities.sort((K,V)=>K.startTime.getTime()-V.startTime.getTime()),k}async parseActivityFile(Q){let J=r(Q).toLowerCase();if(J!==".fit")throw Error(`Unsupported file format: ${J}`);let k=F(Q),C=new _(k).parse();return new f(C).decodeActivity()}async parseMonitoringFile(Q){let J=r(Q).toLowerCase();if(J!==".fit")throw Error(`Unsupported file format: ${J}`);let k=F(Q),C=new _(k).parse();return new f(C).decodeMonitoring()}}function zq(){return new a}
2
+ export{qq as a,S as b,D as c,E as d,O as e,Qq as f,R as g,$ as h,B as i,M as j,v as k,l,Wq as m,P as n,_ as o,h as p,g as q,Cq as r,f as s,a as t,zq as u};
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Create a PKCE verifier and its S256 challenge.
3
+ *
4
+ * Only the challenge travels to Garmin. Because the authorization code comes
5
+ * back through the athlete's browser, someone able to read it there still
6
+ * cannot redeem it without the verifier, which never left the server.
7
+ */
8
+ export declare function createPkcePair(): PkcePair;
9
+ /**
10
+ * Read activity summaries out of a push payload.
11
+ *
12
+ * Garmin posts `{ activities: [...] }`, and has historically also posted a
13
+ * bare array. Accepting both costs nothing and avoids dropping a batch over a
14
+ * shape the caller did not anticipate. Entries without a `summaryId` are
15
+ * dropped, since without it a redelivery cannot be recognised.
16
+ */
17
+ export declare function extractActivitySummaries(body: unknown): GarminActivitySummary[];
18
+ /** Read deregistration notices out of a payload. */
19
+ export declare function extractDeregistrations(body: unknown): GarminDeregistration[];
20
+ /**
21
+ * Compare a presented webhook secret against the expected one.
22
+ *
23
+ * Constant-time: a byte-by-byte comparison leaks how much of a guess was
24
+ * correct, which is enough to construct the rest. Returns false when no secret
25
+ * is configured, so an endpoint that forgot to set one rejects everything
26
+ * rather than accepting everything.
27
+ */
28
+ export declare function isAuthenticWebhook(presented: string | null | undefined, secret: string): boolean;
29
+ /**
30
+ * Sign a value so it can be handed to a browser and trusted on return.
31
+ *
32
+ * The OAuth callback is a top-level navigation carrying no session, so the
33
+ * application has to stash who was connecting somewhere the browser holds.
34
+ * Signing is what stops someone editing that stash to attach their watch to
35
+ * another person's account.
36
+ */
37
+ export declare function signState(payload: string, secret: string): string;
38
+ /** Verify and decode a value produced by {@link signState}, or null if it was altered. */
39
+ export declare function verifyState(token: string | undefined, secret: string): string | null;
40
+ /** Is a token past, or nearly past, its expiry? */
41
+ export declare function isTokenExpired(tokens: GarminTokens, skewSeconds?: number, now?: unknown): boolean;
42
+ /** Endpoints from Garmin's OAuth 2.0 PKCE specification. */
43
+ export declare const GARMIN_ENDPOINTS: {
44
+ authorize: 'https://connect.garmin.com/oauth2Confirm';
45
+ token: 'https://diauth.garmin.com/di-oauth2-service/oauth/token';
46
+ userId: 'https://apis.garmin.com/wellness-api/rest/user/id';
47
+ permissions: 'https://apis.garmin.com/wellness-api/rest/user/permissions';
48
+ registration: 'https://apis.garmin.com/wellness-api/rest/user/registration';
49
+ backfillActivities: 'https://apis.garmin.com/wellness-api/rest/backfill/activities'
50
+ };
51
+ export declare interface GarminActivityApiConfig {
52
+ clientId: string
53
+ clientSecret: string
54
+ redirectUri: string
55
+ scope?: string
56
+ endpoints?: Partial<typeof GARMIN_ENDPOINTS>
57
+ fetch?: typeof globalThis.fetch
58
+ }
59
+ export declare interface GarminTokens {
60
+ accessToken: string
61
+ refreshToken?: string
62
+ expiresAt?: number
63
+ scope?: string
64
+ }
65
+ /** A PKCE verifier and the challenge derived from it. */
66
+ export declare interface PkcePair {
67
+ verifier: string
68
+ challenge: string
69
+ }
70
+ /**
71
+ * An activity summary as the Activity API pushes it.
72
+ *
73
+ * Field names are Garmin's. Everything except `summaryId` is optional, because
74
+ * a summary reflects what the device recorded: a treadmill run carries no GPS,
75
+ * a manually entered activity carries almost nothing.
76
+ */
77
+ export declare interface GarminActivitySummary {
78
+ summaryId: string
79
+ activityId?: number
80
+ userId?: string
81
+ userAccessToken?: string
82
+ activityType?: string
83
+ activityName?: string
84
+ startTimeInSeconds?: number
85
+ startTimeOffsetInSeconds?: number
86
+ durationInSeconds?: number
87
+ distanceInMeters?: number
88
+ activeKilocalories?: number
89
+ averageHeartRateInBeatsPerMinute?: number
90
+ maxHeartRateInBeatsPerMinute?: number
91
+ averageSpeedInMetersPerSecond?: number
92
+ maxSpeedInMetersPerSecond?: number
93
+ averageRunCadenceInStepsPerMinute?: number
94
+ averageBikeCadenceInRoundsPerMinute?: number
95
+ averagePaceInMinutesPerKilometer?: number
96
+ totalElevationGainInMeters?: number
97
+ totalElevationLossInMeters?: number
98
+ startingLatitudeInDegree?: number
99
+ startingLongitudeInDegree?: number
100
+ steps?: number
101
+ deviceName?: string
102
+ manual?: boolean
103
+ isParent?: boolean
104
+ parentSummaryId?: string
105
+ isWebUpload?: boolean
106
+ }
107
+ /** Garmin tells an application when an athlete revokes it. */
108
+ export declare interface GarminDeregistration {
109
+ userId: string
110
+ userAccessToken?: string
111
+ }
112
+ /**
113
+ * A client for the official Garmin Activity API.
114
+ *
115
+ * Holds no state beyond its configuration: tokens are passed in and returned,
116
+ * so the caller decides where they live. That keeps the client usable from a
117
+ * request handler, a job, or a test without assuming a database.
118
+ */
119
+ export declare class GarminActivityApiClient {
120
+ constructor(config: GarminActivityApiConfig);
121
+ get isConfigured(): boolean;
122
+ buildAuthorizationUrl(options: { state: string, challenge: string, scope?: string }): string;
123
+ exchangeCode(code: string, verifier: string): Promise<GarminTokens>;
124
+ refreshTokens(refreshToken: string): Promise<GarminTokens>;
125
+ getUserId(accessToken: string): Promise<string>;
126
+ getPermissions(accessToken: string): Promise<string[]>;
127
+ deregister(accessToken: string): Promise<void>;
128
+ requestBackfill(accessToken: string, start: Date, end: Date): Promise<void>;
129
+ }
@@ -0,0 +1,67 @@
1
+ import type { Activity, SleepData, StressData, BodyBattery, HRVData, WeightData } from '../types';
2
+ export declare function createGarminConnectClient(config?: GarminConnectConfig): GarminConnectClient;
3
+ export declare interface GarminConnectConfig {
4
+ username: string
5
+ password: string
6
+ }
7
+ export declare interface GarminActivitySummary {
8
+ activityId: number
9
+ activityName: string
10
+ activityType: { typeKey: string }
11
+ startTimeLocal: string
12
+ startTimeGMT: string
13
+ duration: number
14
+ distance: number
15
+ calories: number
16
+ averageHR?: number
17
+ maxHR?: number
18
+ averageSpeed?: number
19
+ maxSpeed?: number
20
+ elevationGain?: number
21
+ elevationLoss?: number
22
+ averageRunningCadenceInStepsPerMinute?: number
23
+ maxRunningCadenceInStepsPerMinute?: number
24
+ avgPower?: number
25
+ maxPower?: number
26
+ normPower?: number
27
+ trainingStressScore?: number
28
+ intensityFactor?: number
29
+ }
30
+ export declare interface DailyHeartRate {
31
+ date: Date
32
+ restingHeartRate?: number
33
+ minHeartRate?: number
34
+ maxHeartRate?: number
35
+ avgHeartRate?: number
36
+ samples?: Array<{ timestamp: Date, heartRate: number }>
37
+ }
38
+ export declare interface DailySummary {
39
+ date: Date
40
+ heartRate?: DailyHeartRate
41
+ sleep?: SleepData
42
+ stress?: StressData
43
+ bodyBattery?: BodyBattery
44
+ hrv?: HRVData
45
+ steps?: {
46
+ total: number
47
+ goal: number
48
+ distance: number
49
+ calories: number
50
+ }
51
+ }
52
+ export declare class GarminConnectClient {
53
+ constructor(config?: GarminConnectConfig);
54
+ login(username?: string, password?: string): Promise<void>;
55
+ getUserProfile(): Promise<{ displayName: string, profileId: string }>;
56
+ getActivities(start?: number, limit?: number): Promise<GarminActivitySummary[]>;
57
+ getActivity(activityId: number): Promise<Activity>;
58
+ downloadActivityFit(activityId: number, outputDir: string): Promise<void>;
59
+ getDailySummary(date: Date): Promise<DailySummary>;
60
+ getHeartRateData(date: Date): Promise<DailyHeartRate | undefined>;
61
+ getSleepData(date: Date): Promise<SleepData | undefined>;
62
+ getStepsData(date: Date): Promise<{ total: number, goal: number, distance: number, calories: number } | undefined>;
63
+ getStressData(date: Date): Promise<StressData | undefined>;
64
+ getBodyBatteryData(date: Date): Promise<BodyBattery | undefined>;
65
+ getHrvData(date: Date): Promise<HRVData | undefined>;
66
+ getWeightData(date: Date): Promise<WeightData | undefined>;
67
+ }
@@ -0,0 +1,4 @@
1
+ export * from './garmin-activity-api';
2
+ export * from './garmin-connect';
3
+ export * from './strava';
4
+ export * from './trainingpeaks';
@@ -0,0 +1,88 @@
1
+ import type { Activity } from '../types';
2
+ export declare function createStravaClient(config: StravaConfig): StravaClient;
3
+ // Helper to convert ts-watches Activity to Strava upload
4
+ export declare function activityToStravaUpload(activity: Activity): {
5
+ name: string
6
+ type: string
7
+ description: string
8
+ };
9
+ export declare interface StravaConfig {
10
+ clientId: string
11
+ clientSecret: string
12
+ redirectUri?: string
13
+ }
14
+ export declare interface StravaTokens {
15
+ accessToken: string
16
+ refreshToken: string
17
+ expiresAt: number
18
+ athleteId: number
19
+ }
20
+ export declare interface StravaAthlete {
21
+ id: number
22
+ username: string
23
+ firstname: string
24
+ lastname: string
25
+ city: string
26
+ country: string
27
+ profile: string
28
+ profileMedium: string
29
+ }
30
+ export declare interface StravaActivitySummary {
31
+ id: number
32
+ name: string
33
+ type: string
34
+ sport_type: string
35
+ start_date: string
36
+ start_date_local: string
37
+ elapsed_time: number
38
+ moving_time: number
39
+ distance: number
40
+ total_elevation_gain: number
41
+ average_speed: number
42
+ max_speed: number
43
+ average_heartrate?: number
44
+ max_heartrate?: number
45
+ average_cadence?: number
46
+ average_watts?: number
47
+ weighted_average_watts?: number
48
+ kilojoules?: number
49
+ calories?: number
50
+ }
51
+ export declare interface StravaUploadResponse {
52
+ id: number
53
+ id_str: string
54
+ external_id: string
55
+ error: string | null
56
+ status: string
57
+ activity_id: number | null
58
+ }
59
+ export declare class StravaClient {
60
+ constructor(config: StravaConfig);
61
+ getAuthorizationUrl(scope?: string): string;
62
+ exchangeCode(code: string): Promise<StravaTokens>;
63
+ refreshTokens(): Promise<StravaTokens>;
64
+ setTokens(tokens: StravaTokens): void;
65
+ getAthlete(): Promise<StravaAthlete>;
66
+ getActivities(page?: number, perPage?: number, before?: Date, after?: Date): Promise<StravaActivitySummary[]>;
67
+ getActivity(activityId: number, includeAllEfforts?: boolean): Promise<Activity>;
68
+ uploadActivity(file: ArrayBuffer, fileName: string, options?: {
69
+ name?: string
70
+ description?: string
71
+ type?: string
72
+ private?: boolean
73
+ externalId?: string
74
+ }): Promise<StravaUploadResponse>;
75
+ checkUploadStatus(uploadId: number): Promise<StravaUploadResponse>;
76
+ waitForUpload(uploadId: number, maxAttempts?: number, delayMs?: number): Promise<StravaUploadResponse>;
77
+ updateActivity(activityId: number, updates: {
78
+ name?: string
79
+ type?: string
80
+ description?: string
81
+ private?: boolean
82
+ commute?: boolean
83
+ trainer?: boolean
84
+ gearId?: string
85
+ }): Promise<Activity>;
86
+ deleteActivity(activityId: number): Promise<void>;
87
+ getActivityStreams(activityId: number, streams?: string[]): Promise<Map<string, number[]>>;
88
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * TrainingPeaks Cloud Integration
3
+ *
4
+ * Re-exports from ts-trainingpeaks package which provides
5
+ * browser-based authentication with the TrainingPeaks API.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { TrainingPeaks } from 'ts-watches/cloud'
10
+ *
11
+ * const client = new TrainingPeaks({
12
+ * username: 'your-username',
13
+ * password: 'your-password',
14
+ * cookiePath: './tp-cookies.json', // Optional: persist session
15
+ * headless: true, // Run browser headlessly
16
+ * })
17
+ *
18
+ * await client.login()
19
+ *
20
+ * // Get workouts for the current user
21
+ * const workouts = await client.getWorkouts(startDate, endDate)
22
+ *
23
+ * // For coach accounts, get coached athletes
24
+ * const athleteIds = await client.getCoachedAthleteIds()
25
+ * const athleteWorkouts = await client.getWorkouts(startDate, endDate, athleteIds[0])
26
+ * ```
27
+ */
28
+ export {
29
+ TrainingPeaks,
30
+ type TrainingPeaksConfig,
31
+ } from 'ts-trainingpeaks';
32
+ export {
33
+ type TPAthlete,
34
+ type TPAthleteGroup,
35
+ type TPCoachAthlete,
36
+ type TPWorkout,
37
+ type TPWorkoutStructureResponse,
38
+ type TPWorkoutStructureStep,
39
+ type TPWorkoutType,
40
+ type TPWorkoutTypeId,
41
+ type TPMetrics,
42
+ type TPPerformanceChart,
43
+ type TPUploadResult,
44
+ type TPLibraryWorkout,
45
+ type TPCookie,
46
+ TPWorkoutTypeIds,
47
+ getWorkoutTypeName,
48
+ } from 'ts-trainingpeaks';
@@ -0,0 +1,4 @@
1
+ import type { WatchConfig } from './types';
2
+ export declare function getConfig(): Promise<WatchConfig>;
3
+ export declare const defaultConfig: WatchConfig;
4
+ export declare const config: WatchConfig;
@@ -0,0 +1,17 @@
1
+ import type { WatchDriver, WatchDevice, DownloadOptions, DownloadResult, Activity, MonitoringData } from '../types';
2
+ export declare function createAppleDriver(): AppleDriver;
3
+ export declare interface AppleDevice extends WatchDevice {
4
+ type: 'apple'
5
+ }
6
+ export declare class AppleDriver implements WatchDriver {
7
+ readonly name: string;
8
+ readonly type: unknown;
9
+ detectDevices(): Promise<AppleDevice[]>;
10
+ downloadData(_device: WatchDevice, _options?: DownloadOptions): Promise<DownloadResult>;
11
+ parseHealthExport(exportPath: string): Promise<{
12
+ activities: Activity[]
13
+ monitoring: Map<string, MonitoringData>
14
+ }>;
15
+ parseActivityFile(_filePath: string): Promise<Activity | null>;
16
+ parseMonitoringFile(_filePath: string): Promise<MonitoringData>;
17
+ }
@@ -0,0 +1,13 @@
1
+ import type { WatchDriver, WatchDevice, DownloadOptions, DownloadResult, Activity, MonitoringData } from '../types';
2
+ export declare function createCorosDriver(): CorosDriver;
3
+ export declare interface CorosDevice extends WatchDevice {
4
+ type: 'coros'
5
+ }
6
+ export declare class CorosDriver implements WatchDriver {
7
+ readonly name: string;
8
+ readonly type: unknown;
9
+ detectDevices(): Promise<CorosDevice[]>;
10
+ downloadData(device: WatchDevice, options?: DownloadOptions): Promise<DownloadResult>;
11
+ parseActivityFile(filePath: string): Promise<Activity | null>;
12
+ parseMonitoringFile(filePath: string): Promise<MonitoringData>;
13
+ }
@@ -0,0 +1,12 @@
1
+ import type { WatchDriver, GarminDevice, WatchDevice, DownloadOptions, DownloadResult, Activity, MonitoringData } from '../types';
2
+ // Factory function
3
+ export declare function createGarminDriver(): GarminDriver;
4
+ export declare class GarminDriver implements WatchDriver {
5
+ readonly name: string;
6
+ readonly type: unknown;
7
+ detectDevices(): Promise<GarminDevice[]>;
8
+ detectGarminExpressDevices(): Promise<GarminDevice[]>;
9
+ downloadData(device: WatchDevice, options?: DownloadOptions): Promise<DownloadResult>;
10
+ parseActivityFile(filePath: string): Promise<Activity | null>;
11
+ parseMonitoringFile(filePath: string): Promise<MonitoringData>;
12
+ }
@@ -0,0 +1,6 @@
1
+ export * from './garmin';
2
+ export * from './polar';
3
+ export * from './suunto';
4
+ export * from './coros';
5
+ export * from './wahoo';
6
+ export * from './apple';
@@ -0,0 +1,14 @@
1
+ import type { WatchDriver, WatchDevice, DownloadOptions, DownloadResult, Activity, MonitoringData } from '../types';
2
+ export declare function createPolarDriver(): PolarDriver;
3
+ export declare interface PolarDevice extends WatchDevice {
4
+ type: 'polar'
5
+ deviceId?: string
6
+ }
7
+ export declare class PolarDriver implements WatchDriver {
8
+ readonly name: string;
9
+ readonly type: unknown;
10
+ detectDevices(): Promise<PolarDevice[]>;
11
+ downloadData(device: WatchDevice, options?: DownloadOptions): Promise<DownloadResult>;
12
+ parseActivityFile(filePath: string): Promise<Activity | null>;
13
+ parseMonitoringFile(_filePath: string): Promise<MonitoringData>;
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { WatchDriver, WatchDevice, DownloadOptions, DownloadResult, Activity, MonitoringData } from '../types';
2
+ export declare function createSuuntoDriver(): SuuntoDriver;
3
+ export declare interface SuuntoDevice extends WatchDevice {
4
+ type: 'suunto'
5
+ }
6
+ export declare class SuuntoDriver implements WatchDriver {
7
+ readonly name: string;
8
+ readonly type: unknown;
9
+ detectDevices(): Promise<SuuntoDevice[]>;
10
+ downloadData(device: WatchDevice, options?: DownloadOptions): Promise<DownloadResult>;
11
+ parseActivityFile(filePath: string): Promise<Activity | null>;
12
+ parseMonitoringFile(_filePath: string): Promise<MonitoringData>;
13
+ }