Gisle Aune
12 months ago
commit
35f7241738
8 changed files with 830 additions and 0 deletions
-
18.drone.yml
-
2.gitignore
-
5Dockerfile
-
71index.mjs
-
56lib/client.mjs
-
119lib/tcx.mjs
-
553package-lock.json
-
6package.json
@ -0,0 +1,18 @@ |
|||||
|
name: garmsync-build |
||||
|
kind: pipeline |
||||
|
type: docker |
||||
|
steps: |
||||
|
- name: docker-tag |
||||
|
image: plugins/docker |
||||
|
settings: |
||||
|
auto_tag: true |
||||
|
username: |
||||
|
from_secret: docker_username |
||||
|
password: |
||||
|
from_secret: docker_password |
||||
|
repo: r.vmaple.dev/gisle/garmsync |
||||
|
registry: r.vmaple.dev |
||||
|
dockerfile: Dockerfile |
||||
|
when: |
||||
|
event: |
||||
|
- tag |
@ -0,0 +1,2 @@ |
|||||
|
/node_modules |
||||
|
*.tcx |
@ -0,0 +1,5 @@ |
|||||
|
FROM node:21.5 |
||||
|
WORKDIR /garmsync |
||||
|
COPY . . |
||||
|
RUN npm install |
||||
|
CMD node index.mjs |
@ -0,0 +1,71 @@ |
|||||
|
import gc from 'garmin-connect'; |
||||
|
import Trimlog from './lib/client.mjs'; |
||||
|
import { generateTrimlogInput, today } from './lib/tcx.mjs'; |
||||
|
|
||||
|
async function main() { |
||||
|
const garmin = new gc.GarminConnect({ |
||||
|
username: process.env.GARMIN_USERNAME, |
||||
|
password: process.env.GARMIN_PASSWORD, |
||||
|
}); |
||||
|
const trimlog = new Trimlog({ |
||||
|
username: process.env.TRIMLOG_USERNAME, |
||||
|
password: process.env.TRIMLOG_PASSWORD, |
||||
|
}) |
||||
|
|
||||
|
await trimlog.refreshToken(); |
||||
|
await garmin.login(); |
||||
|
|
||||
|
const blackList = {}; |
||||
|
|
||||
|
setInterval(async() => { |
||||
|
const looseActivities = await trimlog.getLooseActivities(); |
||||
|
const recentWorkouts = await trimlog.getWorkouts(); |
||||
|
|
||||
|
const activities = await garmin.getActivities(); |
||||
|
const todayDate = today(); |
||||
|
for (const act of activities) { |
||||
|
const id = Math.round(act.activityId).toFixed(0); |
||||
|
|
||||
|
if (blackList[id]) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (!act.startTimeLocal.startsWith(todayDate)) { |
||||
|
console.error("Skipping", act.activityId, "(not today)"); |
||||
|
blackList[id] = true; |
||||
|
continue; |
||||
|
} |
||||
|
if (looseActivities.find(l => l.tags.find(t => t.key === "gc:ActivityID" && t.value === id))) { |
||||
|
console.error("Skipping", act.activityId, "(loose activity exists)"); |
||||
|
blackList[id] = true; |
||||
|
continue; |
||||
|
} |
||||
|
if (recentWorkouts.find(l => l.tags.find(t => t.key === "gc:ActivityID" && t.value === id))) { |
||||
|
console.error("Skipping", act.activityId, "(workout exists)"); |
||||
|
blackList[id] = true; |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
await garmin.downloadOriginalActivityData(act, "/tmp/", "tcx") |
||||
|
const input = await generateTrimlogInput(`/tmp/${act.activityId}.tcx`, act.activityName) |
||||
|
|
||||
|
input.tags.push({key: "gc:ActivityID", value: id}) |
||||
|
|
||||
|
await trimlog.postWorkout(input); |
||||
|
|
||||
|
blackList[id] = true; |
||||
|
} |
||||
|
}, 300000); |
||||
|
|
||||
|
process.on('SIGINT', function() { |
||||
|
console.log("INTERRUPTED"); |
||||
|
process.exit(0); |
||||
|
}); |
||||
|
|
||||
|
process.on('SIGTERM', function() { |
||||
|
console.log("TERMINATED"); |
||||
|
process.exit(0); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
main(); |
@ -0,0 +1,56 @@ |
|||||
|
export default class Trimlog { |
||||
|
constructor({username, password}) { |
||||
|
this.token = null; |
||||
|
this.tokenExpiry = new Date(0); |
||||
|
this.username = username; |
||||
|
this.password = password; |
||||
|
} |
||||
|
|
||||
|
async getLooseActivities() { |
||||
|
return this.fetch("GET", "activities"); |
||||
|
} |
||||
|
|
||||
|
async getWorkouts() { |
||||
|
return this.fetch("GET", `workouts?from=${new Date(Date.now() - 86400000 * 3).toISOString().slice(0, 10)}`); |
||||
|
} |
||||
|
|
||||
|
async postWorkout(data) { |
||||
|
return this.fetch("POST", "workouts", data); |
||||
|
} |
||||
|
|
||||
|
async fetch(method, path, body) { |
||||
|
if (new Date() > this.tokenExpiry) { |
||||
|
await this.refreshToken(); |
||||
|
} |
||||
|
|
||||
|
const res = await fetch("https://i.stifred.dev/api/"+path, { |
||||
|
method: method, |
||||
|
headers: { |
||||
|
"content-type": body ? "application/json" : void(0), |
||||
|
"authorization": `Bearer ${this.token}` |
||||
|
}, |
||||
|
body: body ? JSON.stringify(body) : void(0), |
||||
|
}) |
||||
|
|
||||
|
if (!res.ok) { |
||||
|
throw new Error(res.statusText); |
||||
|
} |
||||
|
|
||||
|
return (await res.json()).data; |
||||
|
} |
||||
|
|
||||
|
async refreshToken() { |
||||
|
const res = await fetch("https://stifred.auth.eu-west-1.amazoncognito.com/oauth2/token", { |
||||
|
method: "POST", |
||||
|
headers: { |
||||
|
"content-type": "application/x-www-form-urlencoded", |
||||
|
"authorization": `Basic ${Buffer.from(`${this.username}:${this.password}`).toString("base64")}` |
||||
|
}, |
||||
|
body: "grant_type=client_credentials&scope=indigo/api", |
||||
|
}) |
||||
|
const data = await res.json(); |
||||
|
|
||||
|
this.token = data.access_token; |
||||
|
this.tokenExpiry = new Date(Date.now() + (data.expires_in * 1000 * 0.95)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,119 @@ |
|||||
|
import * as fs from "fs/promises"; |
||||
|
import { parseString } from "xml2js"; |
||||
|
|
||||
|
export function generateTrimlogInput(filename, name = "Walk") { |
||||
|
return new Promise(async(resolve, reject) => { |
||||
|
parseString(await fs.readFile(filename, "utf-8"), (err, res) => { |
||||
|
if (err != null) { |
||||
|
reject(err); |
||||
|
} |
||||
|
|
||||
|
const data = res.TrainingCenterDatabase.Activities[0].Activity[0]; |
||||
|
const map = {}; |
||||
|
|
||||
|
const date = new Date(res.TrainingCenterDatabase.Activities[0].Activity[0].Id[0]); |
||||
|
const dateStr = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` |
||||
|
|
||||
|
let totalCalories = 0; |
||||
|
let minAltitude = null; |
||||
|
let maxAltitude = null; |
||||
|
|
||||
|
for (const lap of data.Lap) { |
||||
|
totalCalories += parseFloat(lap.Calories[0]) || 0; |
||||
|
|
||||
|
for (const tp of lap.Track[0].Trackpoint) { |
||||
|
const point = { |
||||
|
time: tp.Time[0], |
||||
|
longitude: parseFloat(tp.Position?.[0]?.LongitudeDegrees[0]), |
||||
|
latitude: parseFloat(tp.Position?.[0]?.LatitudeDegrees[0]), |
||||
|
altitude: parseFloat(tp.AltitudeMeters[0]), |
||||
|
distance: parseFloat(tp.DistanceMeters[0]), |
||||
|
heartrate: parseFloat(tp.HeartRateBpm?.[0].Value?.[0]), |
||||
|
}; |
||||
|
|
||||
|
if (minAltitude == null || point.altitude < minAltitude) { |
||||
|
minAltitude = point.altitude; |
||||
|
} |
||||
|
if (maxAltitude == null || point.altitude > maxAltitude) { |
||||
|
maxAltitude = point.altitude; |
||||
|
} |
||||
|
|
||||
|
map[tp.Time[0]] = point; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const out = { |
||||
|
date: dateStr, |
||||
|
contents: [{rawActivity: { |
||||
|
kind: "Walking", |
||||
|
sets: [], |
||||
|
effortScale: 1, |
||||
|
weight: 0, |
||||
|
measurements: Object.keys(map).sort().map(k => map[k]).map(p => ({ |
||||
|
seconds: Math.round((new Date(p.time) - date) / 1000), |
||||
|
meters: p.distance, |
||||
|
pulse: p.heartrate, |
||||
|
})) |
||||
|
}}], |
||||
|
description: name, |
||||
|
tags: [ |
||||
|
{ key: "tcx:Time", value: res.TrainingCenterDatabase.Activities[0].Activity[0].Id[0] }, |
||||
|
{ key: "tcx:TotalCalories", value: totalCalories.toFixed(1) }, |
||||
|
{ key: "tcx:MaximumAltitude", value: maxAltitude.toFixed(1) }, |
||||
|
{ key: "tcx:MinimumAltitude", value: minAltitude.toFixed(1) }, |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
resolve(out); |
||||
|
}); |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export function today() { |
||||
|
const date = new Date(); |
||||
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; |
||||
|
} |
||||
|
|
||||
|
function pad(n) {return n > 9 ? n.toString() : `0${n}`} |
||||
|
|
||||
|
/* |
||||
|
|
||||
|
{ |
||||
|
"date": "2023-12-31", |
||||
|
"contents": [ |
||||
|
{ |
||||
|
"rawActivity": { |
||||
|
"kind": "Walking", |
||||
|
"sets": [], |
||||
|
"measurements": [ |
||||
|
{ |
||||
|
"seconds": 1234, |
||||
|
"calories": 4321, |
||||
|
"meters": 432553 |
||||
|
}, |
||||
|
{ |
||||
|
"seconds": 2468, |
||||
|
"calories": 42342, |
||||
|
"meters": 3424 |
||||
|
}, |
||||
|
{ |
||||
|
"seconds": 3702, |
||||
|
"calories": 23423, |
||||
|
"meters": 2342 |
||||
|
}, |
||||
|
{ |
||||
|
"seconds": 4936, |
||||
|
"calories": 23423, |
||||
|
"meters": 2342 |
||||
|
} |
||||
|
], |
||||
|
"effortScale": 1, |
||||
|
"weight": 0 |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"description": "asdfasdfasdf", |
||||
|
"tags": [] |
||||
|
} |
||||
|
|
||||
|
*/ |
@ -0,0 +1,553 @@ |
|||||
|
{ |
||||
|
"name": "garmsync", |
||||
|
"lockfileVersion": 2, |
||||
|
"requires": true, |
||||
|
"packages": { |
||||
|
"": { |
||||
|
"dependencies": { |
||||
|
"garmin-connect": "^1.6.1", |
||||
|
"xml2js": "^0.6.2" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/app-root-path": { |
||||
|
"version": "3.1.0", |
||||
|
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", |
||||
|
"integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", |
||||
|
"engines": { |
||||
|
"node": ">= 6.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/asynckit": { |
||||
|
"version": "0.4.0", |
||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", |
||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" |
||||
|
}, |
||||
|
"node_modules/axios": { |
||||
|
"version": "1.6.3", |
||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", |
||||
|
"integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", |
||||
|
"dependencies": { |
||||
|
"follow-redirects": "^1.15.0", |
||||
|
"form-data": "^4.0.0", |
||||
|
"proxy-from-env": "^1.1.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/call-bind": { |
||||
|
"version": "1.0.5", |
||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", |
||||
|
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", |
||||
|
"dependencies": { |
||||
|
"function-bind": "^1.1.2", |
||||
|
"get-intrinsic": "^1.2.1", |
||||
|
"set-function-length": "^1.1.1" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/combined-stream": { |
||||
|
"version": "1.0.8", |
||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", |
||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", |
||||
|
"dependencies": { |
||||
|
"delayed-stream": "~1.0.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 0.8" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/crypto": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", |
||||
|
"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", |
||||
|
"deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." |
||||
|
}, |
||||
|
"node_modules/define-data-property": { |
||||
|
"version": "1.1.1", |
||||
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", |
||||
|
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", |
||||
|
"dependencies": { |
||||
|
"get-intrinsic": "^1.2.1", |
||||
|
"gopd": "^1.0.1", |
||||
|
"has-property-descriptors": "^1.0.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 0.4" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/delayed-stream": { |
||||
|
"version": "1.0.0", |
||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", |
||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", |
||||
|
"engines": { |
||||
|
"node": ">=0.4.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/follow-redirects": { |
||||
|
"version": "1.15.3", |
||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", |
||||
|
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", |
||||
|
"funding": [ |
||||
|
{ |
||||
|
"type": "individual", |
||||
|
"url": "https://github.com/sponsors/RubenVerborgh" |
||||
|
} |
||||
|
], |
||||
|
"engines": { |
||||
|
"node": ">=4.0" |
||||
|
}, |
||||
|
"peerDependenciesMeta": { |
||||
|
"debug": { |
||||
|
"optional": true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/form-data": { |
||||
|
"version": "4.0.0", |
||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", |
||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", |
||||
|
"dependencies": { |
||||
|
"asynckit": "^0.4.0", |
||||
|
"combined-stream": "^1.0.8", |
||||
|
"mime-types": "^2.1.12" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 6" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/function-bind": { |
||||
|
"version": "1.1.2", |
||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", |
||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/garmin-connect": { |
||||
|
"version": "1.6.1", |
||||
|
"resolved": "https://registry.npmjs.org/garmin-connect/-/garmin-connect-1.6.1.tgz", |
||||
|
"integrity": "sha512-ILYhV1G6UNEXiAj+pbPfhOPb9J8mi/QJnGiJOkg2DkRutpFJrwnG73JzzlBY5hbU1R/KpcDNNC1Sl/zilnZhZg==", |
||||
|
"dependencies": { |
||||
|
"app-root-path": "^3.1.0", |
||||
|
"axios": "^1.5.1", |
||||
|
"crypto": "^1.0.1", |
||||
|
"form-data": "^4.0.0", |
||||
|
"lodash": "^4.17.21", |
||||
|
"luxon": "^3.4.3", |
||||
|
"oauth-1.0a": "^2.2.6", |
||||
|
"qs": "^6.11.2" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/get-intrinsic": { |
||||
|
"version": "1.2.2", |
||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", |
||||
|
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", |
||||
|
"dependencies": { |
||||
|
"function-bind": "^1.1.2", |
||||
|
"has-proto": "^1.0.1", |
||||
|
"has-symbols": "^1.0.3", |
||||
|
"hasown": "^2.0.0" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/gopd": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", |
||||
|
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", |
||||
|
"dependencies": { |
||||
|
"get-intrinsic": "^1.1.3" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/has-property-descriptors": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", |
||||
|
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", |
||||
|
"dependencies": { |
||||
|
"get-intrinsic": "^1.2.2" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/has-proto": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", |
||||
|
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", |
||||
|
"engines": { |
||||
|
"node": ">= 0.4" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/has-symbols": { |
||||
|
"version": "1.0.3", |
||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", |
||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", |
||||
|
"engines": { |
||||
|
"node": ">= 0.4" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/hasown": { |
||||
|
"version": "2.0.0", |
||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", |
||||
|
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", |
||||
|
"dependencies": { |
||||
|
"function-bind": "^1.1.2" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 0.4" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/lodash": { |
||||
|
"version": "4.17.21", |
||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", |
||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" |
||||
|
}, |
||||
|
"node_modules/luxon": { |
||||
|
"version": "3.4.4", |
||||
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", |
||||
|
"integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", |
||||
|
"engines": { |
||||
|
"node": ">=12" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/mime-db": { |
||||
|
"version": "1.52.0", |
||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", |
||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", |
||||
|
"engines": { |
||||
|
"node": ">= 0.6" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/mime-types": { |
||||
|
"version": "2.1.35", |
||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", |
||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", |
||||
|
"dependencies": { |
||||
|
"mime-db": "1.52.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 0.6" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/oauth-1.0a": { |
||||
|
"version": "2.2.6", |
||||
|
"resolved": "https://registry.npmjs.org/oauth-1.0a/-/oauth-1.0a-2.2.6.tgz", |
||||
|
"integrity": "sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==" |
||||
|
}, |
||||
|
"node_modules/object-inspect": { |
||||
|
"version": "1.13.1", |
||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", |
||||
|
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/proxy-from-env": { |
||||
|
"version": "1.1.0", |
||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", |
||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" |
||||
|
}, |
||||
|
"node_modules/qs": { |
||||
|
"version": "6.11.2", |
||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", |
||||
|
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", |
||||
|
"dependencies": { |
||||
|
"side-channel": "^1.0.4" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=0.6" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/sax": { |
||||
|
"version": "1.3.0", |
||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", |
||||
|
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" |
||||
|
}, |
||||
|
"node_modules/set-function-length": { |
||||
|
"version": "1.1.1", |
||||
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", |
||||
|
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", |
||||
|
"dependencies": { |
||||
|
"define-data-property": "^1.1.1", |
||||
|
"get-intrinsic": "^1.2.1", |
||||
|
"gopd": "^1.0.1", |
||||
|
"has-property-descriptors": "^1.0.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 0.4" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/side-channel": { |
||||
|
"version": "1.0.4", |
||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", |
||||
|
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", |
||||
|
"dependencies": { |
||||
|
"call-bind": "^1.0.0", |
||||
|
"get-intrinsic": "^1.0.2", |
||||
|
"object-inspect": "^1.9.0" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/ljharb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/xml2js": { |
||||
|
"version": "0.6.2", |
||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", |
||||
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", |
||||
|
"dependencies": { |
||||
|
"sax": ">=0.6.0", |
||||
|
"xmlbuilder": "~11.0.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=4.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/xmlbuilder": { |
||||
|
"version": "11.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", |
||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", |
||||
|
"engines": { |
||||
|
"node": ">=4.0" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"app-root-path": { |
||||
|
"version": "3.1.0", |
||||
|
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", |
||||
|
"integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==" |
||||
|
}, |
||||
|
"asynckit": { |
||||
|
"version": "0.4.0", |
||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", |
||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" |
||||
|
}, |
||||
|
"axios": { |
||||
|
"version": "1.6.3", |
||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", |
||||
|
"integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", |
||||
|
"requires": { |
||||
|
"follow-redirects": "^1.15.0", |
||||
|
"form-data": "^4.0.0", |
||||
|
"proxy-from-env": "^1.1.0" |
||||
|
} |
||||
|
}, |
||||
|
"call-bind": { |
||||
|
"version": "1.0.5", |
||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", |
||||
|
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", |
||||
|
"requires": { |
||||
|
"function-bind": "^1.1.2", |
||||
|
"get-intrinsic": "^1.2.1", |
||||
|
"set-function-length": "^1.1.1" |
||||
|
} |
||||
|
}, |
||||
|
"combined-stream": { |
||||
|
"version": "1.0.8", |
||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", |
||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", |
||||
|
"requires": { |
||||
|
"delayed-stream": "~1.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"crypto": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", |
||||
|
"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" |
||||
|
}, |
||||
|
"define-data-property": { |
||||
|
"version": "1.1.1", |
||||
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", |
||||
|
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", |
||||
|
"requires": { |
||||
|
"get-intrinsic": "^1.2.1", |
||||
|
"gopd": "^1.0.1", |
||||
|
"has-property-descriptors": "^1.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"delayed-stream": { |
||||
|
"version": "1.0.0", |
||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", |
||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" |
||||
|
}, |
||||
|
"follow-redirects": { |
||||
|
"version": "1.15.3", |
||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", |
||||
|
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" |
||||
|
}, |
||||
|
"form-data": { |
||||
|
"version": "4.0.0", |
||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", |
||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", |
||||
|
"requires": { |
||||
|
"asynckit": "^0.4.0", |
||||
|
"combined-stream": "^1.0.8", |
||||
|
"mime-types": "^2.1.12" |
||||
|
} |
||||
|
}, |
||||
|
"function-bind": { |
||||
|
"version": "1.1.2", |
||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", |
||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" |
||||
|
}, |
||||
|
"garmin-connect": { |
||||
|
"version": "1.6.1", |
||||
|
"resolved": "https://registry.npmjs.org/garmin-connect/-/garmin-connect-1.6.1.tgz", |
||||
|
"integrity": "sha512-ILYhV1G6UNEXiAj+pbPfhOPb9J8mi/QJnGiJOkg2DkRutpFJrwnG73JzzlBY5hbU1R/KpcDNNC1Sl/zilnZhZg==", |
||||
|
"requires": { |
||||
|
"app-root-path": "^3.1.0", |
||||
|
"axios": "^1.5.1", |
||||
|
"crypto": "^1.0.1", |
||||
|
"form-data": "^4.0.0", |
||||
|
"lodash": "^4.17.21", |
||||
|
"luxon": "^3.4.3", |
||||
|
"oauth-1.0a": "^2.2.6", |
||||
|
"qs": "^6.11.2" |
||||
|
} |
||||
|
}, |
||||
|
"get-intrinsic": { |
||||
|
"version": "1.2.2", |
||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", |
||||
|
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", |
||||
|
"requires": { |
||||
|
"function-bind": "^1.1.2", |
||||
|
"has-proto": "^1.0.1", |
||||
|
"has-symbols": "^1.0.3", |
||||
|
"hasown": "^2.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"gopd": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", |
||||
|
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", |
||||
|
"requires": { |
||||
|
"get-intrinsic": "^1.1.3" |
||||
|
} |
||||
|
}, |
||||
|
"has-property-descriptors": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", |
||||
|
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", |
||||
|
"requires": { |
||||
|
"get-intrinsic": "^1.2.2" |
||||
|
} |
||||
|
}, |
||||
|
"has-proto": { |
||||
|
"version": "1.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", |
||||
|
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" |
||||
|
}, |
||||
|
"has-symbols": { |
||||
|
"version": "1.0.3", |
||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", |
||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" |
||||
|
}, |
||||
|
"hasown": { |
||||
|
"version": "2.0.0", |
||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", |
||||
|
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", |
||||
|
"requires": { |
||||
|
"function-bind": "^1.1.2" |
||||
|
} |
||||
|
}, |
||||
|
"lodash": { |
||||
|
"version": "4.17.21", |
||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", |
||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" |
||||
|
}, |
||||
|
"luxon": { |
||||
|
"version": "3.4.4", |
||||
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", |
||||
|
"integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==" |
||||
|
}, |
||||
|
"mime-db": { |
||||
|
"version": "1.52.0", |
||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", |
||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" |
||||
|
}, |
||||
|
"mime-types": { |
||||
|
"version": "2.1.35", |
||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", |
||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", |
||||
|
"requires": { |
||||
|
"mime-db": "1.52.0" |
||||
|
} |
||||
|
}, |
||||
|
"oauth-1.0a": { |
||||
|
"version": "2.2.6", |
||||
|
"resolved": "https://registry.npmjs.org/oauth-1.0a/-/oauth-1.0a-2.2.6.tgz", |
||||
|
"integrity": "sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==" |
||||
|
}, |
||||
|
"object-inspect": { |
||||
|
"version": "1.13.1", |
||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", |
||||
|
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" |
||||
|
}, |
||||
|
"proxy-from-env": { |
||||
|
"version": "1.1.0", |
||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", |
||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" |
||||
|
}, |
||||
|
"qs": { |
||||
|
"version": "6.11.2", |
||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", |
||||
|
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", |
||||
|
"requires": { |
||||
|
"side-channel": "^1.0.4" |
||||
|
} |
||||
|
}, |
||||
|
"sax": { |
||||
|
"version": "1.3.0", |
||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", |
||||
|
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" |
||||
|
}, |
||||
|
"set-function-length": { |
||||
|
"version": "1.1.1", |
||||
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", |
||||
|
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", |
||||
|
"requires": { |
||||
|
"define-data-property": "^1.1.1", |
||||
|
"get-intrinsic": "^1.2.1", |
||||
|
"gopd": "^1.0.1", |
||||
|
"has-property-descriptors": "^1.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"side-channel": { |
||||
|
"version": "1.0.4", |
||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", |
||||
|
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", |
||||
|
"requires": { |
||||
|
"call-bind": "^1.0.0", |
||||
|
"get-intrinsic": "^1.0.2", |
||||
|
"object-inspect": "^1.9.0" |
||||
|
} |
||||
|
}, |
||||
|
"xml2js": { |
||||
|
"version": "0.6.2", |
||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", |
||||
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", |
||||
|
"requires": { |
||||
|
"sax": ">=0.6.0", |
||||
|
"xmlbuilder": "~11.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"xmlbuilder": { |
||||
|
"version": "11.0.1", |
||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", |
||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"dependencies": { |
||||
|
"garmin-connect": "^1.6.1", |
||||
|
"xml2js": "^0.6.2" |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue