How to “run code through Rollup first, then pass it to Babel”?

To quote the top of the readme for @rollup/plugin-babel:

If you’re using Babel to transpile your ES6/7 code and Rollup to generate a standalone bundle, you have a couple of options:

  • run the code through Babel first, being careful to exclude the module transformer, or
  • run the code through Rollup first, and then pass it to Babel.

By default, using the recommended option babelHelpers: "bundled", @rollup/plugin-babel appears to be running Babel during the build process, as each file is read. I would like to implement the second option, let Rollup bundle the files, then pass the bundle to Babel for transpilation. But I don’t see a way to do it.

It occurs to me that I could write a Rollup plugin that runs Babel in a buildEnd hook. But:

  1. How do I prevent Babel from running during the build?
  2. How do I run Babel from inside that hook function?

The plugin readme makes it sound so simple, but it never describes how to implement it. How can I accomplish this?

Use localStorage to get dark/light mode when loading a page? [closed]

I have a script to change the css file and it works once the site is loaded but when I reload the page it always applies the dark css file no matter what is stored in localStorage.

javascript:

var darkmode = localStorage.getItem('darkmode');
var theme = document.querySelector("#theme-link");

window.onload = function() {
    setTheme();
};

function setTheme() {
    if(darkmode) {
        theme.href = "dark-theme.css";
    } else {
        theme.href = "light-theme.css";
    }
}

function changeTheme() {
    darkmode = !darkmode;
    setTheme();
    localStorage.setItem('darkmode', darkmode);
}

html:

<head>
    <link rel="stylesheet" id="theme-link">
</head>
<body>
    <img src="img/square.png" onclick="changeTheme()">
    <script src="themechanger.js"></script>
</body>

Drawing box in custom indicator for TradingView advance charting library

I am try big to draw a box as like we do in pinescript filling some logic in my constructor function inside custom indicator getter function. But it seems we have only line plot field in there. What I need to do is to draw a box when at an index and when the logic is not satisfied anymore I delete that box. Can you help me please.

incorporating the Drawings API within custom_indicators_getter by creating a shape in the constructor and updating it within the main fn. no hope

How to deal with 30k+ HTTP requests in Node

Long story short, I’m trying to fetch all e-mails on my Gmail inbox, which happens to have 36k elements. The way I’m doing it is by using googleapis library to fetch each e-mail (given that I already have the list of ids):

      return this.gmailApi.users.messages.get({
        userId: 'me',
        id: email.id,
        format: 'metadata',
        metadataHeaders: ['Date', 'Subject', 'From']
      });

Since I have thousands of e-mails to fetch, I’m trying to do as many as possible at the same time.

  • First attempt was trying to fetch all e-mails with Promise.all
  • Second attempt was doing the same, but in batches of 500*
  • Third attempt reduced the batch of requests to 50*

*: Still using Promise.all for each batch, iterated by a for await

In all attempts, I got the following error:

Uncaught GaxiosError Error: request to <gmail api url> failed, reason: connect EMFILE <xxx.xxx.xx.xxx:xxx> - Local (undefined:undefined)
    at _request (/gmail-api/node_modules/gaxios/build/src/gaxios.js:149:19)
    at processTicksAndRejections (<node_internals>/internal/process/task_queues:95:5)
gaxios.js:149
Process exited with code 1

Now, I know this is related to the amount of descriptors my OS allows me to have open (mine is set to 4096, I believe), so it probably has nothing to do with google apis whatsoever, so my question is:

What’s a way of dealing with this in a performant manner that will not take hours (like it does when I fetch e-mails sequentially) or that will not exceed the maximum allowed file descriptors? I’m not sure yet why running it in small batches also didn’t work, but I’m really suspicious there are better ways of handling this, either with connection reusage or some other processing technique.

Also, I can think of a few workarounds to deal with this, but they’re all “bad practices” and most would take a very long time anyway, so I would be glad if you could share any ideas.

Some (probably) irrelevant informations about my project:

  • NodeJS v18.20
  • Typescript v5.6.3
  • googleapis v144.0.0
  • Ubuntu 20 (on WSL2)

Responsive navigation bar with dropdown buttons

The problem: in responsive view, dropdown button does not move to the visible navigation bar from the menu icon when any of its dropdown content is clicked.

In more detail: In responsive view (screen width under 730px), the navigation bar becomes a menu icon, and I want any <a> or dropdown <button> to move to the visible navigation bar when any dropdown item is clicked.

The issue

The code is at https://codepen.io/Balazs-Keisz/pen/KKOmgQX

Here’s the JavaScript handling the navigation bar:

    // Function to toggle responsive class for topnav when icon is clicked
    function myFunctionIcon() {
      var x = document.getElementById("myTopnav");
      if (x.className === "topnav") {
        x.className += " responsive";
      } else {
        x.className = "topnav";
      }
    }
    
    // Function to clear all highlights
    function clearAllHighlights() {
      // Remove highlight from all nav items and buttons
      document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
        item.classList.remove('highlight-menu');
      });
    }
    
    // Function to highlight the clicked button or link
    function highlightButton(clickedElement) {
      clearAllHighlights(); // Clear previous highlights
      clickedElement.classList.add('highlight-menu'); // Highlight the clicked element
      
      
    // If the clicked element is part of a dropdown, ensure only the dropdown button is highlighted
      const dropdownButton = clickedElement.closest('.dropdown')?.querySelector('.dropbtn');
      if (dropdownButton) {
        dropdownButton.classList.add('highlight-menu'); // Highlight only the dropdown button
      }
    }
    
    // Event listeners for nav items and dropbtns
    document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
      item.addEventListener('click', function(event) {
        highlightButton(this); // Apply highlight
        // Prevent closing the menu when clicking the dropdown button
        if (this.classList.contains('dropbtn')) {
          event.stopPropagation(); // Prevent event from bubbling up
        }
      });
    });
    
    // Event listener for each dropdown content link to close responsive menu
    document.querySelectorAll('.dropdown-content a').forEach(link => {
      link.addEventListener('click', function(event) {
        const dropdownButton = this.closest('.dropdown').querySelector('.dropbtn');
        highlightButton(dropdownButton); // Highlight only the dropdown button
        if (window.innerWidth <= 729) {
          document.getElementById("myTopnav").classList.remove("responsive"); // Close responsive menu
        }
      });
    });
    
    // Event listener for any other responsive <a> links to close the menu
    document.querySelectorAll('.nav-item').forEach(link => {
      link.addEventListener('click', function() {
        if (window.innerWidth <= 729) {
          document.getElementById("myTopnav").classList.remove("responsive"); // Close responsive menu
        }
      });
    });
    
    // Dropdown toggle function for multiple dropdowns
    function toggleDropdown1(dropdownId) {
      var dropdown = document.getElementById(dropdownId);
      dropdown.classList.toggle("show1");
    }
    
    // Function to close all dropdowns
    function closeAllDropdowns() {
      var dropdowns = document.getElementsByClassName("dropdown-content");
      for (var i = 0; i < dropdowns.length; i++) {
        dropdowns[i].classList.remove('show1');
      }
    }

    // Toggle dropdown for the clicked dropdown button
    function toggleDropdown1(dropdownContent) {
      // Check if the clicked dropdown is already open
      var isOpen = dropdownContent.classList.contains('show1');
      
      // Close all dropdowns
      closeAllDropdowns();

      // If the clicked dropdown was not open, open it
      if (!isOpen) {
        dropdownContent.classList.add('show1');
      }
    }

    // Add event listeners for each dropdown button
    document.querySelectorAll('.dropbtn').forEach(button => {
      button.addEventListener('click', function(event) {
        // Prevent closing on the same dropdown click
        event.stopPropagation();

        // Get the corresponding dropdown content
        var dropdownContent = this.nextElementSibling;

        // Toggle the dropdown
        toggleDropdown1(dropdownContent);
      });
    });

    // Close the dropdown if the user clicks outside of any dropdown
    window.addEventListener('click', function(event) {
      if (!event.target.matches('.dropbtn') && !event.target.matches('.dropbtn i')) {
        closeAllDropdowns();
      }
    });

    // Attach event listeners to all <a> and <button> elements for highlighting
    document.querySelectorAll('.nav-item, .dropbtn').forEach(item => {
      item.addEventListener('click', function() {
        highlightButton(this); // Highlight the clicked element
      });
    });

Error 403 While Downloading Youtube Video Using ytdl-core

I was working on a Project where I’m attempting to download a video file, audio file, and a merged version into a single complete video using ytdl-core.
But while downloading video file only it give error in console:
Error in video stream: Status code: 403 Error: Status code: 403

I expect to get: video file, audio file and merged file containing both video and audio.
Instead of the expected files, receive an empty video file with the name <video_title>_video.webm

Here’s the relevant part of code:

  try {
    createDirectoryIfNotExists('./Downloaded Videos');
    const videoInfo = await getInfo(url);
    const videoTitle = videoInfo.videoDetails.title;
    const sanitizedTitle = removeSpecialCharacters(emojiStrip(videoTitle));
    console.log(yellow(`nBaixando vídeo: ${videoTitle}`));

    const videoFilePath = `./Downloaded Videos/${sanitizedTitle}_video.webm`;
    const audioFilePath = `./Downloaded Videos/${sanitizedTitle}_audio.m4a`;
    const outputFilePath = `./Downloaded Videos/${sanitizedTitle}.mp4`;

    if (existsSync(outputFilePath)) {
      console.error(red(`nArquivo "${outputFilePath}" já existe. Pulando.`));
      return;
    }

    console.log(yellow(`Downloading video stream...`));
    const videoFormat = chooseFormat(videoInfo.formats, { quality: 'highestvideo' });
    const videoStream = downloadFromInfo(videoInfo, { format: videoFormat });

    videoStream.on('error', (error) => {
      console.error(red(`Error in video stream: ${error.message}`,error));
    });

    videoStream.pipe(createWriteStream(videoFilePath));

    // await new Promise((resolve) => videoStream.on('end', resolve));
    await new Promise((resolve, reject) => {
      videoStream.on('end', () => {
        console.log(green(`Video stream download completed.`));
        resolve();
      });
      videoStream.on('error', reject);
    });

   //rest of the code for download audio file and then next..........
} catch (error) {
    console.error(red(`Erro ao processar o vídeo: ${error}`));
  }

The necessary imports are as follows::

import { existsSync, mkdirSync, createWriteStream, unlinkSync } from 'fs';
import ytdl from 'ytdl-core';
const { getInfo, chooseFormat, downloadFromInfo, validateURL } = ytdl;
import chalk from 'chalk';
const { yellow, red, green } = chalk;
import emojiStrip from 'emoji-strip';
import { exec } from 'child_process';
import ffmpeg from 'ffmpeg-static';
import clipboardy from 'clipboardy';
const { writeSync } = clipboardy;

Is this issue occurring because the URL in videoFormat is unreachable and returns a 403 error? How can I resolve this issue, and is there an alternative approach to downloading videos using ytdl-core? Any suggestions would be greatly appreciated!

Blackjack game: 1st and 2nd cards are displyed twice when i ask for a new card

I’m newbie and this is my 1st question on stackoverflow. Please help me.
When I click on “Start game” button I got two cards (i.e.: Cards: 6 7) and this is Ok.
But when I click on “New card” button, the 1st and 2nd card are displayed another time with the 3rd card (i.e. 6 7 6 7 11). Thanks for your help.

let cards = []
let sum = 0; 
let hasBlackJack = false
let isAlive = false;
let message = "";
let messageEl = document.getElementById("message-el");
let cardsEl = document.getElementById("cards-el");
let sumEl = document.getElementById("sum-el");
let player = document.getElementById("player-el");

let players = {
    name : "Moez",
    chips : 195
}
player.innerText = players.name + ": $" + players.chips;

function getRandomCard(){
    let randomCard = Math.floor(Math.random() * 13) + 1;
    if (randomCard === 1) {
        return 11;
    } else if (randomCard > 10) {
        return 10;
    } else {
        return randomCard;
    }
    
}

function startGame() {
    isAlive = true;
    let firstCard = getRandomCard();
    let secondCard = getRandomCard();
     cards = [firstCard, secondCard];
    sum = firstCard + secondCard;

    cardsEl.innerText = "Cards: ";
    for (let i=0; i<cards.length; i++) {
        cardsEl.innerText += " " + cards[i] + " ";
    }

    sumEl.innerText = "Sum: " + sum;
    if (sum <= 20) {
        message = "Do you want to draw a new card?"
    } else if (sum === 21) {
        message = "Wohoo! You've got Blackjack!";
        hasBlackJack = true;
    } else {
        message = "You're out of the game!";
        isAlive = false;
    }
    messageEl.innerText = message;
}

function newCard() {
    
    if (isAlive = true && hasBlackJack === false) {
        let thirdCard = getRandomCard();
        cards.push(thirdCard);
        sum = sum + thirdCard;
        for (let i=0; i<cards.length; i++) {
            cardsEl.innerText += " " + cards[i] + " ";
        }
        sumEl.innerText = "Sum: " + sum;

        if (sum <= 20) {
            message = "Do you want to draw a new card?"
        } else if (sum === 21) {
            message = "Wohoo! You've got Blackjack!";
            hasBlackJack = true;
        } else {
            message = "You're out of the game!";
            isAlive = false;
        }
        messageEl.innerText = message;
    
        
    }
    
}

I want that when I click on “New card” I got only the 3rd card displayed next to the 1st and 2nd card.

Authorize script for file not user

PLATFORM: Google Sheets GIVEN: Input spreadsheet and Data spreadsheet

ISSUE: Script will not edit Data sheet because user is only authorized for Input sheet.

DESIRED OUTCOME: User can click button to run macro to edit Data sheet without being authorized to view or edit Data sheet.

I’m trying to google and search here, but I’m not sure how to find what I need. I keep getting simple ‘authorize script’ tutorials. I imagine this is something with Deploy or script authorization that I’m not used to.

I have a Data sheet with 60 columns and a user that should be able to see and edit 20 of those columns, but not all 60. I built an importrange to pull in some of the columns to view and a script (in the Input sheet) to send user inputs back to edit the data sheet (click-button). This works for me as I’m authorized in both sheets, but does not work for the end user as they are authorized only for the Input sheet. How can I authorize the script but not the user to do that editing?

Does Cypress can run inside web worker?

I want to run a Cypress test case within a web worker to avoid relying on API, which would reduce deployment costs in a serverless environment. My main challenge is figuring out how to import Cypress modules and make its commands available in the web worker context. Below is the test case I’m aiming to run.

it("should visit all the URLs and check thier status", () => {
    cy.get("@URLs").each((url) => {
      cy.visit(url, { failOnStatusCode: false }).then(() => {
        cy.get('img[src="/images/en.svg"]').click();
        cy.get("body").then(($body) => {
          cy.wait(4000).then(() => {
            if (
              $body.find("div.css-t9xer0").length > 0 ||
              $body.find("div.css-1ceac07").length > 0
            ) {
              cy.get("h2.chakra-heading.css-18j379d").then((element) => {
                const text = element.text();
                testResults.push({
                  url,
                  status: text.includes("Standard QR is valid")
                    ? "passed"
                    : "failed",
                });
              });
            } else if ($body.find("div.css-7htuvi").length > 0) {
              cy.get("p.chakra-text.css-mhu0er").then((element) => {
                const text = element.text();
                testResults.push({
                  url,
                  status: text.includes("QR code not found")
                    ? "QR code not found"
                    : text,
                });
              });
            } else {
              testResults.push({
                url,
                status: "QR code has content not expected.",
              });
            }
          });
        });
      });
    });
  });

I tried this but it didn’t work.

import cypress from "cypress";
// const cypress = require("cypress");

self.onmessage = async function (e) {
  const { data, status, specs, config } = e.data;

  if (status === "start") {
    try {
      
      // const results = await cypress.run({
      //   spec: specs,
      //   config: {
      //     ...config,
      //     video: false,
      //     screenshotOnRunFailure: false,
      //   },
      // });

      // console.log(results);

      self.postMessage({ status: "done", data: results });
    } catch (error) {
      self.postMessage({ status: "error", error: error.message });
    }
  }
};

Can’t validate encrypted credentials with bcrypt

I have this routes for registering and login:

import express from 'express';
import bcrypt from 'bcrypt';
import db from '../models/index.js';

const router = express.Router();

router.post('/login', async (req, res) => {
    const { email, password } = req.body;

    try {
        const user = await db.User.findOne({
            where: {
                email
            }
        });

        if (!user) {
            return res.status(404).json({ message: 'User not found' });
        }

        const isMatch = await bcrypt.compare(password, user.password);

        if (isMatch) {
            res.status(200).json({ message: 'Login successful', user });
        } else {
            res.status(401).json({ message: 'Wrong credentials' });
        }
    } catch (error) {
        res.status(500).json({ message: `Internal server error: ${error.message}` });
    }
});

router.post('/register', async (req, res) => {
    console.log('Request body:', req.body);

    const { name, email, password } = req.body; // Collecting name, email, and password

    if (!email) {
        return res.status(400).json({ message: 'Email is required' });
    }

    try {
        const existingUser = await db.User.findOne({ where: { email } });
        if (existingUser) {
            return res.status(400).json({ message: 'User already exists' });
        }

        const hashedPassword = await bcrypt.hash(password, 10);

        const newUser = await db.User.create({
            name,
            email,
            password: hashedPassword,
            provider: 'credentials',
            role: 'guest'
        });

        res.status(201).json({ message: 'User registered successfully', user: { id: newUser.id, name: newUser.name, email: newUser.email } });
    } catch (error) {
        res.status(500).json({ message: `Error registering user:  ${error.message}` });
    }
});


export default router;

Looks like registering is working fine, at least I can see encrypted password in my database. But when I’m using exactly the same password (as a plain text) to login and comparing it with what I have in database it’s always false comparison. I’m talking about this line:

const isMatch = await bcrypt.compare(password, user.password);

Can’t figure out what could be the problem. I’m 100% using correct password, database column is large enough, arguments order is correct, db connection is working and I can see the password from db in the logs…

react redraw only updated array object when property changes using array.map

I would have guessed that this question is a dupe, but I searched and I can’t find it…
Full source for this q is here: https://github.com/micahg/arraytest.

Using react, I’m mapping an array of objects (stored with useState) to a component for display. I’d like react to only render objects in the array with changed properties – but anything I’ve tried either renders every member or none. The (simplified) component:

const ContainerComponent = () => {

   const [things, setThings] = useState<Thing[]>([]);

   const change = () => {
      const newThing: Thing = {...things[0], name: "Third"};
      things.splice(0, 1);
      const newThings = [...things, newThing];
      setThings(newThings);
   }

   useEffect(() => {
      const initialThings: Thing[] = [ { id: 1, name: "First" }, { id: 2, name: "Second"} ];
      setThings(initialThings);
   }, []);

   return (
      <div>
         <div>Container</div>
         <div>
            {things.map(thing => <ContentComponent key={thing.id} thing={thing}/>)}
         </div>
         <button onClick={change}>Change</button>
      </div>
   );
}

I’m looking for the way to update the array so that react knows not to redraw the second element. What i notice in the react developer tools flamegraph is that the first ContentComponent rerenders because “Props changed” (makes sense) but the second rerenders because “The parent component rerendered”.

So far the best I can do is just export default memo(ContentComponent); in ChildComponent to avoid executing the second ChildComponent code… but is there a way to get react to redrawing that second child entirely?

Thanks in advance,

I am trying to implement google signin and i am passing IdToken to backend and get: Firebase ID token has incorrect “aud” (audience) claim

Firebase ID token has incorrect “aud” (audience) claim. Expected “test-f2978” but got “583659997900-tiqii1rc4mjsle349h42nq117h36fvbb.apps.googleusercontent.com”. Make sure the ID token comes from the same Firebase project as the service account used to authenticate this SDK. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.

FrontEnd Code :-

useEffect(() => {
// Configure Google Sign-in with the web client ID
GoogleSignin.configure({
webClientId: '583659997900-tiqii1rc4mjsle349h42nq117h36fvbb.apps.googleusercontent.com',
});
}, []);

// Function to handle Google login
const handleGoogleLogin = async () => {
setIsLoading(true);
try {
// Check if Google Play Services are available
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });

  // Sign in with Google
  const response = await GoogleSignin.signIn();
  console.log(response);
  
  const idToken = response.data?.idToken;
  console.log(idToken);
  
   // Send the ID token to your backend for verification and custom token generation
  const authToken = await signIn(idToken); 
  
  console.log(authToken);

}

Heres the frontend code the IdToken generated in frontend is passed to a fucntion it handles the response from backend and it gets the token from backend .

Backend Code:-

const serviceAccount = require('./firebase-admin-sdk.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),  
});
app.post("/api/generate-token", async (req, res) => {
const { idToken } = req.body;
try {
const decodedToken = await admin.auth().verifyIdToken(idToken);
const uid = decodedToken.uid;
res.status(200).json({
  message: "User signed in successfully",
  user: {
    uid: uid,
    email: decodedToken.email,
  },
});

} catch (error) {
console.log(error);
res.status(401).json({
message: "Invalid token",
error: error.message,
});
}
});

Status Code 403: Spotify API, Requesting User’s Top Genre and Top Artist

I am working on a HTML, and CSS web app that uses the Spotify API. Login works great, the only problem that I have is that if I don’t add the account I’m trying to login to the user dashboard, it doesn’t work and shows an error code of “Failed to load resource: the server responded with a status of 403 ()” and “results.html?code=AQ…WKGIbYgl3eGcTtIF:89 Error fetching artist data: Check settings on developer.spotify.com/dashboard, the user may not be registered.”

index.html

<!DOCTYPE html>
<html lang="en">
<head style="background-color: #061e3e;">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Spotify Outfit - Landing Page</title>
  <link rel="stylesheet" href="LandingPage.css">
</head>
<body style="background-color: #061e3e;">
  <!-- Landing Page Section -->
  <div id="landing-page" class="landing-page">
    <div class="container">
      <h1 class="title">
        <div>YOUR</div>
        <div>SPOTIFY</div>
        <div>OUTFIT</div>
      </h1>
      <p class="subtitle">Your Outfit, Inspired by Your Music Taste</p>
      <a href="https://accounts.spotify.com/authorize?client_id=ceae01a497814f499f7aebd4830653cb&response_type=token&redirect_uri=<redacted for privacy purposes>&scope=user-read-private,user-top-read,user-read-email,user-read-recently-played,user-read-playback-state,user-read-currently-playing&state=STATE&show_dialog=true" class="connect-button">
        <img src="spotify-logo.png" alt="Spotify Logo" class="spotify-logo">
        Connect Spotify
      </a>
    </div>
  </div>

  <!-- Results Page Section (Initially Hidden) -->
  <div id="results-page" class="results-page" style="display: none;">
    <div class="container">
      <h2>Your Top Genre:</h2>
      <p id="genre-name">Loading...</p>
      <h3>Your Top Artist:</h3>
      <p id="artist-name"></p>
      <img id="artist-image" src="outfit-placeholder.png" alt="Top Artist Image" class="artist-image">
    </div>
  </div>

  <script>
    

    // Function to extract access token from URL
    function getAccessTokenFromURL() {
      const hash = window.location.hash.substring(1);
      const params = new URLSearchParams(hash);
      return params.get("access_token");
    }

    // Fetch user's top artist data from Spotify
        function getAccessTokenFromURL() {
            var accessToken = null;
            var hash = window.location.hash.substring(1);
            var params = hash.split('&');
            for (var i = 0; i < params.length; i++) {
                var param = params[i].split('=');
                if (param[0] === 'access_token') {
                    accessToken = param[1];
                    break;
                }
            }
            return accessToken;
        }
        // Check if the URL contains an access token
        window.addEventListener("load", function() {
            var accessToken = getAccessTokenFromURL();
            if (accessToken) {
                // If access token is present, store it in sessionStorage
                sessionStorage.setItem('accessToken', accessToken);
                // Redirect to the main page or perform other actions
                window.location.href = <redacted for privacy purposes>
            }
        });
   
  </script>
</body>
</html>

results.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Spotify Outfit - Results Page</title>
  <link rel="stylesheet" href="ResultsPage.css">
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script>
 document.addEventListener("DOMContentLoaded", function() {
    var clientId = 'ceae01a497814f499f7aebd4830653cb'; // Replace with your actual client_id
    var clientSecret = <redacted for privacy purposes>
  const redirectUri = <redacted for privacy purposes>

    const urlParams = new URLSearchParams(window.location.search);
    const authCode = urlParams.get('code');
    console.log("Auth Code:", authCode); // Check if the code is present

    if (authCode) {
        exchangeCodeForToken(authCode);
    } else {
        redirectToSpotifyAuth();
    }

    function redirectToSpotifyAuth() {
        const scopes ='user-read-private user-read-email user-read-playback-state user-modify-playback-state user-read-recently-played user-top-read';
        const authUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scopes)}`;
        window.location.href = authUrl;
    }

    function exchangeCodeForToken(code) {
        $.ajax({
            url: "https://accounts.spotify.com/api/token",
            type: "POST",
            headers: {
                "Authorization": "Basic " + btoa(`${clientId}:${clientSecret}`)
            },
            data: {
                grant_type: "authorization_code",
                code: code,
                redirect_uri: redirectUri
            },
            success: function(response) {
                console.log("Access Token Response:", response);
                const accessToken = response.access_token;
                fetchUserTopArtistAndGenre(accessToken);
            },
            error: function(xhr, status, error) {
                console.error("Error exchanging code for token:", xhr.responseText || error);
            }
        });
    }

    function fetchUserTopArtistAndGenre(accessToken) {
        $.ajax({
            url: "https://api.spotify.com/v1/me/top/artists?time_range=short_term&limit=1",
            type: "GET",
            headers: {
                "Authorization": "Bearer " + accessToken
            },
            success: function(response) {
                console.log("API Response:", response);
                const mainGenreElement = $('#mainGenre');
                const artistContainer = $('#artist-container');
                const artistNameElement = $('#artistName');
                const artistImageElement = $('#artistImage');

                if (response.items.length > 0) {
                    const topArtist = response.items[0];
                    const genres = topArtist.genres;
                    const artistName = topArtist.name;
                    const artistImage = topArtist.images.length > 0 ? topArtist.images[0].url : null;

                    const mainGenre = genres.length > 0 ? genres[0] : "No genre available";
                    mainGenreElement.text(mainGenre);
                    artistNameElement.text(artistName);
                    if (artistImage) {
                        artistImageElement.attr('src', artistImage).show();
                    } else {
                        artistImageElement.hide();
                    }
                    artistContainer.show();
                } else {
                    mainGenreElement.text("No genre available.");
                    artistContainer.hide();
                }
            },
            error: function(xhr, status, error) {
                console.error("Error fetching artist data:", xhr.responseText || error);
            }
        });
    }
});
</script>
</head>
<body>

    <div class="results-page">
        <div class="results-container">
            <h1 class="main-genre">Your main genre is...</h1>
            <h2 id="mainGenre" class="genre">Loading...</h2>

            <div id="artist-container" class="artist-container" style="display: none;">
                <h3 class="artist-title">Your Top Artist:</h3>
                <p id="artistName" class="artist-name"></p>
                <img id="artistImage" alt="Top Artist" class="artist-image" style="width: 200px; height: auto; border-radius: 10px;" />
            </div>
        <h3 class="outfit-title">Here is your outfit!</h3>
      <img id="outfit-image" src="outfit-placeholder.png" alt="Outfit" class="outfit-image" />
        </div>
    </div>
</body>
</html>

Initially, I thought it was the scope parameters because it was set to user-read-private, I added the ones I think I needed (‘user-read-private user-read-email user-read-playback-state user-modify-playback-state user-read-recently-played user-top-read’) and it still didn’t work out for me. So, I’m quite stuck as someone newer to this.

Create custom array object type with additonal functions and class declaration?

Iam struggling with creating an custom array object type with additional functions, and a class construtor:

The custom array object should have the following content:

["fruits", ["apple", "banana", "pineapple"]]; //array content: string and another array
  1. I would like to work on such an array with custom functions, like:
Array.prototype.returnArrayContent = function(){
    this[1].forEach(element => console.log(element)); }
// But the problem in this case is I add another function to the array prototype, instead 
// I would like to a new Object type which inherits all functions, property from built-in-Array-Object
  1. Because I read in data from JSON file I would like to create those custom array objects with a class constructor like:
new customArrayObject (["fruits", ["apple", "banana", "pineapple"]])

How could this be achieved? As Iam kind of a newbie do I need typescript for this?