typescript! yay! (pain.)

This commit is contained in:
Kaushik Narayan R 2024-12-31 03:47:10 -07:00
parent a837266dca
commit 6733a3be8e
39 changed files with 1188 additions and 335 deletions

7
.gitignore vendored
View File

@ -8,8 +8,12 @@
# testing # testing
/coverage /coverage
# TypeScript cache
*.tsbuildinfo
# production # production
/build /build
/tsout
# misc # misc
.DS_Store .DS_Store
@ -18,6 +22,9 @@
.env.test.local .env.test.local
.env.production.local .env.production.local
# Logs
logs
*.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*

675
package-lock.json generated
View File

@ -25,7 +25,8 @@
"devDependencies": { "devDependencies": {
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"typescript": "^5.7.2" "typescript": "^5.7.2",
"typescript-plugin-css-modules": "^5.1.0"
} }
}, },
"node_modules/@adobe/css-tools": { "node_modules/@adobe/css-tools": {
@ -3054,6 +3055,302 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@parcel/watcher": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz",
"integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.0",
"@parcel/watcher-darwin-arm64": "2.5.0",
"@parcel/watcher-darwin-x64": "2.5.0",
"@parcel/watcher-freebsd-x64": "2.5.0",
"@parcel/watcher-linux-arm-glibc": "2.5.0",
"@parcel/watcher-linux-arm-musl": "2.5.0",
"@parcel/watcher-linux-arm64-glibc": "2.5.0",
"@parcel/watcher-linux-arm64-musl": "2.5.0",
"@parcel/watcher-linux-x64-glibc": "2.5.0",
"@parcel/watcher-linux-x64-musl": "2.5.0",
"@parcel/watcher-win32-arm64": "2.5.0",
"@parcel/watcher-win32-ia32": "2.5.0",
"@parcel/watcher-win32-x64": "2.5.0"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz",
"integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz",
"integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz",
"integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz",
"integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz",
"integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz",
"integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz",
"integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz",
"integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz",
"integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz",
"integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz",
"integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz",
"integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz",
"integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@pkgjs/parseargs": { "node_modules/@pkgjs/parseargs": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -3862,6 +4159,24 @@
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
"dev": true "dev": true
}, },
"node_modules/@types/postcss-modules-local-by-default": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.2.tgz",
"integrity": "sha512-CtYCcD+L+trB3reJPny+bKWKMzPfxEyQpKIwit7kErnOexf5/faaGpkFy4I5AwbV4hp1sk7/aTg0tt0B67VkLQ==",
"dev": true,
"dependencies": {
"postcss": "^8.0.0"
}
},
"node_modules/@types/postcss-modules-scope": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.4.tgz",
"integrity": "sha512-//ygSisVq9kVI0sqx3UPLzWIMCmtSVrzdljtuaAEJtGoGnpjBikZ2sXO5MpH9SnWX9HRfXxHifDAXcQjupWnIQ==",
"dev": true,
"dependencies": {
"postcss": "^8.0.0"
}
},
"node_modules/@types/prettier": { "node_modules/@types/prettier": {
"version": "2.7.3", "version": "2.7.3",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
@ -5990,6 +6305,18 @@
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"dev": true "dev": true
}, },
"node_modules/copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"dev": true,
"dependencies": {
"is-what": "^3.14.1"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/core-js": { "node_modules/core-js": {
"version": "3.39.0", "version": "3.39.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz",
@ -6768,6 +7095,19 @@
"npm": "1.2.8000 || >= 1.4.16" "npm": "1.2.8000 || >= 1.4.16"
} }
}, },
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/detect-newline": { "node_modules/detect-newline": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@ -7100,6 +7440,19 @@
"url": "https://github.com/fb55/entities?sponsor=1" "url": "https://github.com/fb55/entities?sponsor=1"
} }
}, },
"node_modules/errno": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"dev": true,
"optional": true,
"dependencies": {
"prr": "~1.0.1"
},
"bin": {
"errno": "cli.js"
}
},
"node_modules/error-ex": { "node_modules/error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@ -9336,6 +9689,19 @@
"node": ">= 4" "node": ">= 4"
} }
}, },
"node_modules/image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
"dev": true,
"optional": true,
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/immer": { "node_modules/immer": {
"version": "9.0.21", "version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
@ -9346,6 +9712,12 @@
"url": "https://opencollective.com/immer" "url": "https://opencollective.com/immer"
} }
}, },
"node_modules/immutable": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz",
"integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==",
"dev": true
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -9929,6 +10301,12 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-what": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"dev": true
},
"node_modules/is-wsl": { "node_modules/is-wsl": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@ -11264,6 +11642,76 @@
"shell-quote": "^1.8.1" "shell-quote": "^1.8.1"
} }
}, },
"node_modules/less": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/less/-/less-4.2.1.tgz",
"integrity": "sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==",
"dev": true,
"dependencies": {
"copy-anything": "^2.0.1",
"parse-node-version": "^1.0.1",
"tslib": "^2.3.0"
},
"bin": {
"lessc": "bin/lessc"
},
"engines": {
"node": ">=6"
},
"optionalDependencies": {
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"source-map": "~0.6.0"
}
},
"node_modules/less/node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"optional": true,
"dependencies": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/less/node_modules/pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true,
"optional": true,
"engines": {
"node": ">=6"
}
},
"node_modules/less/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"optional": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/less/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/leven": { "node_modules/leven": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@ -11341,6 +11789,12 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}, },
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
"dev": true
},
"node_modules/lodash.debounce": { "node_modules/lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -11710,6 +12164,23 @@
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"dev": true "dev": true
}, },
"node_modules/needle": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz",
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
"dev": true,
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.3",
"sax": "^1.2.4"
},
"bin": {
"needle": "bin/needle"
},
"engines": {
"node": ">= 4.4.x"
}
},
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "0.6.4", "version": "0.6.4",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
@ -11735,6 +12206,13 @@
"tslib": "^2.0.3" "tslib": "^2.0.3"
} }
}, },
"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==",
"dev": true,
"optional": true
},
"node_modules/node-forge": { "node_modules/node-forge": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
@ -12157,6 +12635,15 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
"dev": true,
"engines": {
"node": ">= 0.10"
}
},
"node_modules/parse5": { "node_modules/parse5": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
@ -13808,6 +14295,13 @@
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
}, },
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"dev": true,
"optional": true
},
"node_modules/psl": { "node_modules/psl": {
"version": "1.15.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
@ -14455,6 +14949,12 @@
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"dev": true "dev": true
}, },
"node_modules/reserved-words": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz",
"integrity": "sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==",
"dev": true
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.10", "version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@ -14767,6 +15267,26 @@
"integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==",
"dev": true "dev": true
}, },
"node_modules/sass": {
"version": "1.83.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.83.0.tgz",
"integrity": "sha512-qsSxlayzoOjdvXMVLkzF84DJFc2HZEL/rFyGIKbbilYtAvlCxyuzUeff9LawTn4btVnLKg75Z8MMr1lxU1lfGw==",
"dev": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sass-loader": { "node_modules/sass-loader": {
"version": "12.6.0", "version": "12.6.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
@ -14805,6 +15325,34 @@
} }
} }
}, },
"node_modules/sass/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
"dev": true,
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sax": { "node_modules/sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -15790,6 +16338,40 @@
"postcss": "^8.2.15" "postcss": "^8.2.15"
} }
}, },
"node_modules/stylus": {
"version": "0.62.0",
"resolved": "https://registry.npmjs.org/stylus/-/stylus-0.62.0.tgz",
"integrity": "sha512-v3YCf31atbwJQIMtPNX8hcQ+okD4NQaTuKGUWfII8eaqn+3otrbttGL1zSMZAAtiPsBztQnujVBugg/cXFUpyg==",
"dev": true,
"dependencies": {
"@adobe/css-tools": "~4.3.1",
"debug": "^4.3.2",
"glob": "^7.1.6",
"sax": "~1.3.0",
"source-map": "^0.7.3"
},
"bin": {
"stylus": "bin/stylus"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://opencollective.com/stylus"
}
},
"node_modules/stylus/node_modules/@adobe/css-tools": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz",
"integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==",
"dev": true
},
"node_modules/stylus/node_modules/sax": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==",
"dev": true
},
"node_modules/sucrase": { "node_modules/sucrase": {
"version": "3.35.0", "version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
@ -16577,6 +17159,97 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/typescript-plugin-css-modules": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.1.0.tgz",
"integrity": "sha512-6h+sLBa4l+XYSTn/31vZHd/1c3SvAbLpobY6FxDiUOHJQG1eD9Gh3eCs12+Eqc+TCOAdxcO+zAPvUq0jBfdciw==",
"dev": true,
"dependencies": {
"@types/postcss-modules-local-by-default": "^4.0.2",
"@types/postcss-modules-scope": "^3.0.4",
"dotenv": "^16.4.2",
"icss-utils": "^5.1.0",
"less": "^4.2.0",
"lodash.camelcase": "^4.3.0",
"postcss": "^8.4.35",
"postcss-load-config": "^3.1.4",
"postcss-modules-extract-imports": "^3.0.0",
"postcss-modules-local-by-default": "^4.0.4",
"postcss-modules-scope": "^3.1.1",
"reserved-words": "^0.1.2",
"sass": "^1.70.0",
"source-map-js": "^1.0.2",
"stylus": "^0.62.0",
"tsconfig-paths": "^4.2.0"
},
"peerDependencies": {
"typescript": ">=4.0.0"
}
},
"node_modules/typescript-plugin-css-modules/node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/typescript-plugin-css-modules/node_modules/postcss-load-config": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
"dev": true,
"dependencies": {
"lilconfig": "^2.0.5",
"yaml": "^1.10.2"
},
"engines": {
"node": ">= 10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
"peerDependencies": {
"postcss": ">=8.0.9",
"ts-node": ">=9.0.0"
},
"peerDependenciesMeta": {
"postcss": {
"optional": true
},
"ts-node": {
"optional": true
}
}
},
"node_modules/typescript-plugin-css-modules/node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/typescript-plugin-css-modules/node_modules/tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dev": true,
"dependencies": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",

View File

@ -20,7 +20,8 @@
"devDependencies": { "devDependencies": {
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"typescript": "^5.7.2" "typescript": "^5.7.2",
"typescript-plugin-css-modules": "^5.1.0"
}, },
"overrides": { "overrides": {
"react-scripts": { "react-scripts": {

View File

@ -1,29 +1,33 @@
// Libraries // Libraries
import { createContext, useEffect, useState } from 'react'; import { createContext, useEffect, useState } from "react";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import { ToastContainer } from "react-toastify"; import { ToastContainer } from "react-toastify";
// Styles // Styles
import styles from './App.module.css'; import styles from "./App.module.css";
// Assets // Assets
// Utils // Utils
import ScrollToTop from './utils/ScrollToTop'; import ScrollToTop from "./utils/ScrollToTop";
// Components // Components
import Navbar from './components/Navbar'; import Navbar from "./components/Navbar/index";
// Routes // Routes
import AllRoutes from './routes/AllRoutes'; import AllRoutes from "./routes/AllRoutes";
import { showErrorToastNotification, showInfoToastNotification, showWarnToastNotification } from './components/ToastNotification'; import {
import { apiAuthCheck, apiAuthRefresh } from './api/auth'; showErrorToastNotification,
import { ReactFlowProvider } from '@xyflow/react'; showInfoToastNotification,
showWarnToastNotification,
} from "./components/ToastNotification";
import { apiAuthCheck, apiAuthRefresh } from "./api/auth";
import { ReactFlowProvider } from "@xyflow/react";
// Contexts // Contexts
export const WidthContext = createContext(); export const WidthContext = createContext(0);
export const AuthContext = createContext(); export const AuthContext = createContext(false);
export const RefreshAuthContext = createContext(); export const RefreshAuthContext = createContext<any>(null);
function App() { function App() {
// States // States
@ -78,7 +82,7 @@ function App() {
setAuth(false); setAuth(false);
showWarnToastNotification(resp.data.message); showWarnToastNotification(resp.data.message);
return false; return false;
} };
useEffect(() => { useEffect(() => {
(async () => { (async () => {
@ -117,7 +121,6 @@ function App() {
</div> </div>
</BrowserRouter> </BrowserRouter>
<ToastContainer <ToastContainer
id={"notif-container"}
position={"bottom-center"} position={"bottom-center"}
theme={"dark"} theme={"dark"}
stacked stacked

View File

@ -1,20 +0,0 @@
import { axiosInstance } from "./axiosInstance";
import { authHealthCheckURL, authRefreshURL } from "./paths";
export const apiAuthCheck = async () => {
try {
const response = await axiosInstance.get(authHealthCheckURL);
return response;
} catch (error) {
return error.response;
}
}
export const apiAuthRefresh = async () => {
try {
const response = await axiosInstance.get(authRefreshURL);
return response;
} catch (error) {
return error.response;
}
}

25
src/api/auth.ts Normal file
View File

@ -0,0 +1,25 @@
import { AxiosResponse } from "axios";
import { apiRespBase, axiosInstance } from "./axiosInstance";
import { authHealthCheckURL, authRefreshURL } from "./paths";
export const apiAuthCheck = async (): Promise<
AxiosResponse<apiRespBase, any>
> => {
try {
const response = await axiosInstance.get(authHealthCheckURL);
return response;
} catch (error: any) {
return error.response;
}
};
export const apiAuthRefresh = async (): Promise<
AxiosResponse<apiRespBase, any>
> => {
try {
const response = await axiosInstance.get(authRefreshURL);
return response;
} catch (error: any) {
return error.response;
}
};

View File

@ -9,3 +9,8 @@ export const axiosInstance = axios.create({
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
}); });
export interface apiRespBase {
message?: string,
errors?: any[],
};

View File

@ -1,11 +0,0 @@
import { axiosInstance } from "./axiosInstance";
import { opFetchGraphURL } from "./paths";
export const apiFetchGraph = async () => {
try {
const response = await axiosInstance.get(opFetchGraphURL);
return response;
} catch (error) {
return error.response;
}
}

25
src/api/operations.ts Normal file
View File

@ -0,0 +1,25 @@
import { AxiosResponse } from "axios";
import { apiRespBase, axiosInstance } from "./axiosInstance";
import { opFetchGraphURL } from "./paths";
interface fetchGraphDataType extends apiRespBase {
playlists?: {
playlistID: string;
playlistName: string;
}[];
links?: {
from: string; // playlistID
to: string; // playlistID
}[];
}
export const apiFetchGraph = async (): Promise<
AxiosResponse<fetchGraphDataType, any>
> => {
try {
const response = await axiosInstance.get(opFetchGraphURL);
return response;
} catch (error: any) {
return error.response;
}
};

View File

@ -1,6 +1,6 @@
/* the value 54150 is decided by getting the length of the path (54122 for the logo asset) */ /* the value 54150 is decided by getting the length of the path (54122 for the logo asset) */
.svgWrapper path { .svg_wrapper path {
stroke-dasharray: 54150; stroke-dasharray: 54150;
stroke-dashoffset: 54150; stroke-dashoffset: 54150;
animation: draw 5s ease-in-out infinite; animation: draw 5s ease-in-out infinite;

View File

@ -1,13 +1,15 @@
import React from 'react'; import React from "react";
import styles from "./AnimatedSVG.module.css"; import styles from "./AnimatedSVG.module.css";
const AnimatedSVG = ({ stroke = "#ffffff" }) => { const AnimatedSVG = () => {
const stroke = "#fff";
return ( return (
<div className={styles.svgWrapper}> <div className={styles.svg_wrapper}>
{/* width, height and viewBox are necessary */} {/* width, height and viewBox are necessary */}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="256" height="256" // adjust size here width="256"
height="256" // adjust size here
viewBox="0 0 512 512" viewBox="0 0 512 512"
preserveAspectRatio="xMidYMid meet" preserveAspectRatio="xMidYMid meet"
> >
@ -35,12 +37,12 @@ const AnimatedSVG = ({ stroke = "#ffffff" }) => {
strokeMiterlimit={10} strokeMiterlimit={10}
fill="none" fill="none"
id="svglength" id="svglength"
// document.getElementById('svglength').getTotalLength() // document.getElementById('svglength').getTotalLength()
/> />
</g> </g>
</svg> </svg>
</div> </div>
) );
} };
export default AnimatedSVG; export default AnimatedSVG;

View File

@ -1,18 +0,0 @@
import React from 'react';
import styles from "./Button.module.css";
function Button({ children, onClickMethod }) {
const clickHandler = (e) => {
e.preventDefault();
onClickMethod();
}
return (
<button type="button"
className={styles.btn_wrapper}
onClick={clickHandler}>
{children}
</button>
)
}
export default Button;

View File

@ -0,0 +1,21 @@
import React from "react";
import styles from "./Button.module.css";
type ButtonProps = {
children: React.ReactNode;
onClickMethod: () => void;
}
const Button = ({ children, onClickMethod }: ButtonProps) => {
const clickHandler = (e: React.MouseEvent) => {
e.preventDefault();
onClickMethod();
};
return (
<button type="button" className={styles.btn_wrapper} onClick={clickHandler}>
{children}
</button>
);
};
export default Button;

View File

@ -1,24 +0,0 @@
import React, { useContext } from 'react'
import styles from "./Navbar.module.css";
import { AuthContext } from "../../App";
import StyledNavLink from '../StyledNavLink';
const Navbar = () => {
const auth = useContext(AuthContext);
return (
<nav className={styles.navbar_wrapper}>
<StyledNavLink exact path="/" text="About" />
<StyledNavLink exact path="/graph" text="Graph" />
{
auth === true ?
<StyledNavLink exact path="/logout" text="Logout" /> :
<StyledNavLink exact path="/login" text="Login" />
}
</nav>
)
}
export default Navbar

View File

@ -0,0 +1,24 @@
import React, { useContext } from "react";
import styles from "./Navbar.module.css";
import { AuthContext } from "../../App";
import StyledNavLink from "../StyledNavLink/index";
const Navbar = () => {
const auth = useContext(AuthContext);
return (
<nav className={styles.navbar_wrapper}>
<StyledNavLink path="/" text="About" />
<StyledNavLink path="/graph" text="Graph" />
{auth === true ? (
<StyledNavLink path="/logout" text="Logout" />
) : (
<StyledNavLink path="/login" text="Login" />
)}
</nav>
);
};
export default Navbar;

View File

@ -1,32 +0,0 @@
import React from 'react'
import { NavLink } from 'react-router-dom';
import styles from "./StyledNavLink.module.css";
/**
* @param {{
* path: string,
* text: string,
* activeClass: string,
* inactiveClass: string
* }}
* @returns
*/
const StyledNavLink = ({
path = "/",
text = "Go To",
activeClass = styles.active_link,
inactiveClass = styles.inactive_link
}) => {
return (
<NavLink
to={path}
className={({ isActive }) => isActive ? activeClass : inactiveClass}
>
{text}
</NavLink>
)
}
export default StyledNavLink;

View File

@ -0,0 +1,28 @@
import React from "react";
import { NavLink } from "react-router-dom";
import styles from "./StyledNavLink.module.css";
type StyledNavLinkProps = {
path: string;
text: string;
activeClass?: string;
inactiveClass?: string;
}
const StyledNavLink = ({
path = "/",
text = "Go To",
activeClass = styles.active_link,
inactiveClass = styles.inactive_link,
}: StyledNavLinkProps): React.ReactNode => {
return (
<NavLink
to={path}
className={({ isActive }) => (isActive ? activeClass : inactiveClass)}
>
{text}
</NavLink>
);
};
export default StyledNavLink;

View File

@ -1,17 +0,0 @@
import { toast } from "react-toastify";
export const showErrorToastNotification = (message) => {
toast.error(message || "Server Error");
};
export const showSuccessToastNotification = (message) => {
toast.success(message || "Success");
};
export const showWarnToastNotification = (message) => {
toast.warn(message || "Warning");
};
export const showInfoToastNotification = (message) => {
toast.info(message || "Info");
};

View File

@ -0,0 +1,17 @@
import { toast, type ToastContent } from "react-toastify";
export const showErrorToastNotification = (message: ToastContent) => {
toast.error(message || "Server Error");
};
export const showSuccessToastNotification = (message: ToastContent) => {
toast.success(message || "Success");
};
export const showWarnToastNotification = (message: ToastContent) => {
toast.warn(message || "Warning");
};
export const showInfoToastNotification = (message: ToastContent) => {
toast.info(message || "Info");
};

1
src/globals.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module "*.module.css";

View File

@ -1,10 +1,12 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom/client'; import ReactDOM from "react-dom/client";
import './index.css'; import "./index.css";
import App from './App'; import App from "./App";
import reportWebVitals from './reportWebVitals'; import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />

View File

@ -1,4 +1,4 @@
import React, { useCallback, useContext, useEffect } from 'react'; import React, { useCallback, useContext, useEffect } from "react";
import { import {
ReactFlow, ReactFlow,
Controls, Controls,
@ -8,55 +8,57 @@ import {
addEdge, addEdge,
useReactFlow, useReactFlow,
MarkerType, MarkerType,
} from '@xyflow/react'; BackgroundVariant,
import Dagre from '@dagrejs/dagre'; ConnectionLineType,
type DefaultEdgeOptions,
type ProOptions,
type ReactFlowInstance,
type Node,
type Edge,
type OnConnect,
} from "@xyflow/react";
import Dagre, { type GraphLabel } from "@dagrejs/dagre";
import '@xyflow/react/dist/style.css'; import "@xyflow/react/dist/style.css";
import styles from './Graph.module.css'; import styles from "./Graph.module.css";
import { showErrorToastNotification, showInfoToastNotification } from '../../components/ToastNotification'; import {
showErrorToastNotification,
showInfoToastNotification,
} from "../../components/ToastNotification";
import { apiFetchGraph } from '../../api/operations'; import { apiFetchGraph } from "../../api/operations";
import { RefreshAuthContext } from "../../App"; import { RefreshAuthContext } from "../../App";
import Button from '../../components/Button'; import Button from "../../components/Button";
const initialNodes = []; const initialNodes: any[] = [];
// const initialNodes = [ const initialEdges: any[] = [];
// { id: '1', position: { x: 0, y: 0 }, data: { label: '1' } },
// { id: '2', position: { x: 0, y: 100 }, data: { label: '2' } },
// { id: '3', position: { x: 50, y: 50 }, data: { label: '3' } },
// ];
const initialEdges = [];
// const initialEdges = [
// { id: 'e1-2', source: '1', target: '2' }
// ];
const nodeOffsets = { const nodeOffsets = {
connected: { connected: {
origin: { origin: {
x: 0, x: 0,
y: 0 y: 0,
}, },
scaling: { scaling: {
x: 240, x: 240,
y: 80 y: 80,
} },
}, },
unconnected: { unconnected: {
origin: { origin: {
x: 800, x: 800,
y: 0 y: 0,
}, },
scaling: { scaling: {
x: 160, x: 160,
y: 40 y: 40,
} },
} },
} };
/** @type {import('@xyflow/react').DefaultEdgeOptions} */ const edgeOptions: DefaultEdgeOptions = {
const edgeOptions = {
animated: true, animated: true,
style: { style: {
stroke: "white", stroke: "white",
@ -67,10 +69,10 @@ const edgeOptions = {
color: "white", color: "white",
width: 40, width: 40,
height: 40, height: 40,
} },
}; };
const proOptions = { hideAttribution: true }; const proOptions: ProOptions = { hideAttribution: true };
const Graph = () => { const Graph = () => {
const refreshAuth = useContext(RefreshAuthContext); const refreshAuth = useContext(RefreshAuthContext);
@ -78,39 +80,65 @@ const Graph = () => {
const [playlistNodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [playlistNodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [linkEdges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const [linkEdges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onFlowInit = (instance) => { const onFlowInit = (instance: ReactFlowInstance) => {
console.debug("flow loaded"); console.debug("flow loaded");
} };
const onConnect = useCallback((params) => { const onConnect: OnConnect = useCallback(
setEdges((eds) => addEdge(params, eds)); (params) => {
console.debug("new connection"); setEdges((eds) => addEdge(params, eds));
console.debug(params); console.debug("new connection");
}, [setEdges]); console.debug(params);
},
[setEdges]
);
const getLayoutedElements = (nodes, edges, options = { direction: "TB" }) => { type getLayoutedElementsOpts = {
const g = new Dagre.graphlib.Graph() direction: GraphLabel["rankdir"];
};
const getLayoutedElements = (
nodes: Node[],
edges: Edge[],
options: getLayoutedElementsOpts = { direction: "TB" }
) => {
const g = new Dagre.graphlib.Graph();
g.setDefaultEdgeLabel(() => ({})); g.setDefaultEdgeLabel(() => ({}));
g.setGraph({ rankdir: options.direction, nodesep: 200, edgesep: 200, ranksep: 200 }); g.setGraph({
rankdir: options.direction,
nodesep: 200,
edgesep: 200,
ranksep: 200,
});
edges.forEach((edge) => g.setEdge(edge.source, edge.target)); edges.forEach((edge) => g.setEdge(edge.source, edge.target));
const connectedNodesID = new Set(edges.flatMap(edge => [edge.source, edge.target])); const connectedNodesID = new Set(
const connectedNodes = nodes.filter(node => connectedNodesID.has(node.id)); edges.flatMap((edge) => [edge.source, edge.target])
const unconnectedNodes = nodes.filter(node => !connectedNodesID.has(node.id)); );
const connectedNodes = nodes.filter((node) =>
connectedNodesID.has(node.id)
);
const unconnectedNodes = nodes.filter(
(node) => !connectedNodesID.has(node.id)
);
nodes.forEach((node) => { nodes.forEach((node) => {
g.setNode(node.id, { g.setNode(node.id, {
...node, ...node,
width: node.measured?.width ?? 0, width: node.measured?.width ?? 0,
height: node.measured?.height ?? 0, height: node.measured?.height ?? 0,
}) });
}); });
Dagre.layout(g); Dagre.layout(g);
let finalLayout = { edges }; let finalLayout: { nodes: Node[]; edges: Edge[] } = {
finalLayout.nodes = [ nodes: [],
edges: [],
};
finalLayout.edges = edges;
finalLayout.nodes.push(
...connectedNodes.map((node) => { ...connectedNodes.map((node) => {
const position = g.node(node.id); const position = g.node(node.id);
// We are shifting the dagre node position (anchor=center center) to the top left // We are shifting the dagre node position (anchor=center center) to the top left
@ -119,32 +147,41 @@ const Graph = () => {
const y = position.y - (node.measured?.height ?? 0) / 2; const y = position.y - (node.measured?.height ?? 0) / 2;
return { ...node, position: { x, y } }; return { ...node, position: { x, y } };
}), })
);
finalLayout.nodes.push(
...unconnectedNodes.map((node, idx) => { ...unconnectedNodes.map((node, idx) => {
const position = { const position = {
x: nodeOffsets.unconnected.origin.x + Math.floor(idx / 20) * nodeOffsets.unconnected.scaling.x, x:
y: nodeOffsets.unconnected.origin.y + Math.floor(idx % 20) * nodeOffsets.unconnected.scaling.y, nodeOffsets.unconnected.origin.x +
Math.floor(idx / 20) * nodeOffsets.unconnected.scaling.x,
y:
nodeOffsets.unconnected.origin.y +
Math.floor(idx % 20) * nodeOffsets.unconnected.scaling.y,
}; };
const x = position.x - (node.measured?.width ?? 0) / 2; const x = position.x - (node.measured?.width ?? 0) / 2;
const y = position.y - (node.measured?.height ?? 0) / 2; const y = position.y - (node.measured?.height ?? 0) / 2;
return { ...node, position: { x, y } }; return { ...node, position: { x, y } };
}) })
]; );
console.debug("layout generated"); console.debug("layout generated");
return finalLayout; return finalLayout;
}; };
const arrangeLayout = (direction) => { const arrangeLayout = (direction: GraphLabel["rankdir"]) => {
const layouted = getLayoutedElements(playlistNodes, linkEdges, { direction }); const layouted = getLayoutedElements(playlistNodes, linkEdges, {
direction,
});
setNodes([...layouted.nodes]); setNodes([...layouted.nodes]);
setEdges([...layouted.edges]); setEdges([...layouted.edges]);
setTimeout(flowInstance.fitView); setTimeout(flowInstance.fitView);
console.debug("layout applied"); console.debug("layout applied");
} };
useEffect(() => { useEffect(() => {
const fetchGraph = async () => { const fetchGraph = async () => {
@ -155,29 +192,37 @@ const Graph = () => {
} }
if (resp.status === 200) { if (resp.status === 200) {
// place playlist nodes // place playlist nodes
setNodes(resp.data.playlists.map((pl, idx) => { setNodes(
return { resp.data.playlists?.map((pl, idx) => {
id: `${pl.playlistID}`, return {
position: { id: `${pl.playlistID}`,
x: nodeOffsets.unconnected.origin.x + Math.floor(idx / 15) * nodeOffsets.unconnected.scaling.x, position: {
y: nodeOffsets.unconnected.origin.y + Math.floor(idx % 15) * nodeOffsets.unconnected.scaling.y, x:
}, nodeOffsets.unconnected.origin.x +
data: { Math.floor(idx / 15) * nodeOffsets.unconnected.scaling.x,
label: pl.playlistName, y:
metadata: { nodeOffsets.unconnected.origin.y +
pl Math.floor(idx % 15) * nodeOffsets.unconnected.scaling.y,
} },
} data: {
} label: pl.playlistName,
})); metadata: {
pl,
},
},
};
}) ?? []
);
// connect links // connect links
setEdges(resp.data.links.map((link, idx) => { setEdges(
return { resp.data.links?.map((link, idx) => {
id: `${idx}`, return {
source: link.from, id: `${idx}`,
target: link.to source: link.from,
} target: link.to,
})); };
}) ?? []
);
showInfoToastNotification("Graph updated."); showInfoToastNotification("Graph updated.");
return; return;
} }
@ -186,11 +231,11 @@ const Graph = () => {
return; return;
} }
if (resp.status === 401) { if (resp.status === 401) {
await refreshAuth(); refreshAuth();
} }
showErrorToastNotification(resp.data.message); showErrorToastNotification(resp.data.message);
return; return;
} };
fetchGraph(); fetchGraph();
}, [refreshAuth, setEdges, setNodes]); }, [refreshAuth, setEdges, setNodes]);
@ -200,7 +245,7 @@ const Graph = () => {
nodes={playlistNodes} nodes={playlistNodes}
edges={linkEdges} edges={linkEdges}
defaultEdgeOptions={edgeOptions} defaultEdgeOptions={edgeOptions}
connectionLineType="smoothstep" connectionLineType={ConnectionLineType.SmoothStep}
fitView fitView
proOptions={proOptions} proOptions={proOptions}
colorMode={"light"} colorMode={"light"}
@ -210,17 +255,17 @@ const Graph = () => {
onConnect={onConnect} onConnect={onConnect}
> >
<Controls /> <Controls />
<Background variant='dots' gap={36} size={3} /> <Background variant={BackgroundVariant.Dots} gap={36} size={3} />
{/* <Panel position="top-right"> */} {/* <Panel position="top-right"> */}
{/* <button onClick={() => arrangeLayout('TB')}>Arrange vertically</button> */} {/* <button onClick={() => arrangeLayout('TB')}>Arrange vertically</button> */}
{/* <button onClick={() => arrangeLayout('LR')}>Arrange horizontally</button> */} {/* <button onClick={() => arrangeLayout('LR')}>Arrange horizontally</button> */}
{/* </Panel> */} {/* </Panel> */}
</ReactFlow> </ReactFlow>
<div className={styles.operations_wrapper}> <div className={styles.operations_wrapper}>
<Button onClickMethod={() => arrangeLayout('TB')}>Arrange</Button> <Button onClickMethod={() => arrangeLayout("TB")}>Arrange</Button>
</div> </div>
</div> </div>
) );
} };
export default Graph; export default Graph;

View File

@ -1,13 +1,14 @@
import React, { useEffect } from "react" import React, { useEffect } from "react";
import { useSearchParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom";
import styles from "./Landing.module.css" import styles from "./Landing.module.css";
import { showInfoToastNotification, showSuccessToastNotification } from "../../components/ToastNotification"; import {
showInfoToastNotification,
showSuccessToastNotification,
} from "../../components/ToastNotification";
import AnimatedSVG from "../../components/AnimatedSVG"; import AnimatedSVG from "../../components/AnimatedSVG";
const Landing = () => { const Landing = () => {
// eslint-disable-next-line no-unused-vars const [searchParams] = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams();
useEffect(() => { useEffect(() => {
if (searchParams.get("login") === "success") { if (searchParams.get("login") === "success") {
showSuccessToastNotification("Logged in!"); showSuccessToastNotification("Logged in!");
@ -27,7 +28,7 @@ const Landing = () => {
<li>Periodic syncing</li> <li>Periodic syncing</li>
</ul> </ul>
</> </>
) );
} };
export default Landing export default Landing;

View File

@ -1,21 +0,0 @@
import React, { useEffect } from 'react';
import styles from './Login.module.css';
import { authLoginURL } from '../../api/paths';
// auth through backend
const Login = () => {
useEffect(() => {
const timeoutID = setTimeout(() => {
window.open(authLoginURL, "_self")
}, 1000);
return () => clearTimeout(timeoutID);
}, []);
return (
<div className={styles.login_wrapper}>
Redirecting to Spotify...
</div>
)
}
export default Login;

17
src/pages/Login/index.tsx Normal file
View File

@ -0,0 +1,17 @@
import React, { useEffect } from "react";
import styles from "./Login.module.css";
import { authLoginURL } from "../../api/paths";
// auth through backend
const Login = () => {
useEffect(() => {
const timeoutID = setTimeout(() => {
window.open(authLoginURL, "_self");
}, 1000);
return () => clearTimeout(timeoutID);
}, []);
return <div className={styles.login_wrapper}>Redirecting to Spotify...</div>;
};
export default Login;

View File

@ -1,20 +0,0 @@
import React, { useEffect } from 'react';
import styles from './Logout.module.css';
import { authLogoutURL } from '../../api/paths';
const Logout = () => {
useEffect(() => {
const timeoutID = setTimeout(() => {
window.open(authLogoutURL, "_self")
}, 1000);
return () => clearTimeout(timeoutID);
}, []);
return (
<div className={styles.logout_wrapper}>
See you soon!
</div>
)
}
export default Logout;

View File

@ -0,0 +1,16 @@
import React, { useEffect } from "react";
import styles from "./Logout.module.css";
import { authLogoutURL } from "../../api/paths";
const Logout = () => {
useEffect(() => {
const timeoutID = setTimeout(() => {
window.open(authLogoutURL, "_self");
}, 1000);
return () => clearTimeout(timeoutID);
}, []);
return <div className={styles.logout_wrapper}>See you soon!</div>;
};
export default Logout;

View File

@ -1,17 +0,0 @@
import React, { useEffect } from 'react';
import styles from "./PageNotFound.module.css";
import { showWarnToastNotification } from '../../components/ToastNotification';
const PageNotFound = () => {
useEffect(() => {
showWarnToastNotification("Oops!")
}, []);
return (
<div className={styles.pnf_wrapper}>
PageNotFound
</div>
)
}
export default PageNotFound

View File

@ -0,0 +1,13 @@
import React, { useEffect } from "react";
import styles from "./PageNotFound.module.css";
import { showWarnToastNotification } from "../../components/ToastNotification";
const PageNotFound = () => {
useEffect(() => {
showWarnToastNotification("Oops!");
}, []);
return <div className={styles.pnf_wrapper}>Page Not Found</div>;
};
export default PageNotFound;

View File

@ -1,13 +0,0 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

12
src/reportWebVitals.ts Normal file
View File

@ -0,0 +1,12 @@
import { type MetricType, onCLS, onFCP, onLCP, onTTFB } from "web-vitals";
const reportWebVitals = (onPerfEntry?: (metric: MetricType) => void): void => {
if (onPerfEntry && onPerfEntry instanceof Function) {
onCLS(onPerfEntry);
onFCP(onPerfEntry);
onLCP(onPerfEntry);
onTTFB(onPerfEntry);
}
};
export default reportWebVitals;

View File

@ -14,21 +14,21 @@ const AllRoutes = () => {
<Routes> <Routes>
{/* Routes that require user to be logged in */} {/* Routes that require user to be logged in */}
<Route element={<AuthOnlyRoutes />}> <Route element={<AuthOnlyRoutes />}>
<Route exact path="/logout" element={<Logout />} /> <Route path="/logout" element={<Logout />} />
<Route exact path="/graph" element={<Graph />} /> <Route path="/graph" element={<Graph />} />
{/* <Route exact path="/playlists" element={<Playlists />} /> */} {/* <Route path="/playlists" element={<Playlists />} /> */}
</Route> </Route>
{/* Routes that require user to be logged *out* */} {/* Routes that require user to be logged *out* */}
<Route element={<UnAuthOnlyRoutes />}> <Route element={<UnAuthOnlyRoutes />}>
<Route exact path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
</Route> </Route>
{/* Common routes */} {/* Common routes */}
<Route exact path="/" element={<Landing />} /> <Route path="/" element={<Landing />} />
{/* 404 */} {/* 404 */}
<Route exact path="/page-not-found" element={<PageNotFound />} /> <Route path="/page-not-found" element={<PageNotFound />} />
<Route path="*" element={<PageNotFound />} /> <Route path="*" element={<PageNotFound />} />
</Routes> </Routes>
); );

View File

@ -1,16 +0,0 @@
/**
* Returns a string with zero padding of the number
*
* @param {number} num Input number (positive integer only, for now)
* @param {number} places Number of zeroes to pad
* @param {"before" | "after"} position Position of zeroes
* @returns {string} Zero-padded string
*/
export const zeroPaddedString = (num, places, position) => {
if (num < 0) throw new Error("negative number");
if (places < 0) throw new Error("invalid number of zeroes");
if (position !== "before" && position !== "after") throw new Error("invalid position (before or after only)");
const zeroes = "0".repeat(places);
return position === "before" ? '' + zeroes + num : '' + num + zeroes;
}

13
src/utils/numFormatter.ts Normal file
View File

@ -0,0 +1,13 @@
export const zeroPaddedString = (
num: number,
places: number,
position: "before" | "after"
): string => {
if (num < 0) throw new Error("negative number");
if (places < 0) throw new Error("invalid number of zeroes");
if (position !== "before" && position !== "after")
throw new Error("invalid position (before or after only)");
const zeroes = "0".repeat(places);
return position === "before" ? "" + zeroes + num : "" + num + zeroes;
};

111
tsconfig.json Normal file
View File

@ -0,0 +1,111 @@
{
"include": [
"src/**/*"
],
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
"plugins": [
{
"name": "typescript-plugin-css-modules"
}
],
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./tsout", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}