From 3df16ee995df99852746cea1f851dd3897481b38 Mon Sep 17 00:00:00 2001 From: Jp Date: Fri, 30 Jan 2026 23:32:43 +0800 Subject: [PATCH] initial commit --- .gitignore | 1 + Dockerfile | 24 + data/calorie_tracker.db | Bin 0 -> 77824 bytes data/sessions.db | Bin 0 -> 12288 bytes models/index.js | 191 +++ package-lock.json | 3232 +++++++++++++++++++++++++++++++++++++++ package.json | 30 + scripts/seed.js | 358 +++++ server.js | 734 +++++++++ utils/api_client.js | 213 +++ utils/index.js | 340 ++++ views/add_meal.ejs | 237 +++ views/dashboard.ejs | 294 ++++ views/foods.ejs | 104 ++ views/goals.ejs | 117 ++ views/layout.ejs | 104 ++ views/login.ejs | 26 + views/meal_planner.ejs | 253 +++ views/progress.ejs | 117 ++ views/register.ejs | 30 + 20 files changed, 6405 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 data/calorie_tracker.db create mode 100644 data/sessions.db create mode 100644 models/index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/seed.js create mode 100644 server.js create mode 100644 utils/api_client.js create mode 100644 utils/index.js create mode 100644 views/add_meal.ejs create mode 100644 views/dashboard.ejs create mode 100644 views/foods.ejs create mode 100644 views/goals.ejs create mode 100644 views/layout.ejs create mode 100644 views/login.ejs create mode 100644 views/meal_planner.ejs create mode 100644 views/progress.ejs create mode 100644 views/register.ejs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6926b35 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM node:18-alpine + +WORKDIR /app + +# Install dependencies +COPY package*.json ./ +RUN npm install + +# Copy application code +COPY . . + +# Create data directory +RUN mkdir -p /app/data + +# Expose port +EXPOSE 5000 + +# Healthcheck +RUN apk add --no-cache curl +HEALTHCHECK --interval=30s --timeout=3s \ + CMD curl -f http://localhost:5000/ || exit 1 + +# Start server +CMD ["npm", "start"] diff --git a/data/calorie_tracker.db b/data/calorie_tracker.db new file mode 100644 index 0000000000000000000000000000000000000000..e2825ac11f443b5a514ddc7b98e531e68c3ef3b9 GIT binary patch literal 77824 zcmeI4O>7(2wZ~^D@k7*CqL`v&S<$$TEYY&;p=8OD+q9u%%3(#xj6~H@3YfYgN7O{a z8JaV+tfD|MvE39|T`T@Ld+(pr@iY~l(ugJDQ-YN**_C27BqS?H2FF8X} zB*&x3LH&%wM8n~n%i*8D^D*bnaJ>0WQZ^)EPb(FOAzwUOKq{OFE{%|I6-g20H>_-xuBwJEuIq=6>J*k9KcvyZzz4 zZhv@ejQ=odkX=RE(xuWZjVQXk(zCn~kEP>6I<}OI3p@7q9bs}u&g}?^^>qAJd_zcW zB-UaZ+rnymTZnC?ZztB_jcf7s^krcu4|~iQM@4Bz_+D&d`R&-otUIkEORSyI~7TaB6#>qhMnqt%%Cx*>Eyo@S~tVJU-4pjxnF76r!q9Zbj2_di{ELqvz{QCvpD0GEoiLII6nR z{?;|$MXBdb2WU+q3KiH6l4U;;C{RVABn+w0Zoihj)qXeQ75BFCz;+kQy2m{8fUVA6 z2{y8~x1R(&ji^er+05Y^6@XNWT&#;lS74hRqx<^-fB5`)zRLCvDu_Ih9X-RwJ$8O zhqAA7ggtNPIxDp_r`d|Xk(j;xB*a`6S!-A(Um^uaUn}^sMA1sJL=#`t%LSVEmdQGl zHd`tO&*tqk!kf3Au0&8M?P@T!wW_~u4}g$TR^4v5Ps`qFzfN_1&g&0{!~CO!?Q5OM z^ELa}+O^%GICW58wU#$zMPrFMokN~)&3-)A$#UeQUdGL)Z?AY8`Pth~k|QdiHMQ1& zLI+w`Kd3bWB^fdSy2koZv;spF%WEa*VJyR(7-8Ehd2_tC0h5(CRtsBecSP3iSe+-! zvtve@L2cf*C+|X|*~X}s^Q65|%gNiPgdljtZHq$3_6C~htfnlnKqpWDasj0 z4`MeX3ek;BwV|$c+tgcMy$6q^I(*@&DSn&9r+!ZXMHyYXSC-VQbnInkL#!S%PSQBL z4DXCQ9N}55_;HBslO`0Kb?Kgl_p=$-iE2d|_S6{b3w5C`Y^^8W*`jSlk?8uNR)Sh@ z1vSi7nsTCMt-6?JR$IjK}XipIcxWdfHi)D9%aJ0loHvW9#grUn^Bx-Y@p zsivdWY)%w3*gxc7-SjR;WS9zC2d%GT&J~GxeBn34JSW4qdr)^v?U6i3e}*DL*LRPH%oUzH)!>y|vA) zjg+A$jJd+bqC>XUTW9eN{-2vLeLbyn{?~nsti7|LKf4Ge0dxr)NbWI(K7c z{)V`4<@(}+FeQpNMA6wA1f6{U|8Vd>K9B$sKmter2_OL^fCP{L5t;V0v|{K2_OL^fCP{L55F3{y|^C`$w<8bGzdY9V4C( z_|N%u?#aIrh8Z3Tal^x-JUjw{06#L?72pzyM6z5c%A~R^NTnmV$%EuNgWj2_P?V*t zWZk)Q>=zN@jk(1u*A`}5|6bxmR&yX6;zA*+tgBvG3U0!nf0nBEJOh(%L|t$5BaVtF zmsa!KV2B$X9rYZ;wIuJtk5dka^=ygbKJ#Am3Y?Y3{pPtm0a>(Bm0)sT{ zcC|yr(U~)jx$V+wexn=2MViE=w9*ZBW3qBRahm5M3IyAFMw)+|NHBnOWY?>p?Zt6Bh08{ z3cIwLqg`;=8EewHN=ni+UDzv3-w%fdhPi*b)aTeQMBQ1?%ZeR&(4N;v&ttyu2@G4&)%`VJ4R71B`^Q;Flsrf)yX?KZKc$bKaX)7|g{-C>NI6HAj`OS8>rRQ!tT2Ti7^2tz;UE3O2NFO6 zNB{{S0VIF~kN^@u0!RP}AOR%s5);7r|4Zy#{Ax%52_OL^fCP{L5$}?jpS}Op{mo!UAng0X`(fvl=hxit!FbZg z!+YKS@YoptVbmbIinOImrCSVjaY-#n;o9g`GU8&KO5UX-D{8Y-9QD*v90H z7!hv9S7KYqv@lVUvRa`y0V54k%1cJ(P?GceM&=;DBdjEE$LKx|CFwvp%CKG8+iSZJ zugulGzgW@?NmeuPeoLSAVbM9U_BmNn+SOZ)ni1%d9Wus zE?bgdNG@jV2sgoH>BL%GSihYX*0+*LFqextCo}bUq7L>(d?mgSUtf-Ig4e;OSG^Gt zZm$EAWE_HGIkveRyBVijwo>#ntu2uceiZbF$H)1{F&0-Tg{Z2OThX+fUccVm7+v+I zlQ{ognW%qSSMz1GFX)g$is3$+Dja6sV$55{6W0w_nTNYQLNDihEmm zV7rTD_G6xTz*c9k1RL4g+fM?XMpPx*Z07Kd3P7qwF4o1OtY>ICDiXx&j*w2QZ$l1E zMr-*><5`6SgD;(qze|(zbk2Lu(f$2^KYac?UuAoTm6D{E*K2ECjdW{<)8jo}x$4pH zYPq8fEha^&l$BJ2!f?b_~8oI0qlTFV=p#!a}AJm$Gk_?#uU1R+yT7jX8<+T#@FqUCXjIiyMygA<6fXT`m ztA(w#J0fd$tj?3=*)b!{pf>N@lXs!fY-7~RdD7mf<>Yc9Q)u^IsdQgf^BG@J@L*K zZ7YgI*AKN4)OstZVXo4Y6E$nqy{v1g&Z=N9c}dMlrTS4c2KFlxxNMStI>0%bJQ}3+iP^Vb-(&-$$QW-=yB!M`8h{*?yTnMYawoIVgw?MB|*5F1S{}k z-<%N7=TmS4nx^K3gaJ1P5~WZE$?y66kPK2j@0benqNBd3E2}v*5aQk(8L38{$4b() zmW7+=ghScb7dGUqBmhM!mu{~K_5AA6x6kn(gRd31i^@^$oFgxnRx|o)h>MRksZ9dG zJW*wxG%Kbyy)RD5O-nNmNr~1Ml2MUz<-KXS?Xk$9P)RCz#t8~UF}eDbmoj> zZo9Oa-{=N$ktT5|t#lx)%L?2+U0Ff^E|FdBs3LOcTu&c=*NsTWh=Y3T&{x=2b37R0 zCMH5wYK&Pujg;NjRAH4I5=BndzW}NQ{_n4QE^(hkh3YNNjxeK+DeTf}j&{LeXRJx* zDk(|RbYZVBeLox;80P-zQlDeL5OrrY!vWeGHtAcF)N)R%ESa*m27AuX{BT1~bZo<1 zTFtZm5O+S@q>P2(rmV|(wqWA^JRG<{>xwIJj&w zsyS}0=0qp(yxO|vY{KCVnhNlDxTe4*SB7sWsm9a z{Ozao@iXq{OsA05v;!&U$kK6sHGADD@oQE(|L^SG;rj3P{d*r>!3PpR0!RP}AOR$R z1dsp{Kmter34A32=J|6WZae~KeF6>FDl2o;|c%>RV4gU$tWvRAiT}I?S5jtI6eO+)Q>eBju<0DjN literal 0 HcmV?d00001 diff --git a/data/sessions.db b/data/sessions.db new file mode 100644 index 0000000000000000000000000000000000000000..9e11b4e79cd13910389d6d3cd60be9f90620b881 GIT binary patch literal 12288 zcmeI$&u-H&90zc_gLXoi!e!d2!mRRL8jT4{@>aVchXJF8g}VpnRZ zdKgcFco`&Kfd_#jPEpQ11K*!)`A>dMq)$#i_upJ7BiT5~?u21=@|aLc&KM(v?AyL$ zdlA%59ux~Z=Id>-PmZ#$d(JOXax`(Y^KIJ!Jcj@TAOHafKmY;|fB*y_009X6BLY2I zKB!jd(ufbS)KY7e%yco9+P$zf2-%=@ei5=_mp#{N#Jass)aqTc*WtCx9J!mE z7ssXYLAy$cnvLW_e~fKSKNNG5Y}Ui#HAAnM<#qjT?zv|>bzTwM5D1Rwwb2tWV= z5P$##AOL~?Q{eu6`C(M~tmos=M5KLn)sD=kX}{aOI_<{9 literal 0 HcmV?d00001 diff --git a/models/index.js b/models/index.js new file mode 100644 index 0000000..7a56ef1 --- /dev/null +++ b/models/index.js @@ -0,0 +1,191 @@ +const Sequelize = require('sequelize'); +const path = require('path'); +const bcrypt = require('bcryptjs'); + +// Initialize database +const dbPath = process.env.DATABASE_URL || 'sqlite://calorie_tracker.db'; +const storagePath = dbPath.startsWith('sqlite://') + ? dbPath.replace('sqlite://', '') + : path.join(__dirname, '../data/calorie_tracker.db'); + +// Ensure storage path is absolute or relative to cwd correctly +// For Docker, we use /app/data/calorie_tracker.db +const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: storagePath.startsWith('/') ? storagePath : path.join(process.cwd(), 'data', 'calorie_tracker.db'), + logging: false +}); + +const db = {}; + +// User Model +const User = sequelize.define('User', { + username: { type: Sequelize.STRING(80), unique: true, allowNull: false }, + password: { type: Sequelize.STRING(200), allowNull: false }, + name: Sequelize.STRING(100), + age: Sequelize.INTEGER, + gender: Sequelize.STRING(10), + height_cm: Sequelize.FLOAT, + weight_kg: Sequelize.FLOAT, + activity_level: { type: Sequelize.STRING(20), defaultValue: 'moderate' }, + target_daily_calories: { type: Sequelize.INTEGER, defaultValue: 2000 } +}); + +// Method to verify password +User.prototype.validPassword = function(password) { + return bcrypt.compareSync(password, this.password); +}; + +// Hook to hash password +User.beforeCreate(async (user) => { + if (user.password) { + user.password = await bcrypt.hash(user.password, 10); + } +}); + +// FoodItem Model +const FoodItem = sequelize.define('FoodItem', { + name: { type: Sequelize.STRING(200), allowNull: false }, + name_tagalog: Sequelize.STRING(200), + category: Sequelize.STRING(50), + calories: { type: Sequelize.FLOAT, allowNull: false }, + protein_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + carbs_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + fat_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + fiber_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + sugar_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + sodium_mg: { type: Sequelize.FLOAT, defaultValue: 0 }, + serving_size_g: { type: Sequelize.FLOAT, defaultValue: 100 }, + serving_description: Sequelize.STRING(100), + source: { type: Sequelize.STRING(20), defaultValue: 'manual' }, + is_filipino: { type: Sequelize.BOOLEAN, defaultValue: false }, + is_favorite: { type: Sequelize.BOOLEAN, defaultValue: false }, + api_data: Sequelize.TEXT, + last_updated: { type: Sequelize.DATE, defaultValue: Sequelize.NOW } +}); + +// Meal Model +const Meal = sequelize.define('Meal', { + date: { type: Sequelize.DATEONLY, allowNull: false, defaultValue: Sequelize.NOW }, + meal_type: { type: Sequelize.STRING(20), allowNull: false }, + time: Sequelize.TIME, + notes: Sequelize.TEXT +}); + +// MealFood Model +const MealFood = sequelize.define('MealFood', { + quantity: { type: Sequelize.FLOAT, allowNull: false, defaultValue: 1.0 }, + quantity_grams: Sequelize.FLOAT, + calories_consumed: Sequelize.FLOAT, + protein_consumed: Sequelize.FLOAT, + carbs_consumed: Sequelize.FLOAT, + fat_consumed: Sequelize.FLOAT +}); + +// WaterLog Model +const WaterLog = sequelize.define('WaterLog', { + date: { type: Sequelize.DATEONLY, allowNull: false, defaultValue: Sequelize.NOW }, + amount_ml: { type: Sequelize.INTEGER, allowNull: false }, + time: { type: Sequelize.TIME, defaultValue: Sequelize.NOW } +}); + +// WeightLog Model +const WeightLog = sequelize.define('WeightLog', { + date: { type: Sequelize.DATEONLY, allowNull: false, defaultValue: Sequelize.NOW }, + weight_kg: { type: Sequelize.FLOAT, allowNull: false }, + body_fat_percentage: Sequelize.FLOAT, + notes: Sequelize.TEXT, + time: { type: Sequelize.TIME, defaultValue: Sequelize.NOW } +}); + +// MealPlan Model +const MealPlan = sequelize.define('MealPlan', { + date: { type: Sequelize.DATEONLY, allowNull: false }, + meal_type: { type: Sequelize.STRING(20), allowNull: false }, + is_completed: { type: Sequelize.BOOLEAN, defaultValue: false }, + notes: Sequelize.TEXT +}); + +// PlannedFood Model +const PlannedFood = sequelize.define('PlannedFood', { + quantity: { type: Sequelize.FLOAT, allowNull: false, defaultValue: 1.0 } +}); + +// UserGoal Model +const UserGoal = sequelize.define('UserGoal', { + goal_type: { type: Sequelize.STRING(20), defaultValue: 'recomp' }, + target_weight_kg: Sequelize.FLOAT, + weekly_goal_kg: { type: Sequelize.FLOAT, defaultValue: 0.5 }, + target_protein_g: { type: Sequelize.INTEGER, defaultValue: 150 }, + target_carbs_g: { type: Sequelize.INTEGER, defaultValue: 200 }, + target_fat_g: { type: Sequelize.INTEGER, defaultValue: 60 }, + target_water_ml: { type: Sequelize.INTEGER, defaultValue: 2000 } +}); + +// DailySummary Model +const DailySummary = sequelize.define('DailySummary', { + date: { type: Sequelize.DATEONLY, allowNull: false }, + total_calories: { type: Sequelize.FLOAT, defaultValue: 0 }, + total_protein_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + total_carbs_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + total_fat_g: { type: Sequelize.FLOAT, defaultValue: 0 }, + total_water_ml: { type: Sequelize.INTEGER, defaultValue: 0 }, + calories_remaining: Sequelize.FLOAT, + weight_kg: Sequelize.FLOAT, + notes: Sequelize.TEXT +}); + +// APICache Model +const APICache = sequelize.define('APICache', { + query: { type: Sequelize.STRING(200), allowNull: false, unique: true }, + api_source: Sequelize.STRING(50), + response_json: Sequelize.TEXT, + cached_at: { type: Sequelize.DATE, defaultValue: Sequelize.NOW } +}); + +// Relationships +User.hasMany(Meal, { onDelete: 'CASCADE' }); +Meal.belongsTo(User); + +User.hasMany(WeightLog, { onDelete: 'CASCADE' }); +WeightLog.belongsTo(User); + +User.hasMany(WaterLog, { onDelete: 'CASCADE' }); +WaterLog.belongsTo(User); + +User.hasMany(MealPlan, { onDelete: 'CASCADE' }); +MealPlan.belongsTo(User); + +User.hasOne(UserGoal, { onDelete: 'CASCADE' }); +UserGoal.belongsTo(User); + +Meal.hasMany(MealFood, { onDelete: 'CASCADE' }); +MealFood.belongsTo(Meal); + +FoodItem.hasMany(MealFood); +MealFood.belongsTo(FoodItem); + +MealPlan.hasMany(PlannedFood, { onDelete: 'CASCADE' }); +PlannedFood.belongsTo(MealPlan); + +FoodItem.hasMany(PlannedFood); +PlannedFood.belongsTo(FoodItem); + +User.hasMany(DailySummary, { onDelete: 'CASCADE' }); +DailySummary.belongsTo(User); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; +db.User = User; +db.FoodItem = FoodItem; +db.Meal = Meal; +db.MealFood = MealFood; +db.WaterLog = WaterLog; +db.WeightLog = WeightLog; +db.MealPlan = MealPlan; +db.PlannedFood = PlannedFood; +db.UserGoal = UserGoal; +db.DailySummary = DailySummary; +db.APICache = APICache; + +module.exports = db; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..65f9e3f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3232 @@ +{ + "name": "calorie-tracker", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "calorie-tracker", + "version": "1.0.0", + "dependencies": { + "axios": "^1.6.0", + "bcryptjs": "^2.4.3", + "connect-sqlite3": "^0.9.13", + "dotenv": "^16.3.1", + "ejs": "^3.1.9", + "express": "^4.18.2", + "express-ejs-layouts": "^2.5.1", + "express-flash": "^0.0.2", + "express-session": "^1.17.3", + "method-override": "^3.0.0", + "passport": "^0.6.0", + "passport-local": "^1.0.0", + "sequelize": "^6.33.0", + "sqlite3": "^5.1.6" + }, + "devDependencies": { + "nodemon": "^3.0.1" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", + "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/connect-sqlite3": { + "version": "0.9.16", + "resolved": "https://registry.npmjs.org/connect-sqlite3/-/connect-sqlite3-0.9.16.tgz", + "integrity": "sha512-2gqo0QmcBBL8p8+eqpBETn7RgM/PaoKvpQGl8PfjEgwlr0VuMYNMxRJRrRCo3KR3fxMYeSsCw2tGNG0JKN9Nvg==", + "dependencies": { + "sqlite3": "^5.0.2" + }, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-ejs-layouts": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/express-ejs-layouts/-/express-ejs-layouts-2.5.1.tgz", + "integrity": "sha512-IXROv9n3xKga7FowT06n1Qn927JR8ZWDn5Dc9CJQoiiaaDqbhW5PDmWShzbpAa2wjWT1vJqaIM1S6vJwwX11gA==" + }, + "node_modules/express-flash": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz", + "integrity": "sha512-QVUR0ZZRCaa8+iPHoUQaQJrQWcQuK/Q+19M7IUIdIEtvwhrA/ifHT7y1CVJI41YfGiOQnbGtn3uvd2vOdgu58A==", + "dependencies": { + "connect-flash": "0.1.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.19.0.tgz", + "integrity": "sha512-0csaMkGq+vaiZTmSMMGkfdCOabYv192VbytFypcvI0MANrp+4i/7yEkJ0sbAEhycQjntaKGzYfjfXQyVb7BHMA==", + "license": "MIT", + "dependencies": { + "cookie": "~0.7.2", + "cookie-signature": "~1.0.7", + "debug": "~2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.1.0", + "parseurl": "~1.3.3", + "safe-buffer": "~5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC", + "optional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "optional": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "optional": true + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "license": "MIT", + "dependencies": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "license": "MIT", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/pg-connection-string": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.10.1.tgz", + "integrity": "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/sequelize": { + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sequelize/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..bd7448d --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "calorie-tracker", + "version": "1.0.0", + "description": "Calorie Tracker - Filipino Food Edition", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js", + "seed": "node scripts/seed.js" + }, + "dependencies": { + "axios": "^1.6.0", + "bcryptjs": "^2.4.3", + "connect-sqlite3": "^0.9.13", + "dotenv": "^16.3.1", + "ejs": "^3.1.9", + "express": "^4.18.2", + "express-ejs-layouts": "^2.5.1", + "express-flash": "^0.0.2", + "express-session": "^1.17.3", + "method-override": "^3.0.0", + "passport": "^0.6.0", + "passport-local": "^1.0.0", + "sequelize": "^6.33.0", + "sqlite3": "^5.1.6" + }, + "devDependencies": { + "nodemon": "^3.0.1" + } +} diff --git a/scripts/seed.js b/scripts/seed.js new file mode 100644 index 0000000..fa1430b --- /dev/null +++ b/scripts/seed.js @@ -0,0 +1,358 @@ +const db = require('../models'); + +const filipinoFoods = [ + // Rice (Kanin) + { + name: 'White Rice', + name_tagalog: 'Kanin', + category: 'kanin', + calories: 206, + protein_g: 4.3, + carbs_g: 45, + fat_g: 0.4, + serving_description: '1 cup cooked', + serving_size_g: 158 + }, + { + name: 'Fried Rice', + name_tagalog: 'Sinangag', + category: 'kanin', + calories: 280, + protein_g: 5, + carbs_g: 42, + fat_g: 10, + serving_description: '1 cup', + serving_size_g: 170 + }, + + // Main Dishes (Ulam) + { + name: 'Chicken Adobo', + name_tagalog: 'Adobong Manok', + category: 'ulam', + calories: 350, + protein_g: 35, + carbs_g: 5, + fat_g: 20, + serving_description: '1 serving (2 pieces)', + serving_size_g: 200 + }, + { + name: 'Pork Sinigang', + name_tagalog: 'Sinigang na Baboy', + category: 'sabaw', + calories: 280, + protein_g: 25, + carbs_g: 10, + fat_g: 15, + serving_description: '1 bowl', + serving_size_g: 350 + }, + { + name: 'Chicken Tinola', + name_tagalog: 'Tinolang Manok', + category: 'sabaw', + calories: 200, + protein_g: 28, + carbs_g: 8, + fat_g: 6, + serving_description: '1 bowl', + serving_size_g: 350 + }, + { + name: 'Bicol Express', + name_tagalog: 'Bicol Express', + category: 'ulam', + calories: 400, + protein_g: 20, + carbs_g: 10, + fat_g: 32, + serving_description: '1 serving', + serving_size_g: 200 + }, + { + name: 'Pork Sisig', + name_tagalog: 'Sisig', + category: 'ulam', + calories: 450, + protein_g: 25, + carbs_g: 8, + fat_g: 35, + serving_description: '1 serving', + serving_size_g: 180 + }, + { + name: 'Menudo', + name_tagalog: 'Menudo', + category: 'ulam', + calories: 320, + protein_g: 22, + carbs_g: 12, + fat_g: 20, + serving_description: '1 serving', + serving_size_g: 200 + }, + { + name: 'Kare-Kare', + name_tagalog: 'Kare-Kare', + category: 'ulam', + calories: 380, + protein_g: 24, + carbs_g: 18, + fat_g: 25, + serving_description: '1 serving', + serving_size_g: 250 + }, + { + name: 'Lechon Kawali', + name_tagalog: 'Lechon Kawali', + category: 'ulam', + calories: 500, + protein_g: 30, + carbs_g: 2, + fat_g: 42, + serving_description: '1 serving', + serving_size_g: 150 + }, + { + name: 'Pork Nilaga', + name_tagalog: 'Nilagang Baboy', + category: 'sabaw', + calories: 280, + protein_g: 28, + carbs_g: 12, + fat_g: 14, + serving_description: '1 bowl', + serving_size_g: 350 + }, + { + name: 'Beef Bulalo', + name_tagalog: 'Bulalo', + category: 'sabaw', + calories: 350, + protein_g: 32, + carbs_g: 8, + fat_g: 22, + serving_description: '1 bowl', + serving_size_g: 400 + }, + + // Vegetables (Gulay) + { + name: 'Pinakbet', + name_tagalog: 'Pinakbet', + category: 'gulay', + calories: 150, + protein_g: 5, + carbs_g: 20, + fat_g: 6, + serving_description: '1 cup', + serving_size_g: 200 + }, + { + name: 'Laing', + name_tagalog: 'Laing', + category: 'gulay', + calories: 180, + protein_g: 6, + carbs_g: 15, + fat_g: 12, + serving_description: '1 cup', + serving_size_g: 180 + }, + { + name: 'Ginisang Monggo', + name_tagalog: 'Ginisang Monggo', + category: 'gulay', + calories: 200, + protein_g: 12, + carbs_g: 30, + fat_g: 4, + serving_description: '1 cup', + serving_size_g: 220 + }, + + // Breakfast (Almusal) + { + name: 'Beef Tapa with Rice and Egg', + name_tagalog: 'Tapsilog', + category: 'almusal', + calories: 650, + protein_g: 45, + carbs_g: 60, + fat_g: 25, + serving_description: '1 plate', + serving_size_g: 400 + }, + { + name: 'Longganisa with Rice and Egg', + name_tagalog: 'Longsilog', + category: 'almusal', + calories: 700, + protein_g: 38, + carbs_g: 65, + fat_g: 32, + serving_description: '1 plate', + serving_size_g: 420 + }, + { + name: 'Tocino with Rice and Egg', + name_tagalog: 'Tocilog', + category: 'almusal', + calories: 680, + protein_g: 42, + carbs_g: 62, + fat_g: 28, + serving_description: '1 plate', + serving_size_g: 400 + }, + { + name: 'Fried Egg', + name_tagalog: 'Pritong Itlog', + category: 'almusal', + calories: 90, + protein_g: 6, + carbs_g: 1, + fat_g: 7, + serving_description: '1 egg', + serving_size_g: 50 + }, + + // Snacks (Meryenda) + { + name: 'Pandesal', + name_tagalog: 'Pandesal', + category: 'meryenda', + calories: 120, + protein_g: 3, + carbs_g: 22, + fat_g: 2, + serving_description: '1 piece', + serving_size_g: 40 + }, + { + name: 'Turon', + name_tagalog: 'Turon', + category: 'meryenda', + calories: 180, + protein_g: 2, + carbs_g: 35, + fat_g: 5, + serving_description: '1 piece', + serving_size_g: 80 + }, + { + name: 'Bibingka', + name_tagalog: 'Bibingka', + category: 'meryenda', + calories: 220, + protein_g: 5, + carbs_g: 38, + fat_g: 6, + serving_description: '1 piece', + serving_size_g: 100 + }, + { + name: 'Puto', + name_tagalog: 'Puto', + category: 'meryenda', + calories: 90, + protein_g: 2, + carbs_g: 18, + fat_g: 1, + serving_description: '1 piece', + serving_size_g: 40 + }, + { + name: 'Lumpia', + name_tagalog: 'Lumpia', + category: 'meryenda', + calories: 100, + protein_g: 4, + carbs_g: 10, + fat_g: 5, + serving_description: '1 piece', + serving_size_g: 50 + }, + { + name: 'Banana Cue', + name_tagalog: 'Banana Cue', + category: 'meryenda', + calories: 150, + protein_g: 1, + carbs_g: 32, + fat_g: 4, + serving_description: '1 piece', + serving_size_g: 100 + }, + + // Proteins + { + name: 'Grilled Tilapia', + name_tagalog: 'Inihaw na Tilapia', + category: 'ulam', + calories: 180, + protein_g: 32, + carbs_g: 0, + fat_g: 5, + serving_description: '1 whole fish', + serving_size_g: 150 + }, + { + name: 'Grilled Chicken', + name_tagalog: 'Inihaw na Manok', + category: 'ulam', + calories: 280, + protein_g: 40, + carbs_g: 0, + fat_g: 13, + serving_description: '1 breast', + serving_size_g: 150 + }, + { + name: 'Fried Bangus', + name_tagalog: 'Pritong Bangus', + category: 'ulam', + calories: 220, + protein_g: 28, + carbs_g: 0, + fat_g: 12, + serving_description: '1 piece', + serving_size_g: 120 + } +]; + +async function seed() { + try { + await db.sequelize.sync(); + + let addedCount = 0; + + for (const foodData of filipinoFoods) { + // Check if already exists + const existing = await db.FoodItem.findOne({ + where: { + name: foodData.name, + is_filipino: true + } + }); + + if (!existing) { + await db.FoodItem.create({ + ...foodData, + source: 'filipino', + is_filipino: true + }); + addedCount++; + } + } + + console.log(`Successfully added ${addedCount} Filipino foods to the database!`); + } catch (error) { + console.error('Error seeding Filipino foods:', error); + } finally { + await db.sequelize.close(); + } +} + +seed(); diff --git a/server.js b/server.js new file mode 100644 index 0000000..5ee31e0 --- /dev/null +++ b/server.js @@ -0,0 +1,734 @@ +require('dotenv').config(); +const express = require('express'); +const session = require('express-session'); +const passport = require('passport'); +const LocalStrategy = require('passport-local').Strategy; +const expressLayouts = require('express-ejs-layouts'); +const flash = require('express-flash'); +const methodOverride = require('method-override'); +const path = require('path'); +const SQLiteStore = require('connect-sqlite3')(session); +const db = require('./models'); +const utils = require('./utils'); +const { NutritionAPI, searchAllSources } = require('./utils/api_client'); + +const app = express(); +const PORT = process.env.PORT || 5001; + +// Initialize API Client +const apiClient = new NutritionAPI(process.env.API_NINJAS_KEY); + +// Passport Config +passport.use(new LocalStrategy( + async (username, password, done) => { + try { + const user = await db.User.findOne({ where: { username } }); + if (!user) { + return done(null, false, { message: 'Incorrect username.' }); + } + if (!user.validPassword(password)) { + return done(null, false, { message: 'Incorrect password.' }); + } + return done(null, user); + } catch (err) { + return done(err); + } + } +)); + +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +passport.deserializeUser(async (id, done) => { + try { + const user = await db.User.findByPk(id); + done(null, user); + } catch (err) { + done(err); + } +}); + +// Middleware +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); +app.set('layout', 'layout'); +app.use(expressLayouts); +app.use(express.static(path.join(__dirname, 'public'))); +app.use(express.urlencoded({ extended: true })); +app.use(express.json()); +app.use(methodOverride('_method')); + +// Session +app.use(session({ + store: new SQLiteStore({ db: 'sessions.db', dir: './data' }), + secret: process.env.SECRET_KEY || 'secret', + resave: false, + saveUninitialized: false, + cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 } // 30 days +})); + +app.use(passport.initialize()); +app.use(passport.session()); +app.use(flash()); + +// Global Variables +app.use((req, res, next) => { + res.locals.current_user = req.user; + res.locals.success_msg = req.flash('success_msg'); + res.locals.error_msg = req.flash('error_msg'); + res.locals.error = req.flash('error'); + res.locals.path = req.path; + next(); +}); + +// Helper functions for templates +app.locals.round = Math.round; + +// Auth Middleware +function ensureAuthenticated(req, res, next) { + if (req.isAuthenticated()) { + return next(); + } + req.flash('error_msg', 'Please log in to view that resource'); + res.redirect('/login'); +} + +// Routes +app.get('/', (req, res) => { + if (req.isAuthenticated()) { + res.redirect('/dashboard'); + } else { + res.redirect('/login'); + } +}); + +app.get('/login', (req, res) => { + res.render('login'); +}); + +app.post('/login', passport.authenticate('local', { + successRedirect: '/dashboard', + failureRedirect: '/login', + failureFlash: true +})); + +app.get('/register', (req, res) => { + res.render('register'); +}); + +app.post('/register', async (req, res) => { + const { username, password, name } = req.body; + try { + const existingUser = await db.User.findOne({ where: { username } }); + if (existingUser) { + req.flash('error', 'Username already exists'); + return res.redirect('/register'); + } + await db.User.create({ username, password, name }); + req.flash('success_msg', 'You are now registered and can log in'); + res.redirect('/login'); + } catch (err) { + console.error(err); + req.flash('error', 'Error registering user'); + res.redirect('/register'); + } +}); + +app.get('/logout', (req, res, next) => { + req.logout((err) => { + if (err) { return next(err); } + req.flash('success_msg', 'You are logged out'); + res.redirect('/login'); + }); +}); + +app.get('/dashboard', ensureAuthenticated, async (req, res) => { + try { + const today = new Date(); + const dateStr = today.toISOString().split('T')[0]; + + // Get daily totals + const nutrition = await utils.calculateDailyTotals(req.user.id, dateStr); + const water = await utils.calculateWaterTotal(req.user.id, dateStr); + + // Get user goals + let goals = await db.UserGoal.findOne({ where: { user_id: req.user.id } }); + if (!goals) { + goals = await db.UserGoal.create({ + user_id: req.user.id, + target_protein_g: 150, + target_carbs_g: 200, + target_fat_g: 60, + target_water_ml: 2000 + }); + } + + // Get weight info + const weightLogToday = await db.WeightLog.findOne({ + where: { user_id: req.user.id, date: dateStr } + }); + + const yesterday = new Date(today); + yesterday.setDate(yesterday.getDate() - 1); + const yesterdayStr = yesterday.toISOString().split('T')[0]; + + const weightLogYesterday = await db.WeightLog.findOne({ + where: { user_id: req.user.id, date: yesterdayStr } + }); + + let weightChange = null; + if (weightLogToday && weightLogYesterday) { + weightChange = weightLogToday.weight_kg - weightLogYesterday.weight_kg; + } + + // Calculate remaining + const remaining = { + calories: req.user.target_daily_calories - nutrition.calories, + protein: goals.target_protein_g - nutrition.protein, + carbs: goals.target_carbs_g - nutrition.carbs, + fat: goals.target_fat_g - nutrition.fat, + water: goals.target_water_ml - water.total_ml + }; + + // Get macro percentages + const macroPercentages = utils.getMacroPercentages( + nutrition.protein, + nutrition.carbs, + nutrition.fat + ); + + // Get trends + const weightTrend = await utils.getWeightTrend(req.user.id, 7); + const calorieTrend = await utils.getCalorieTrend(req.user.id, 7); + + // Suggestions + const suggestions = utils.suggestFoodsForMacros( + remaining.protein, + remaining.carbs, + remaining.fat + ); + + res.render('dashboard', { + nutrition, + water, + goals, + remaining, + macro_percentages: macroPercentages, + weight_today: weightLogToday, + weight_change: weightChange, + weight_trend: weightTrend, + calorie_trend: calorieTrend, + suggestions, + today: dateStr + }); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error loading dashboard'); + res.render('dashboard', { + nutrition: { calories: 0, protein: 0, carbs: 0, fat: 0 }, + water: { total_ml: 0 }, + goals: { target_water_ml: 2000 }, + remaining: { calories: 2000, protein: 150, carbs: 200, fat: 60, water: 2000 }, + macro_percentages: { protein: 0, carbs: 0, fat: 0 }, + weight_trend: [], + calorie_trend: [], + suggestions: [], + today: new Date().toISOString().split('T')[0] + }); + } +}); + +app.get('/add-meal', ensureAuthenticated, (req, res) => { + res.render('add_meal', { today: new Date().toISOString().split('T')[0] }); +}); + +app.post('/add-meal', ensureAuthenticated, async (req, res) => { + try { + let { date, meal_type, time, 'food_id[]': foodIds, 'quantity[]': quantities } = req.body; + + if (!foodIds) { + req.flash('error_msg', 'Please add at least one food item'); + return res.redirect('/add-meal'); + } + + // Normalize input (handle single item vs array) + if (!Array.isArray(foodIds)) { + foodIds = [foodIds]; + quantities = [quantities]; + } + + const meal = await db.Meal.create({ + user_id: req.user.id, + date: date || new Date(), + meal_type, + time: time || null + }); + + for (let i = 0; i < foodIds.length; i++) { + const foodId = foodIds[i]; + const quantity = quantities[i]; + + if (foodId && quantity) { + const food = await db.FoodItem.findByPk(foodId); + if (food) { + await db.MealFood.create({ + MealId: meal.id, + FoodItemId: foodId, + quantity: quantity, + calories_consumed: food.calories * quantity, + protein_consumed: food.protein_g * quantity, + carbs_consumed: food.carbs_g * quantity, + fat_consumed: food.fat_g * quantity + }); + } + } + } + + await utils.updateDailySummary(req.user.id, date); + + req.flash('success_msg', 'Meal added successfully!'); + res.redirect('/dashboard'); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error adding meal'); + res.redirect('/add-meal'); + } +}); + +app.get('/api/search-food', ensureAuthenticated, async (req, res) => { + const query = req.query.q || ''; + if (query.length < 2) { + return res.json([]); + } + + try { + const results = await searchAllSources(query, apiClient); + res.json(results); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}); + +app.post('/api/add-food', ensureAuthenticated, async (req, res) => { + try { + const data = req.body; + const food = await apiClient.saveFoodToDb(data); + + if (food) { + res.json({ + success: true, + food_id: food.id, + name: food.name + }); + } else { + res.status(500).json({ success: false }); + } + } catch (err) { + console.error(err); + res.status(500).json({ success: false }); + } +}); + +app.post('/add-water', ensureAuthenticated, async (req, res) => { + try { + const amountMl = parseInt(req.body.amount_ml) || 250; + const date = req.body.date || new Date().toISOString().split('T')[0]; + + await db.WaterLog.create({ + user_id: req.user.id, + date: date, + amount_ml: amountMl, + time: new Date() + }); + + await utils.updateDailySummary(req.user.id, date); + + req.flash('success_msg', `Added ${amountMl}ml of water!`); + res.redirect('/dashboard'); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error adding water'); + res.redirect('/dashboard'); + } +}); + +app.post('/add-weight', ensureAuthenticated, async (req, res) => { + try { + const weightKg = parseFloat(req.body.weight_kg); + const date = req.body.date || new Date().toISOString().split('T')[0]; + + let weightLog = await db.WeightLog.findOne({ + where: { user_id: req.user.id, date: date } + }); + + if (weightLog) { + weightLog.weight_kg = weightKg; + weightLog.time = new Date(); + await weightLog.save(); + } else { + await db.WeightLog.create({ + user_id: req.user.id, + date: date, + weight_kg: weightKg, + time: new Date() + }); + } + + await utils.updateDailySummary(req.user.id, date); + + res.redirect('/dashboard'); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error adding weight'); + res.redirect('/dashboard'); + } +}); + +app.get('/goals', ensureAuthenticated, async (req, res) => { + try { + const userGoals = await db.UserGoal.findOne({ where: { user_id: req.user.id } }); + + let bmr = null; + let tdee = null; + + if (req.user.weight_kg && req.user.height_cm && req.user.age) { + bmr = utils.calculateBMR( + req.user.weight_kg, + req.user.height_cm, + req.user.age, + req.user.gender || 'male' + ); + tdee = utils.calculateTDEE(bmr, req.user.activity_level || 'moderate'); + } + + res.render('goals', { + goals: userGoals, + bmr, + tdee + }); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error loading goals'); + res.redirect('/dashboard'); + } +}); + +app.post('/goals', ensureAuthenticated, async (req, res) => { + try { + // Update user info + const user = await db.User.findByPk(req.user.id); + user.age = parseInt(req.body.age) || 25; + user.gender = req.body.gender || 'male'; + user.height_cm = parseFloat(req.body.height_cm) || 170; + user.weight_kg = parseFloat(req.body.weight_kg) || 70; + user.activity_level = req.body.activity_level || 'moderate'; + + // Calculate targets + const bmr = utils.calculateBMR(user.weight_kg, user.height_cm, user.age, user.gender); + const tdee = utils.calculateTDEE(bmr, user.activity_level); + + const goalType = req.body.goal_type || 'recomp'; + let targetCalories; + + if (goalType === 'weight_loss') { + targetCalories = tdee - 500; + } else if (goalType === 'muscle_gain') { + targetCalories = tdee + 300; + } else { + targetCalories = tdee; + } + + user.target_daily_calories = Math.round(targetCalories); + await user.save(); + + // Update or create goals + let userGoals = await db.UserGoal.findOne({ where: { user_id: req.user.id } }); + if (!userGoals) { + userGoals = await db.UserGoal.create({ user_id: req.user.id }); + } + + userGoals.goal_type = goalType; + userGoals.target_weight_kg = parseFloat(req.body.target_weight_kg) || 70; + + // Calculate macros + const macros = utils.calculateMacroTargets(user.weight_kg, goalType); + userGoals.target_protein_g = macros.protein_g; + userGoals.target_carbs_g = macros.carbs_g; + userGoals.target_fat_g = macros.fat_g; + userGoals.target_water_ml = parseInt(req.body.target_water_ml) || 2000; + + await userGoals.save(); + + req.flash('success_msg', 'Goals updated successfully!'); + res.redirect('/goals'); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error updating goals'); + res.redirect('/goals'); + } +}); + +app.get('/foods', ensureAuthenticated, async (req, res) => { + try { + const category = req.query.category || 'all'; + const searchQuery = req.query.q || ''; + + const whereClause = {}; + + if (category !== 'all') { + whereClause.category = category; + } + + if (searchQuery) { + whereClause[db.Sequelize.Op.or] = [ + { name: { [db.Sequelize.Op.like]: `%${searchQuery}%` } }, + { name_tagalog: { [db.Sequelize.Op.like]: `%${searchQuery}%` } } + ]; + } + + const filipinoWhere = { ...whereClause, is_filipino: true }; + const otherWhere = { ...whereClause, is_filipino: false }; + + const filipinoFoods = await db.FoodItem.findAll({ + where: filipinoWhere, + order: [['name', 'ASC']] + }); + + const otherFoods = await db.FoodItem.findAll({ + where: otherWhere, + limit: 20, + order: [['name', 'ASC']] + }); + + const categories = ['all', 'kanin', 'ulam', 'sabaw', 'gulay', 'meryenda', 'almusal']; + + res.render('foods', { + filipino_foods: filipinoFoods, + other_foods: otherFoods, + categories, + current_category: category, + search_query: searchQuery + }); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error loading foods'); + res.redirect('/dashboard'); + } +}); + +app.get('/progress', ensureAuthenticated, async (req, res) => { + try { + const days = parseInt(req.query.days) || 30; + + const weightTrend = await utils.getWeightTrend(req.user.id, days); + const calorieTrend = await utils.getCalorieTrend(req.user.id, days); + + let avgCalories = 0, avgProtein = 0, avgCarbs = 0, avgFat = 0; + + if (calorieTrend.length > 0) { + avgCalories = calorieTrend.reduce((sum, d) => sum + d.calories, 0) / calorieTrend.length; + avgProtein = calorieTrend.reduce((sum, d) => sum + d.protein, 0) / calorieTrend.length; + avgCarbs = calorieTrend.reduce((sum, d) => sum + d.carbs, 0) / calorieTrend.length; + avgFat = calorieTrend.reduce((sum, d) => sum + d.fat, 0) / calorieTrend.length; + } + + let weightChange = null; + if (weightTrend.length >= 2) { + weightChange = weightTrend[weightTrend.length - 1].weight_kg - weightTrend[0].weight_kg; + } + + res.render('progress', { + weight_trend: weightTrend, + calorie_trend: calorieTrend, + avg_calories: Math.round(avgCalories), + avg_protein: Math.round(avgProtein), + avg_carbs: Math.round(avgCarbs), + avg_fat: Math.round(avgFat), + weight_change: weightChange, + days + }); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error loading progress'); + res.redirect('/dashboard'); + } +}); + +app.get('/meal-planner', ensureAuthenticated, async (req, res) => { + try { + const today = new Date(); + // Calculate Monday of current week + const day = today.getDay(); + const diff = today.getDate() - day + (day === 0 ? -6 : 1); // adjust when day is sunday + const startDate = new Date(today); + startDate.setDate(diff); + + const dates = []; + for (let i = 0; i < 7; i++) { + const d = new Date(startDate); + d.setDate(startDate.getDate() + i); + dates.push(d); + } + + const mealPlans = {}; + + for (const d of dates) { + const dateStr = d.toISOString().split('T')[0]; + + const plans = await db.MealPlan.findAll({ + where: { user_id: req.user.id, date: dateStr }, + include: [{ + model: db.PlannedFood, + include: [db.FoodItem] + }] + }); + + mealPlans[dateStr] = plans.map(p => { + let calories = 0, protein = 0, carbs = 0, fat = 0; + const foods = []; + + if (p.PlannedFoods) { + p.PlannedFoods.forEach(pf => { + if (pf.FoodItem) { + const q = pf.quantity || 1; + calories += pf.FoodItem.calories * q; + protein += pf.FoodItem.protein_g * q; + carbs += pf.FoodItem.carbs_g * q; + fat += pf.FoodItem.fat_g * q; + + foods.push({ + name: pf.FoodItem.name, + quantity: q + }); + } + }); + } + + return { + id: p.id, + meal_type: p.meal_type, + is_completed: p.is_completed, + foods: foods, + totals: { + calories, protein, carbs, fat + } + }; + }); + } + + res.render('meal_planner', { + dates: dates.map(d => d.toISOString().split('T')[0]), + meal_plans: mealPlans, + today: new Date().toISOString().split('T')[0] + }); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error loading meal planner'); + res.redirect('/dashboard'); + } +}); + +app.post('/meal-planner/add', ensureAuthenticated, async (req, res) => { + try { + let { date, meal_type, 'food_id[]': foodIds, 'quantity[]': quantities } = req.body; + + if (!foodIds) { + req.flash('error_msg', 'Please add at least one food item'); + return res.redirect('/meal-planner'); + } + + // Normalize input + if (!Array.isArray(foodIds)) { + foodIds = [foodIds]; + quantities = [quantities]; + } + + // Find or create meal plan + let mealPlan = await db.MealPlan.findOne({ + where: { + user_id: req.user.id, + date: date, + meal_type: meal_type + } + }); + + if (!mealPlan) { + mealPlan = await db.MealPlan.create({ + user_id: req.user.id, + date: date, + meal_type: meal_type + }); + } + + // Add foods + for (let i = 0; i < foodIds.length; i++) { + const foodId = foodIds[i]; + const quantity = quantities[i]; + + if (foodId && quantity) { + await db.PlannedFood.create({ + MealPlanId: mealPlan.id, + FoodItemId: foodId, + quantity: quantity + }); + } + } + + req.flash('success_msg', 'Meal plan updated!'); + res.redirect('/meal-planner'); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error adding meal plan'); + res.redirect('/meal-planner'); + } +}); + +app.post('/meal-planner/complete/:id', ensureAuthenticated, async (req, res) => { + try { + const plan = await db.MealPlan.findOne({ + where: { id: req.params.id, user_id: req.user.id } + }); + + if (plan) { + plan.is_completed = !plan.is_completed; + await plan.save(); + + // If marked complete, optionally create actual meal log? + // For now just toggle status as per request simplicity. + } + + res.redirect('/meal-planner'); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error updating plan status'); + res.redirect('/meal-planner'); + } +}); + +app.post('/meal-planner/delete/:id', ensureAuthenticated, async (req, res) => { + try { + await db.MealPlan.destroy({ + where: { id: req.params.id, user_id: req.user.id } + }); + req.flash('success_msg', 'Meal plan deleted'); + res.redirect('/meal-planner'); + } catch (err) { + console.error(err); + req.flash('error_msg', 'Error deleting meal plan'); + res.redirect('/meal-planner'); + } +}); + +// Database Sync and Server Start +const startServer = async () => { + try { + await db.sequelize.sync(); + app.listen(PORT, () => console.log(`Server started on port ${PORT}`)); + } catch (err) { + console.error('Database connection error:', err); + } +}; + +startServer(); diff --git a/utils/api_client.js b/utils/api_client.js new file mode 100644 index 0000000..0a68f31 --- /dev/null +++ b/utils/api_client.js @@ -0,0 +1,213 @@ +const axios = require('axios'); +const db = require('../models'); +const { Op } = require('sequelize'); + +class NutritionAPI { + constructor(apiKey) { + this.apiKey = apiKey; + this.baseUrl = "https://api.api-ninjas.com/v1/nutrition"; + this.headers = { 'X-Api-Key': apiKey }; + this.cacheDurationDays = 30; + } + + async searchFood(query) { + // Check cache first + const cached = await this._getFromCache(query); + if (cached) { + return cached; + } + + // Make API request + try { + const response = await axios.get(this.baseUrl, { + headers: this.headers, + params: { query: query }, + timeout: 10000 + }); + + if (response.status === 200) { + const data = response.data; + + // Cache the response + await this._saveToCache(query, 'api_ninjas', data); + + // Parse and return standardized format + return this._parseApiResponse(data); + } else { + console.error(`API Error: ${response.status}`); + return []; + } + } catch (error) { + console.error(`API Request failed: ${error.message}`); + return []; + } + } + + async _getFromCache(query) { + const cacheEntry = await db.APICache.findOne({ + where: { query: query.toLowerCase() } + }); + + if (cacheEntry) { + // Check if cache is still valid (30 days) + const age = (new Date() - new Date(cacheEntry.cached_at)) / (1000 * 60 * 60 * 24); + if (age < this.cacheDurationDays) { + const data = JSON.parse(cacheEntry.response_json); + return this._parseApiResponse(data); + } + } + + return null; + } + + async _saveToCache(query, source, data) { + try { + let cacheEntry = await db.APICache.findOne({ + where: { query: query.toLowerCase() } + }); + + if (cacheEntry) { + // Update existing cache + cacheEntry.response_json = JSON.stringify(data); + cacheEntry.cached_at = new Date(); + await cacheEntry.save(); + } else { + // Create new cache entry + await db.APICache.create({ + query: query.toLowerCase(), + api_source: source, + response_json: JSON.stringify(data), + cached_at: new Date() + }); + } + } catch (error) { + console.error(`Cache save failed: ${error.message}`); + } + } + + _parseApiResponse(data) { + return data.map(item => ({ + name: (item.name || '').replace(/\b\w/g, l => l.toUpperCase()), // Title case + calories: item.calories || 0, + protein_g: item.protein_g || 0, + carbs_g: item.carbohydrates_total_g || 0, + fat_g: item.fat_total_g || 0, + fiber_g: item.fiber_g || 0, + sugar_g: item.sugar_g || 0, + sodium_mg: item.sodium_mg || 0, + serving_size_g: item.serving_size_g || 100, + source: 'api_ninjas' + })); + } + + async saveFoodToDb(foodData) { + try { + // Check if food already exists + const existing = await db.FoodItem.findOne({ + where: { + name: foodData.name, + source: foodData.source || 'api' + } + }); + + if (existing) { + return existing; + } + + // Create new food item + const food = await db.FoodItem.create({ + name: foodData.name, + calories: foodData.calories, + protein_g: foodData.protein_g || 0, + carbs_g: foodData.carbs_g || 0, + fat_g: foodData.fat_g || 0, + fiber_g: foodData.fiber_g || 0, + sugar_g: foodData.sugar_g || 0, + sodium_mg: foodData.sodium_mg || 0, + serving_size_g: foodData.serving_size_g || 100, + serving_description: foodData.serving_description || '1 serving', + source: foodData.source || 'api', + api_data: JSON.stringify(foodData) + }); + + return food; + } catch (error) { + console.error(`Error saving food to DB: ${error.message}`); + return null; + } + } +} + +async function searchAllSources(query, apiClient) { + const results = []; + + // 1. Search Filipino foods first + const filipinoFoods = await db.FoodItem.findAll({ + where: { + is_filipino: true, + [Op.or]: [ + { name: { [Op.like]: `%${query}%` } }, + { name_tagalog: { [Op.like]: `%${query}%` } } + ] + }, + limit: 5 + }); + + for (const food of filipinoFoods) { + results.push({ + id: food.id, + name: food.name, + name_tagalog: food.name_tagalog, + calories: food.calories, + protein_g: food.protein_g, + carbs_g: food.carbs_g, + fat_g: food.fat_g, + serving_description: food.serving_description, + source: 'filipino', + category: food.category + }); + } + + // 2. Search other local foods + const otherFoods = await db.FoodItem.findAll({ + where: { + is_filipino: false, + name: { [Op.like]: `%${query}%` } + }, + limit: 5 + }); + + for (const food of otherFoods) { + results.push({ + id: food.id, + name: food.name, + calories: food.calories, + protein_g: food.protein_g, + carbs_g: food.carbs_g, + fat_g: food.fat_g, + serving_description: food.serving_description, + source: food.source + }); + } + + // 3. If not enough results, search API + if (results.length < 3 && apiClient && apiClient.apiKey) { + const apiResults = await apiClient.searchFood(query); + for (const foodData of apiResults.slice(0, 5)) { + results.push({ + name: foodData.name, + calories: foodData.calories, + protein_g: foodData.protein_g, + carbs_g: foodData.carbs_g, + fat_g: foodData.fat_g, + serving_size_g: foodData.serving_size_g, + source: 'api_ninjas', + api_data: foodData + }); + } + } + + return results; +} + +module.exports = { NutritionAPI, searchAllSources }; diff --git a/utils/index.js b/utils/index.js new file mode 100644 index 0000000..c6551d5 --- /dev/null +++ b/utils/index.js @@ -0,0 +1,340 @@ +const db = require('../models'); +const { Op } = require('sequelize'); + +function calculateBMR(weightKg, heightCm, age, gender) { + // Calculate Basal Metabolic Rate using Mifflin-St Jeor Equation + let bmr; + if (gender.toLowerCase() === 'male') { + bmr = (10 * weightKg) + (6.25 * heightCm) - (5 * age) + 5; + } else { + // female + bmr = (10 * weightKg) + (6.25 * heightCm) - (5 * age) - 161; + } + return Math.round(bmr); +} + +function calculateTDEE(bmr, activityLevel) { + // Calculate Total Daily Energy Expenditure + const multipliers = { + 'sedentary': 1.2, + 'light': 1.375, + 'moderate': 1.55, + 'active': 1.725, + 'very_active': 1.9 + }; + + const multiplier = multipliers[activityLevel] || 1.55; + return Math.round(bmr * multiplier); +} + +function calculateMacroTargets(weightKg, goalType = 'recomp') { + // Calculate macro targets based on body weight and goal + let protein, carbs, fat; + + if (goalType === 'muscle_gain') { + protein = weightKg * 2.4; // High protein for muscle building + carbs = weightKg * 3.5; // Higher carbs for energy + fat = weightKg * 1.0; // Moderate fat + } else if (goalType === 'weight_loss') { + protein = weightKg * 2.2; // High protein to preserve muscle + carbs = weightKg * 2.0; // Lower carbs for deficit + fat = weightKg * 0.8; // Lower fat + } else { + // recomp (body recomposition) + protein = weightKg * 2.2; // High protein + carbs = weightKg * 2.5; // Moderate carbs + fat = weightKg * 0.9; // Moderate fat + } + + return { + protein_g: Math.round(protein), + carbs_g: Math.round(carbs), + fat_g: Math.round(fat) + }; +} + +async function calculateDailyTotals(userId, targetDate = null) { + // Calculate total nutrition consumed for a given date + if (!targetDate) { + targetDate = new Date(); + } + + // Ensure date is in YYYY-MM-DD format if it's a string, or create date object + const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate; + + // Get all meals for the date + const meals = await db.Meal.findAll({ + where: { + user_id: userId, + date: dateStr + }, + include: [{ + model: db.MealFood, + include: [db.FoodItem] + }] + }); + + const totals = { + calories: 0, + protein: 0, + carbs: 0, + fat: 0, + meals: [] + }; + + for (const meal of meals) { + const mealTotals = { + calories: 0, + protein: 0, + carbs: 0, + fat: 0 + }; + + const mealFoods = []; + + for (const mf of meal.MealFoods) { + // Calculate nutrition for this food item based on quantity + // If calculated values exist in MealFood, use them, otherwise calculate + const quantity = mf.quantity || 1.0; + const food = mf.FoodItem; + + const calories = mf.calories_consumed || (food.calories * quantity); + const protein = mf.protein_consumed || (food.protein_g * quantity); + const carbs = mf.carbs_consumed || (food.carbs_g * quantity); + const fat = mf.fat_consumed || (food.fat_g * quantity); + + mealTotals.calories += calories; + mealTotals.protein += protein; + mealTotals.carbs += carbs; + mealTotals.fat += fat; + + mealFoods.push({ + name: food.name, + quantity: quantity, + calories: calories + }); + } + + totals.calories += mealTotals.calories; + totals.protein += mealTotals.protein; + totals.carbs += mealTotals.carbs; + totals.fat += mealTotals.fat; + + totals.meals.push({ + id: meal.id, + type: meal.meal_type, + time: meal.time ? meal.time.substring(0, 5) : null, + totals: mealTotals, + foods: mealFoods + }); + } + + return totals; +} + +async function calculateWaterTotal(userId, targetDate = null) { + // Calculate total water intake for a given date + if (!targetDate) { + targetDate = new Date(); + } + + const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate; + + const waterLogs = await db.WaterLog.findAll({ + where: { + user_id: userId, + date: dateStr + } + }); + + const total = waterLogs.reduce((sum, log) => sum + log.amount_ml, 0); + + return { + total_ml: total, + logs: waterLogs.map(log => ({ + id: log.id, + amount_ml: log.amount_ml, + time: log.time ? log.time.substring(0, 5) : null + })) + }; +} + +async function getWeightTrend(userId, days = 7) { + // Get weight trend for the past N days + const endDate = new Date(); + const startDate = new Date(); + startDate.setDate(endDate.getDate() - (days - 1)); + + const startDateStr = startDate.toISOString().split('T')[0]; + const endDateStr = endDate.toISOString().split('T')[0]; + + const weightLogs = await db.WeightLog.findAll({ + where: { + user_id: userId, + date: { + [Op.between]: [startDateStr, endDateStr] + } + }, + order: [['date', 'ASC']] + }); + + return weightLogs.map(log => ({ + date: log.date, + weight_kg: log.weight_kg + })); +} + +async function getCalorieTrend(userId, days = 7) { + // Get calorie intake trend for the past N days + const endDate = new Date(); + const startDate = new Date(); + startDate.setDate(endDate.getDate() - (days - 1)); + + const trend = []; + let currentDate = new Date(startDate); + + while (currentDate <= endDate) { + const dateStr = currentDate.toISOString().split('T')[0]; + const totals = await calculateDailyTotals(userId, dateStr); + + trend.push({ + date: dateStr, + calories: Math.round(totals.calories), + protein: Math.round(totals.protein), + carbs: Math.round(totals.carbs), + fat: Math.round(totals.fat) + }); + + currentDate.setDate(currentDate.getDate() + 1); + } + + return trend; +} + +async function updateDailySummary(userId, targetDate = null) { + // Update or create daily summary for a user + if (!targetDate) { + targetDate = new Date(); + } + + const dateStr = targetDate instanceof Date ? targetDate.toISOString().split('T')[0] : targetDate; + + // Calculate totals + const nutrition = await calculateDailyTotals(userId, dateStr); + const water = await calculateWaterTotal(userId, dateStr); + + // Get weight for the day + const weightLog = await db.WeightLog.findOne({ + where: { + user_id: userId, + date: dateStr + } + }); + const weight = weightLog ? weightLog.weight_kg : null; + + // Get user's calorie target + const user = await db.User.findByPk(userId); + const targetCalories = user ? user.target_daily_calories : 2000; + + // Find or create summary + let summary = await db.DailySummary.findOne({ + where: { + user_id: userId, + date: dateStr + } + }); + + if (!summary) { + summary = await db.DailySummary.create({ + user_id: userId, + date: dateStr + }); + } + + // Update values + summary.total_calories = nutrition.calories; + summary.total_protein_g = nutrition.protein; + summary.total_carbs_g = nutrition.carbs; + summary.total_fat_g = nutrition.fat; + summary.total_water_ml = water.total_ml; + summary.calories_remaining = targetCalories - nutrition.calories; + summary.weight_kg = weight; + + try { + await summary.save(); + return summary; + } catch (e) { + console.error(`Error updating daily summary: ${e}`); + return null; + } +} + +function getMacroPercentages(proteinG, carbsG, fatG) { + // Calculate macro distribution as percentages + const proteinCal = proteinG * 4; + const carbsCal = carbsG * 4; + const fatCal = fatG * 9; + const totalCal = proteinCal + carbsCal + fatCal; + + if (totalCal === 0) { + return { protein: 0, carbs: 0, fat: 0 }; + } + + return { + protein: Math.round((proteinCal / totalCal) * 100), + carbs: Math.round((carbsCal / totalCal) * 100), + fat: Math.round((fatCal / totalCal) * 100) + }; +} + +function suggestFoodsForMacros(remainingProtein, remainingCarbs, remainingFat) { + // Suggest Filipino foods based on remaining macros + const suggestions = []; + + // High protein needed + if (remainingProtein > 30) { + suggestions.push({ + category: 'High Protein Ulam', + examples: ['Grilled Tilapia', 'Chicken Tinola', 'Grilled Chicken'] + }); + } + + // High carbs needed + if (remainingCarbs > 40) { + suggestions.push({ + category: 'Carbs', + examples: ['White Rice', 'Pandesal', 'Sweet Potato'] + }); + } + + // High fat needed + if (remainingFat > 20) { + suggestions.push({ + category: 'Healthy Fats', + examples: ['Sisig', 'Lechon Kawali', 'Bicol Express'] + }); + } + + // Balanced meal needed + if (remainingProtein > 20 && remainingCarbs > 30) { + suggestions.push({ + category: 'Balanced Meals', + examples: ['Tapsilog', 'Chicken Adobo with Rice', 'Sinigang'] + }); + } + + return suggestions; +} + +module.exports = { + calculateBMR, + calculateTDEE, + calculateMacroTargets, + calculateDailyTotals, + calculateWaterTotal, + getWeightTrend, + getCalorieTrend, + updateDailySummary, + getMacroPercentages, + suggestFoodsForMacros +}; diff --git a/views/add_meal.ejs b/views/add_meal.ejs new file mode 100644 index 0000000..e3fa210 --- /dev/null +++ b/views/add_meal.ejs @@ -0,0 +1,237 @@ +
+

Add Meal

+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + +
+ + +
+

Selected Foods

+
+

No foods added yet. Search and add foods above.

+
+
+ + +
+

Meal Summary

+
+
+

Calories

+

0

+
+
+

Protein

+

0g

+
+
+

Carbs

+

0g

+
+
+

Fat

+

0g

+
+
+
+ + +
+ + Cancel + + +
+
+
+ + diff --git a/views/dashboard.ejs b/views/dashboard.ejs new file mode 100644 index 0000000..282c642 --- /dev/null +++ b/views/dashboard.ejs @@ -0,0 +1,294 @@ +
+ +
+
+

Today's Summary

+

<%= new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) %>

+
+ +
+ + +
+ +
+
+
+

Calories

+

<%= Math.round(nutrition.calories) %>

+

/ <%= current_user.target_daily_calories %>

+
+ 🔥 +
+ +
+ <% let cal_percent = current_user.target_daily_calories > 0 ? Math.round(nutrition.calories / current_user.target_daily_calories * 100) : 0; %> +
+
+

+ <%= Math.round(remaining.calories) %> remaining +

+
+ + +
+
+
+

Protein

+

<%= Math.round(nutrition.protein) %>g

+

/ <%= goals.target_protein_g %>g

+
+ 💪 +
+
+ <% let prot_percent = goals.target_protein_g > 0 ? Math.round(nutrition.protein / goals.target_protein_g * 100) : 0; %> +
+
+

+ <%= Math.round(remaining.protein) %>g remaining +

+
+ + +
+
+
+ Carbs + <%= Math.round(nutrition.carbs) %>g / <%= goals.target_carbs_g %>g +
+
+ <% let carb_percent = goals.target_carbs_g > 0 ? Math.round(nutrition.carbs / goals.target_carbs_g * 100) : 0; %> +
+
+
+
+
+ Fat + <%= Math.round(nutrition.fat) %>g / <%= goals.target_fat_g %>g +
+
+ <% let fat_percent = goals.target_fat_g > 0 ? Math.round(nutrition.fat / goals.target_fat_g * 100) : 0; %> +
+
+
+
+ + +
+
+
+ Water + <%= water.total_ml %>ml / <%= goals.target_water_ml %>ml +
+
+ <% let glasses_filled = Math.floor(water.total_ml / 250); %> + <% for (let i = 0; i < 8; i++) { %> + 💧 + <% } %> +
+ +
+ + +
+
+
+
+ Weight + <% if (typeof weight_today !== 'undefined' && weight_today) { %> +
+ <%= weight_today.weight_kg %>kg + <% if (typeof weight_change !== 'undefined' && weight_change !== null) { %> + + <%= weight_change.toFixed(1) %>kg + + <% } %> +
+ <% } else { %> +
+ + +
+ <% } %> +
+
+
+
+ + +
+
+

Macro Distribution

+ +
+ + +
+

💡 Smart Suggestions

+ <% if (suggestions && suggestions.length > 0) { %> +
+ <% suggestions.forEach(function(suggestion) { %> +
+

<%= suggestion.category %>

+

Try: <%= suggestion.examples.join(', ') %>

+
+ <% }); %> +
+ <% } else { %> +

You're on track! Keep up the good work! 🎉

+ <% } %> +
+
+ + +
+
+

Today's Meals

+ + Add Meal +
+ + <% if (nutrition.meals && nutrition.meals.length > 0) { %> +
+ <% nutrition.meals.forEach(function(meal) { %> +
+
+
+
+ + <% if (meal.type == 'breakfast') { %>🌅<% } else if (meal.type == 'lunch') { %>🌞<% } else if (meal.type == 'dinner') { %>🌙<% } else { %>🍪<% } %> + +

<%= meal.type %>

+ <% if (meal.time) { %> + <%= meal.time %> + <% } %> +
+
+ <% meal.foods.forEach(function(food) { %> +

• <%= food.name %> (<%= food.quantity %>x) - <%= Math.round(food.calories) %> cal

+ <% }); %> +
+
+
+

<%= Math.round(meal.totals.calories) %>

+

calories

+

+ P: <%= Math.round(meal.totals.protein) %>g | + C: <%= Math.round(meal.totals.carbs) %>g | + F: <%= Math.round(meal.totals.fat) %>g +

+
+
+
+ <% }); %> +
+ <% } else { %> +
+

🍽️

+

No meals logged yet today.

+ Add your first meal +
+ <% } %> +
+ + +
+ +
+

📈 Calorie Trend (7 Days)

+ +
+ + +
+

⚖️ Weight Trend (7 Days)

+ +
+
+
+ + diff --git a/views/foods.ejs b/views/foods.ejs new file mode 100644 index 0000000..6e1f9e2 --- /dev/null +++ b/views/foods.ejs @@ -0,0 +1,104 @@ +

Food Database

+ + +
+
+
+ +
+
+ +
+
+
+ +
+

+ 🇵🇭 Filipino Foods +

+ + <% if (filipino_foods.length === 0) { %> +

No Filipino foods found matching your criteria.

+ <% } else { %> +
+ <% filipino_foods.forEach(food => { %> +
+
+
+

<%= food.name %>

+ <% if (food.name_tagalog) { %> +

<%= food.name_tagalog %>

+ <% } %> +
+ <%= food.category %> +
+ +
+
+ <%= Math.round(food.calories) %> + cal +
+
+ <%= Math.round(food.protein_g) %>g + prot +
+
+ <%= Math.round(food.carbs_g) %>g + carb +
+
+ <%= Math.round(food.fat_g) %>g + fat +
+
+ +
+ Per <%= food.serving_description || 'serving' %> (<%= food.serving_size_g %>g) +
+
+ <% }); %> +
+ <% } %> +
+ +
+

Other Foods

+ + <% if (other_foods.length === 0) { %> +

No other foods found.

+ <% } else { %> +
+ <% other_foods.forEach(food => { %> +
+
+

<%= food.name %>

+ Global +
+ +
+
+ <%= Math.round(food.calories) %> + cal +
+
+ <%= Math.round(food.protein_g) %>g + prot +
+
+ <%= Math.round(food.carbs_g) %>g + carb +
+
+ <%= Math.round(food.fat_g) %>g + fat +
+
+
+ <% }); %> +
+ <% } %> +
diff --git a/views/goals.ejs b/views/goals.ejs new file mode 100644 index 0000000..4cc9020 --- /dev/null +++ b/views/goals.ejs @@ -0,0 +1,117 @@ +

Goals & Settings

+ +
+ +
+

Personal Information

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ +

Goals

+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+

Your Stats

+
+
+

BMR

+

<%= bmr || '-' %>

+

Calories burned at rest

+
+
+

TDEE

+

<%= tdee || '-' %>

+

Total daily energy expenditure

+
+
+
+ + <% if (goals) { %> +
+

Daily Targets

+
+
+ Calories + <%= current_user.target_daily_calories %> +
+
+ Protein + <%= goals.target_protein_g %>g +
+
+ Carbs + <%= goals.target_carbs_g %>g +
+
+ Fat + <%= goals.target_fat_g %>g +
+
+ Water + <%= goals.target_water_ml %>ml +
+
+
+ <% } %> +
+
diff --git a/views/layout.ejs b/views/layout.ejs new file mode 100644 index 0000000..e228ada --- /dev/null +++ b/views/layout.ejs @@ -0,0 +1,104 @@ + + + + + + Calorie Tracker + + + + + + + <% if (current_user) { %> + + + <% } %> + + + <% if (success_msg.length > 0) { %> +
+ +
+ <% } %> + + <% if (error_msg.length > 0) { %> +
+ +
+ <% } %> + + <% if (error.length > 0) { %> +
+ +
+ <% } %> + + +
+ <%- body %> +
+ + +
+

© 2026 Calorie Tracker - Filipino Food Edition

+
+ + <%- defineContent('scripts') %> + + diff --git a/views/login.ejs b/views/login.ejs new file mode 100644 index 0000000..1689ea7 --- /dev/null +++ b/views/login.ejs @@ -0,0 +1,26 @@ +
+
+

🍽️ Calorie Tracker

+

Filipino Food Edition

+ +
+
+ + +
+ +
+ + +
+ + +
+ +

+ Don't have an account? Register +

+
+
diff --git a/views/meal_planner.ejs b/views/meal_planner.ejs new file mode 100644 index 0000000..1242961 --- /dev/null +++ b/views/meal_planner.ejs @@ -0,0 +1,253 @@ +

Weekly Meal Planner

+ +
+ <% dates.forEach(dateStr => { %> + <% const dateObj = new Date(dateStr); %> + <% const isToday = dateStr === today; %> + +
+ +
+

<%= dateObj.toLocaleDateString('en-US', { weekday: 'short' }) %>

+

<%= dateObj.getDate() %>

+
+ + +
+ <% if (meal_plans[dateStr] && meal_plans[dateStr].length > 0) { %> + <% meal_plans[dateStr].forEach(plan => { %> +
+
+ <%= plan.meal_type %> +
+ +
+ +
+ + +
+ +
+
+
+ +
    + <% plan.foods.forEach(food => { %> +
  • • <%= food.name %>
  • + <% }); %> +
+ +
+ <%= Math.round(plan.totals.calories) %> cal + P:<%= Math.round(plan.totals.protein) %> +
+
+ <% }); %> + <% } else { %> +
+ No meals planned +
+ <% } %> + + + +
+
+ <% }); %> +
+ + + + + \ No newline at end of file diff --git a/views/progress.ejs b/views/progress.ejs new file mode 100644 index 0000000..7d3bdff --- /dev/null +++ b/views/progress.ejs @@ -0,0 +1,117 @@ +

Progress & Trends

+ + +
+
+ + + +
+
+ + +
+
+

Avg Calories

+

<%= avg_calories %>

+
+
+

Avg Protein

+

<%= avg_protein %>g

+
+
+

Avg Carbs

+

<%= avg_carbs %>g

+
+
+

Avg Fat

+

<%= avg_fat %>g

+
+
+ +
+ +
+
+

Weight Trend

+ <% if (weight_change !== null) { %> + + <%= weight_change > 0 ? '+' : '' %><%= weight_change.toFixed(1) %> kg + + <% } %> +
+
+ +
+
+ + +
+

Calorie Intake

+
+ +
+
+
+ + diff --git a/views/register.ejs b/views/register.ejs new file mode 100644 index 0000000..9e61497 --- /dev/null +++ b/views/register.ejs @@ -0,0 +1,30 @@ +
+
+

Create Account

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +

+ Already have an account? Login +

+
+