Possible to export an async function from a Mongoose.js model to simulate findOne() with callback

I recently updated a large project to the newest version of Mongoose and realized that support for callbacks in findOne() have been dropped. I’m curious if anyone has recommendations for the most low-impact way to update my codebase. Currently, in my users.js model I’m using this code:

module.exports.getOne = function(query, callback) {
  User.findOne(query, callback);
}

and then calling the function like so:

User.getOne({ 'username' : username }, function(err, user) { (SOME CODE) });

Is there a way to rewrite the getOne function using the newer async method that would essentially allow me to leave all the calls as-is (i.e. allows me to simply pass function to the call that will have access to the data in findOne)? Or do I just need to bite the bullet and rewrite every call with the new format?

Any suggestions appreciated!

I keep getting TypeError: Cannot read properties of undefined (reading ‘0’)

Trying to convert google form into a pdf and then email it. Currently it stores the data into an array, and then later grabs that info, puts it into a doc instead of a form.

When it gets to grabbing the info is where it keeps throwing an error.


function createPDF(){
// all info collected on the form, pre-filled to test
const info = {
  'First Name' : ['Mike'],
  'Last Name' : ['Wazowski'], 
  'Birthday' : [0], 
  'Email' : ['[email protected]'],
  'Phone Number' : ['1'],
  'Province' : ['Ontario'],
  'Contact Method' : ['email'],
  'Language': ['english'],  
  'Type Of Service': ['in person'],
  'Child Name': [],
  'Child Birthday': [],
  'Services Required': ['counselling'],
  'Staff Requested': ['Nancy'],
  'Priority':['Health'],
  'Referral': ['blank'],
  'Jane Consent': ['Yes'],
  };


const pdfFolder = DriveApp.getFolderById("1g58GUQLPjPonsHtxj5LlxyoDgXs5wj2R");
const tempFolder = DriveApp.getFolderById("1Fkzf0xeZcedfq7BF2k3V4mn4Pz_LsXsv");
const templateDoc = DriveApp.getFileById("1eOqom8SqhuDUpIqYEVum-EvQ09cVz2d_XCLcRNAz8jE");

const newTempFile = templateDoc.makeCopy(tempFolder);
const openDoc = DocumentApp.openById(newTempFile.getId());
const body = openDoc.getBody();

  body.replaceText("{fn}", info['First Name'][0]);
  body.replaceText("{ln}", info['Last Name'][0]); 
  body.replaceText("{bd}", info['Birthday'][0]);
  body.replaceText("{em}", info['Email'][0]);
  body.replaceText("{pn}", info['Phone Number'][0]);
  body.replaceText("{pv}", info['Province'][0]);
  body.replaceText("{cm}", info['Contact Method'][0]);
  body.replaceText("{lg}", info['Language'][0]);
  body.replaceText("{ts}", info['Type of Service'][0]); //where its throwing the error
  body.replaceText("{cn}", info['Child Name'][0]);
  body.replaceText("{cbd}", info['Child Birthday'][0]);
  body.replaceText("{sr}", info['Services Required'][0]);
  body.replaceText("{stf}", info['Staff Requested'][0]);
  body.replaceText("{pri}", info['Priority'][0]);
  body.replaceText("{ref}", info['Referral'][0]);
  body.replaceText("{jc}", info['Jane Consent'][0]);

const blobPDF = newTempFile.getAs(MimeType.pdf);
const pdfFile = pdfFolder.createFile(blobPDF).getName("Form Response");


}

I’m trying to copy this guy: https://www.youtube.com/watch?v=EpZGvKIHmR8&ab_channel=LearnGoogleSheets%26ExcelSpreadsheets

but his works and mine doesn’t, and the only difference is the amount of data I have and the fact that i’m using a date and not a string.

Webrtc Connection ice candidate state getting stuck on “new”

I want to establish a webrtc connection between 2 clients on the same device but when i try doing it the ice candidate state doesnt continues to checking and gets stuck in new how can i fix it.

basically what i do is

    
const connection = new window.RTCPeerConnection();

const localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
document.getElementById('localVideo').srcObject = localStream;

localStream.getTracks().forEach(track => {
    console.log(`Track kind: ${track.kind}, enabled: ${track.enabled}`);
    connection.addTrack(track, localStream);
});

connection.onicecandidate = (event) => {
    console.log('New Candidate:', event.candidate);
};

connection.onconnectionstatechange = () => {
    console.log('Connection state:', connection.connectionState);
};

connection.onicecandidateerror = (event) => {
    console.error('ICE Candidate error:', event.errorCode, event.errorText);
};

connection.onsignalingstatechange = () => {
    console.log('Signaling state changed:', connection.signalingState);
};

connection.oniceconnectionstatechange = () => {
    console.log('ICE Connection state:', connection.iceConnectionState);
};

connection.onicegatheringstatechange = () => {
    console.log('ICE Gathering State:', connection.iceGatheringState);
};

connection.ondatachannel = (event) => {
    console.log('Data channel is created:', event.channel);

    event.channel.onmessage = (messageEvent) => {
        console.log('Received message on data channel:', messageEvent.data);
    };

    event.channel.onopen = () => {
        console.log('Data channel is open.');
    };

    event.channel.onclose = () => {
        console.log('Data channel is closed.');
    };
};

const allStreams = [];
connection.ontrack = (event) => {
    allStreams.push(...Array.from(event.streams));
    console.log(event.streams[0]);
    document.getElementById('remoteVideo').srcObject = event.streams[0];
};


const offer = await connection.createOffer();
await connection.setLocalDescription(offer);

const candidates = [];
connection.onicecandidate = (event) => {
    console.log('New Candidate:', event.candidate);
    if (event.candidate) {
        candidates.push(event.candidate);
    } else {
        console.log(`const data=${JSON.stringify({offer: offer, candidates: candidates})};`)
    }
};

i run this code on the one i want to use as offer

then i take the result of this code and run it on the other machine (to load the necessary data)

then i run this code to create the answer


const connection = new window.RTCPeerConnection();

const localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
document.getElementById('localVideo').srcObject = localStream;

localStream.getTracks().forEach(track => {
    console.log(`Track kind: ${track.kind}, enabled: ${track.enabled}`);
    connection.addTrack(track, localStream);
});

connection.onicecandidate = (event) => {
    console.log('New Candidate:', event.candidate);
};

connection.onconnectionstatechange = () => {
    console.log('Connection state:', connection.connectionState);
};

connection.onicecandidateerror = (event) => {
    console.error('ICE Candidate error:', event.errorCode, event.errorText);
};

connection.onsignalingstatechange = () => {
    console.log('Signaling state changed:', connection.signalingState);
};

connection.oniceconnectionstatechange = () => {
    console.log('ICE Connection state:', connection.iceConnectionState);
};

connection.onicegatheringstatechange = () => {
    console.log('ICE Gathering State:', connection.iceGatheringState);
};

connection.ondatachannel = (event) => {
    console.log('Data channel is created:', event.channel);

    event.channel.onmessage = (messageEvent) => {
        console.log('Received message on data channel:', messageEvent.data);
    };

    event.channel.onopen = () => {
        console.log('Data channel is open.');
    };

    event.channel.onclose = () => {
        console.log('Data channel is closed.');
    };
};

const allStreams = [];
connection.ontrack = (event) => {
    allStreams.push(...Array.from(event.streams));
    console.log(event.streams[0]);
    document.getElementById('remoteVideo').srcObject = event.streams[0];
};

await connection.setRemoteDescription(new RTCSessionDescription(data.offer));

data.candidates.forEach(async candidate => {await connection.addIceCandidate(candidate); console.log('Successfully added ICE candidate', candidate)});

const candidates = [];
connection.onicecandidate = (event) => {
    console.log('New Candidate:', event.candidate);
    if (event.candidate) {
        candidates.push(event.candidate);
    } else {
        console.log(`const data=${JSON.stringify({answer: answer, candidates: candidates})};`);
    }
};

const answer = await connection.createAnswer();
await connection.setLocalDescription(answer);

then i load the resulting variable to first client to load the data and run


connection.setRemoteDescription(new RTCSessionDescription(data.answer));
data.candidates.forEach(async candidate => {await connection.addIceCandidate(candidate); console.log('Successfully added ICE candidate', candidate)});

but after i do all these steps it doesnt proceeds to the checking step, how can i fix it

Node backend not sending proper status code

I am trying to send status code 401 for an unauthorized user but my node backend only sends a 500 error.

auth.controller.ts

async authUser(req: Request, res: Response) {
    try {
        //assigns the username and password to the authUser variable
        const authUser = await req.body as UserCredentials;
        //checks to see if the email is present that it contains an email over 0 characters
        const accessIsValid: boolean = authServices.validateAccess(
            authUser.user_email,
        );
        //if the accessISValid returns false, there is no email. and the functions is exited
        if (!accessIsValid) {
           throw new HttpError("Invalid Email", 409)
        };
        const result: ValidatedUser = await authServices.validateUser(authUser);
        if (result.userIsValid) {
            const token: string = jwt.sign({
                alg: "****",
                typ: "JWT"
            }, config.get<string>('*****'), {
                expiresIn:*****,
                jwtid: *****
            });
            res.status(200);
            res.send({
                token: token,
                success: true,
                user: result,
            });
        } else {
            throw new HttpError("Invalid Password", 401)
        }
    } catch (err: any) {
        logger.error(err)
        res.status(err.statusCode).json({ error: err.status, message: err.message })
    }
}

I am also using a custom error class

customError.ts

  export class HttpError extends Error {
    statusCode: number;
    status: string;
    isOperational: boolean;
    constructor(message: string, statusCode: number) {
        super(message);
        this.statusCode = statusCode;
        this.status = statusCode >= 400 && statusCode < 500 ? 'fail' : 'error';
        this.isOperational = true;

        Error.captureStackTrace(this, this.constructor);
    }
}

The code runs perfectly if you put the right password in, but it won’t send anything other then status 500 to the client. When I log the error this is what I get.

  err: {
      "type": "HttpError",
      "message": "Invalid Password",
      "stack":
          Error: Invalid Password
              at C:UsersjwatsonDocumentsGitHubEID-WebsiteEID-Website-API-V2srccontrollersauth.controller.ts:45:23
              at Generator.next (<anonymous>)
              at fulfilled (C:UsersjwatsonDocumentsGitHubEID-WebsiteEID-Website-API-V2srccontrollersauth.controller.ts:5:58)
              at processTicksAndRejections (node:internal/process/task_queues:96:5)
      "statusCode": 401,
      "status": "fail",
      "isOperational": true
    }

So I know it’s producing the correct code and message. I have tried with and without a try catch block. Any help would be greatly appreciated.

Configured webpack with scss module gives error Module Build Failed expected {

I am trying to use scss modules in my React app but getting error.

I configured my react app using webpack, now when I went on to configure scss modules, i got this error. The webpack build is failing, but when I remove the module.scss rule it works fine with only the .scss rule

ERROR in ./src/components/Button/Button.module.scss (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/sass-loader/dist/cjs.js!./node_modules/style-loader/dist/cjs.js!./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/components/Button/Button.module.scss)
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
expected "{".
  ╷
2 │       import API from "!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"; 

Below is my webpack.config.js file:

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.tsx",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js",
    clean: true,
  },

  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },

  module: {
    rules: [
      {
        test: /.(ts|tsx)$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /.module.scss$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: true,
              sourceMap: true,
            },
          },
          {
            loader: "sass-loader",
          }
        ],
        exclude: "/node_modules/",
      },
      {
        test: /.scss$/,
        exclude: "/.module.scss$/",
        use: ["style-loader", "css-loader", "sass-loader"],
      },
      {
        test: /.(js|jsx)$/,
        use: { loader: "babel-loader" },
        exclude: /node_modules/,
      },
      {
        test: /.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
  ],

};

Do I need to refresh the refresh token when using JWTs?

I’m using JWTs for authentication with an accessToken and a refreshToken.

  • The accessToken is short-lived (e.g., 15 minutes).
  • The refreshToken is long-lived (e.g., 7 days).

When the client uses the /auth/refresh endpoint to get a new accessToken, should I also issue a new refreshToken?

If I do:

  • What’s the best way to handle invalidating the old refreshToken?
  • Are there security benefits to refreshing the refreshToken along with the accessToken?

I want to ensure the implementation is secure and follows best practices.

Any advice or recommendations are greatly appreciated!

I haven’t tried refreshing the refreshToken yet, as I’m unsure if it’s necessary. I want to understand whether it’s a best practice to refresh the refreshToken along with the accessToken or if the same refreshToken should be used until it expires.

Pikaday datepicker internal state is not cleared when dob field is cleared. It is not removing the required attribute for other fields when cleared

I am developing an html form for sports event where the required attributes for name, guardian, dob, school and class classes are added or removed dynamically when one field is non empty or every field is empty. After using pikaday datepicker for dob field when i clear the dob field other field’s required attribes are not removed dynamically. Please fix this if you can.Here is my javascript code-

document.addEventListener("DOMContentLoaded", function () {
  // Initialize Pikaday datepicker for each form
  const formConfigs = {
    form1: {
      minDate: new Date(2018, 0, 1),
      apiUrl:
        "https://script.google.com/macros/s/AKfycbymk9DbjsFZbWz_uuDACLcnkHA7eji1U3Q8K4cLscqvTZ2Wb9EPBvAN30Hmcq92XYAH/exec",
      redirectUrl: "./group2.html",
    },
    form2: {
      minDate: new Date(2016, 0, 1),
      apiUrl:
        "https://script.google.com/macros/s/AKfycbxJZbH7G9iSPj3Pq-lGMTZoywoTIH5CN7dsRECmb2VvYsxNsb5oIQ2lM7qKnpKYbd5Q/exec",
      redirectUrl: "./group3.html",
    },
    form3: { minDate: new Date(2014, 0, 1), apiUrl: "", redirectUrl: "" },
  };

  Object.keys(formConfigs).forEach((formId) => {
    const form = document.getElementById(formId);
    if (form) {
      const minDate = formConfigs[formId].minDate;
      const dobFields = form.querySelectorAll(".dob");

      dobFields.forEach((field) => {
        const picker = new Pikaday({
          field: field,
          format: "DD/MM/YYYY",
          toString: (date, format) => {
            const day = date.getDate().toString().padStart(2, "0");
            const month = (date.getMonth() + 1).toString().padStart(2, "0");
            const year = date.getFullYear();
            return `${day}/${month}/${year}`;
          },
          parse: (dateString, format) => {
            const parts = dateString.split("/");
            const day = parseInt(parts[0], 10);
            const month = parseInt(parts[1], 10) - 1;
            const year = parseInt(parts[2], 10);
            return new Date(year, month, day);
          },
          yearRange: [1900, 2100],
          minDate: minDate,
          showDaysInNextAndPreviousMonths: true,
          enableSelectionDaysInNextAndPreviousMonths: true,
          onSelect: function () {
            console.log("Date selected:", picker.getDate()); // Debug log
            updateRequiredAttributes(form);
          },
        });

        // Store the Pikaday instance in the field
        field._pikaday = picker;

        // Sync calendar with manually entered dates
        field.addEventListener("input", function (e) {
          const dateValue = e.target.value;
          const parsedDate = picker.parse(dateValue, "DD/MM/YYYY");
          console.log("Input value:", dateValue); // Debug log
          if (parsedDate && !isNaN(parsedDate)) {
            picker.setDate(parsedDate, true);
          } else if (!dateValue.trim()) {
            picker.setDate(null); // Clear the Pikaday value
            console.log("Pikaday state cleared."); // Debug log for confirmation
          }
          updateRequiredAttributes(form);
        });

        // Add forward slashes automatically
        field.addEventListener("input", function (e) {
          const input = e.target;
          let value = input.value.replace(/D/g, "");
          if (value.length > 2)
            value = `${value.slice(0, 2)}/${value.slice(2)}`;
          if (value.length > 5)
            value = `${value.slice(0, 5)}/${value.slice(5)}`;
          input.value = value;
          updateRequiredAttributes(form);
        });
      });

      // Form validation and submission logic
      const t = document.getElementById("msg");
      let r = false;

      function n(e, t) {
        i(e);
        const r = document.createElement("span");
        r.className = "error-message";
        r.style.color = "red";
        r.style.fontWeight = "bold";
        r.textContent = t;
        e.insertAdjacentElement("afterend", r);
      }

      function i(e) {
        const t = e.parentElement.querySelector(".error-message");
        t && t.remove();
      }

      function updateRequiredAttributes(form) {
        form.querySelectorAll(".event").forEach((event) => {
          const r = event.querySelector(".name"),
            n = event.querySelector(".guardian"),
            i = event.querySelector(".dob"),
            p = event.querySelector(".school"),
            o = event.querySelector(".class");

          if (
            r.value.trim() ||
            n.value.trim() ||
            i.value.trim() ||
            p.value.trim() ||
            o.value.trim()
          ) {
            r.setAttribute("required", "required");
            n.setAttribute("required", "required");
            i.setAttribute("required", "required");
            p.setAttribute("required", "required");
            o.setAttribute("required", "required");
          } else {
            r.removeAttribute("required");
            n.removeAttribute("required");
            i.removeAttribute("required");
            p.removeAttribute("required");
            o.removeAttribute("required");
          }
        });
      }

      form.addEventListener("input", function (e) {
        if (
          ["name", "guardian", "dob", "school", "class"].some((t) =>
            e.target.classList.contains(t)
          )
        ) {
          i(e.target);
          updateRequiredAttributes(form);
        }
      });

      form.addEventListener("submit", function (i) {
        i.preventDefault();
        let o = false,
          a = null;

        document.querySelectorAll(".event").forEach((e) => {
          const t = e.querySelector(".name"),
            r = e.querySelector(".guardian");
          t &&
            "" !== t.value.trim() &&
            t.value.trim().length < 3 &&
            (n(t, "Competitor's Name must be at least 3 characters long."),
            a || (a = t),
            (o = true)),
            r &&
              "" !== r.value.trim() &&
              r.value.trim().length < 3 &&
              (n(r, "Guardian's Name must be at least 3 characters long."),
              a || (a = r),
              (o = true));
        });

        if ((a && a.focus(), o || r)) return;

        r = true;
        let l = document.getElementById("loading-animation");
        if (!l) {
          l = document.createElement("div");
          l.id = "loading-animation";
          l.innerHTML = '<div class="spinner"></div><p>Submitting...</p>';
          document.body.appendChild(l);
        }

        const u = document.querySelector("button[type='submit']");
        u.disabled = true;
        u.innerText = "Submitting...";

        const s = new FormData(form);
        for (let d of s.entries()) console.log(d[0] + ": " + d[1]);

        fetch(formConfigs[formId].apiUrl, { method: "POST", body: s })
          .then((e) => {
            if (!e.ok) throw Error("Network response was not ok");
            return e.json();
          })
          .then((r) => {
            console.log("Success:", r);
            document.body.removeChild(l);
            t.innerHTML = "Form Submitted Successfully";
            t.style.display = "block";

            setTimeout(() => {
              t.innerHTML = "Redirecting...";
              setTimeout(() => {
                window.location.href = formConfigs[formId].redirectUrl;
              }, 3000);
            }, 2000);

            form.reset();
            u.innerText = "Submit";
          })
          .catch((e) => {
            console.error("Error!", e.message);
            document.body.removeChild(l);
            t.innerHTML = "Error occurred! Please try again.";
            t.style.display = "block";
            setTimeout(() => {
              t.innerHTML = "";
              t.style.display = "none";
            }, 5000);
            u.disabled = false;
            u.innerText = "Submit";
            r = false;
          });
      });

      const clearButton = document.getElementById("clearButton");

      function clearForm() {
        form.reset();
        form
          .querySelectorAll(".name, .guardian, .dob, .school, .class")
          .forEach((e) => {
            e.removeAttribute("required");
          });

        form.querySelectorAll(".dob").forEach((field) => {
          const picker = field._pikaday;
          if (picker) {
            picker.setDate(null); // Reset Pikaday's internal date
            console.log("Pikaday instance reset for field:", field); // Debug log
          }
        });
      }

      clearButton &&
        clearButton.addEventListener("click", function (e) {
          e.preventDefault();
          confirm("Do you want to clear the form?") && clearForm();
        });
    }
  });
});

Timestamp a webpage using javascript [closed]

I need a webpage to show the last time it was refreshed using javascript.
I have found this code online:

    <script>
  // create a function to update the date and time
  function updateDateTime() {
    // create a new `Date` object
    const now = new Date();

    // get the current date and time as a string
    const currentDateTime = now.toLocaleString();

    // update the `textContent` property of the `span` element with the `id` of `datetime`
    document.querySelector('#datetime').textContent = currentDateTime;
  }

  // call the `updateDateTime` function every second
  setInterval(updateDateTime, 1000);
</script>

on the page it uses

<span id="datetime"></span>

to display it.
But
This script just seems to show all the time (1 second intervals).
I would need to show as
21/02/2025 , 16:30:35
then next
21/02/2025, 16:31:05
etc.
The webpage is set to refresh 10 second intervals for testing
<meta http-equiv="refresh" content="10">

Thanks in advance,

How to avoid Stripe confirmParams in stripe.confirmPayment() to handle payment status programmatically

I’m implementing Stripe Payments and running into an issue where I can’t handle the payment completion flow programmatically. I want to redirect users based on the PaymentIntent status, but Stripe keeps requiring a return_url and forcing redirects.

Current Backend snippet:

const paymentIntent = await stripe.paymentIntents.create({
    amount,
    currency: 'gbp',
    metadata: {
        orderId,
        customerEmail
    }
    // Even without specifying automatic_payment_methods,
    // it seems to be enabled by default
});

Current Frontend snippet:

const handlePayment = async () => {
    if (!stripe || !elements || !clientSecret) return;
    
    try {
        const { error: submitError } = await elements.submit();
        if (submitError) throw new Error(submitError.message);
        
        const { error, paymentIntent } = await stripe.confirmPayment({
            elements,
            clientSecret,
            // If I remove confirmParams.return_url, I get an error
            confirmParams: {
                return_url: `http://localhost:5173/order/confirmation/${orderNumber}/${confirmationToken}`,
            },
        });

        // I want to handle the status here and redirect programmatically
        if (paymentIntent.status === 'succeeded') {
            // My custom redirect logic
        }
    } catch (error) {
        console.error(error);
    }
}

The Problem:

  • When I try to remove confirmParams.return_url, I get the error: stripe.confirmPayment(): the confirmParams.return_url argument is required when using automatic payment methods
  • I’ve tried disabling automatic payment methods in the backend and explicitly specifying payment methods like google_pay, apple_pay etc., but error shows me list of payment methods I can add and they don’t mention these methods.
  • If I add only ['card'] as payment methods, it would allow me custom redirect logic but adding more methods would not allow unless I use automatic_payment_method.

What I’ve Tried:

  • Setting automatic_payment_methods: { enabled: false } in the backend
  • Explicitly specifying payment_method_types: [‘card’] instead of using automatic payments
  • Adding redirect: ‘if_required’ to the confirmPayment options
    Setting return_url to the current page URL

None of these approaches have worked – Stripe still enforces the redirect behavior.

Environment:

  • Stripe.js version: latest
  • Frontend: React
  • Backend: Node.js
  • Using Elements and Payment Intents API

How to avoid Stripe confirmParams in confirmPayment to handle payment status programmatically

I’m implementing Stripe Payments and running into an issue where I can’t handle the payment completion flow programmatically. I want to redirect users based on the PaymentIntent status, but Stripe keeps requiring a return_url and forcing redirects.

Current Backend snippet:

const paymentIntent = await stripe.paymentIntents.create({
    amount,
    currency: 'gbp',
    metadata: {
        orderId,
        customerEmail
    }
    // Even without specifying automatic_payment_methods,
    // it seems to be enabled by default
});

Current Frontend snippet:

const handlePayment = async () => {
    if (!stripe || !elements || !clientSecret) return;
    
    try {
        const { error: submitError } = await elements.submit();
        if (submitError) throw new Error(submitError.message);
        
        const { error, paymentIntent } = await stripe.confirmPayment({
            elements,
            clientSecret,
            // If I remove confirmParams.return_url, I get an error
            confirmParams: {
                return_url: `http://localhost:5173/order/confirmation/${orderNumber}/${confirmationToken}`,
            },
        });

        // I want to handle the status here and redirect programmatically
        if (paymentIntent.status === 'succeeded') {
            // My custom redirect logic
        }
    } catch (error) {
        console.error(error);
    }
}

The Problem:

  • When I try to remove confirmParams.return_url, I get the error: stripe.confirmPayment(): the confirmParams.return_url argument is required when using automatic payment methods
  • I’ve tried disabling automatic payment methods in the backend and explicitly specifying payment methods like google_pay, apple_pay etc., but error shows me list of payment methods I can add and they don’t mention these methods.
  • If I add only ['card'] as payment methods, it would allow me custom redirect logic but adding more methods would not allow unless I use automatic_payment_method.

What I’ve Tried:

  • Setting automatic_payment_methods: { enabled: false } in the backend
  • Explicitly specifying payment_method_types: [‘card’] instead of using automatic payments
  • Adding redirect: ‘if_required’ to the confirmPayment options
    Setting return_url to the current page URL

None of these approaches have worked – Stripe still enforces the redirect behavior.

Environment:

  • Stripe.js version: latest
  • Frontend: React
  • Backend: Node.js
  • Using Elements and Payment Intents API

how to handle a blank value with excel script converting CSV to worksheet

I have a flow, that runs a script, that converts a CSV document to an excel document for use elsewhere. The CSV often has a comment field that is blank ex) john,doe,8,,SC the blank value would be the comment field. However when the script runs it will just write the SC into the comments field shifting all columns over one. I need this field to either return null or blank and not shift the columns.
Here is my current code :

function main(workbook: ExcelScript.Workbook, lineCSV: string[]) {
    let selectedSheet = workbook.getActiveWorksheet();
    const alllines = lineCSV;
    let counter = 1;

    for (let line of alllines) {
        if (line.includes(",")) {
            // Split line by commas, ignoring commas within quotes
            let items = line.match(/(".*?"|[^",]+)(?=s*,|s*$)/g);

            // Replace empty items with "empty"
            if (items) {
                for (let i = 0; i < items.length; i++) {
                    if (items[i] === "") {
                        items[i] = "null";
                    }
                }
            } else {
                items = [];
            }

            // Ensure the items array has exactly 25 elements (A to Y)
            while (items.length < 25) {
                items.push("null");
            }

            // Create a new array to ensure each column is correctly mapped
            let rowValues: string[] = new Array(25).fill("null");
            for (let i = 0; i < items.length; i++) {
                rowValues[i] = items[i];
            }

            // Set range dynamically based on the number of items
            selectedSheet.getRange("A" + counter + ":Y" + counter).setValues([rowValues]);
            counter++;
        }
    }

    // Add a new table at range A1:Y based on the number of rows processed
    workbook.addTable(selectedSheet.getRange("A1:Y" + counter), true).setName("Data");
}

I’ve been trying things for hours with no luck. any insight would be great! thanks!

expected the values to stay in the expected columns and not shift over since one value is blank

sending form Data using formData and fetch api and getting server internal error

Am trying to send data from my form one of the fields is input field of type file that selects an image, then am using formData to get the data of all these inputs finally am using fetch api to send the data to the server and am getting error 500 internal server, am trying to make sure that my frontend code is valid and if there is a problem with the backend

// Function to send form data containing the ad image through the API
const adForm = document.querySelector('#adForm'); // The form

adForm.addEventListener('submit', (event) => {
  const fileImgInput = document.querySelector('#adImage').files[0];
  const adNameField = document.querySelector('#ad-name').value;
  const startDate = document.querySelector('#startDate').value;
  const endDate = document.querySelector('#endDate').value;
  const switchBtn = document.querySelector('#flexSwitchCheckDefault').checked;
  const token = localStorage.getItem('token');
  

  // Sending the photo and other data using formData API within the fetch API
  //Prepare form data
    var formData = new FormData();
    formData.append("url", `${fileImgInput}`);
    formData.append("name", `${adNameField}`);
    formData.append("start_date", `${startDate}`);
    formData.append("end_date", `${endDate}`);
    formData.append("show", `${switchBtn}`);  
    console.log(fileImgInput);
    

  fetch('http://127.0.0.1:8000/api/ads', {
    method: 'POST',  
    body: formData
  })
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => {
    // Handle successful login, e.g., store token in local storage
    console.log('Register successful:', data.data);
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });

  event.preventDefault(); 
})

it is my first time trying to send image to the backend so please any help is appreciated Thank you.

FormData fetch API sending data to the server

Vuetify3 autocomplete with per-item tooltips not clickable

Given Vue3 + Vuetify3 code:

<template>
  <v-autocomplete :items="items" :value="selectedItem">
    <template v-slot:item="data">
      <v-tooltip>
        <template v-slot:activator="{ props }">
          <v-list-item v-bind="props">
            {{ data.item.title }}
          </v-list-item>
        </template>
        <span>{{ data.item.raw.tooltip }}</span>
      </v-tooltip>
    </template>
  </v-autocomplete>
</template>
<script setup lang="ts">
import { ref } from "vue";

const selectedItem = ref(null);
const items = [
  { title: "Option 1", tooltip: "This is the first option" },
  { title: "Option 2", tooltip: "This is the second option" },
  { title: "Option 3", tooltip: "This is the third option" },
];
</script>

I’d like to have a unique tooltip for every item in my autocomplete model, but with the current code none of the items in the dropdown are clickable while the tooltips render correctly.

React useEffect proper cleanup when adding event listeners

This is simplified example. The real code works with event listeners, specifically web socket message listeners.

function App() {
  const [count, setCount] = useState(0);
  const [subscribed, setSubscribed] = useState<boolean>();

  useEffect(() => {
    if (!subscribed) {
      console.log('Subscribing');
      setSubscribed(true);
    }

    return () => {
      if (subscribed) {
        console.log('Unsubscribing');
        setSubscribed(false);
      }
    };
  }, [subscribed]);

  useEffect(() => {
    if (subscribed !== undefined) {
      setCount((count) => (subscribed ? count + 1 : count - 1));
    }
  }, [subscribed]);

  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">count is {count}</div>
    </>
  );
}

The above code (Stackblitz Demo), has an effect that is logging in the console actions (that’s simplification, again the real code adds an event listener). And it’s setting a state whether it’s already subscribed or not. I have what I think is proper cleanup there for the effect, but in strict mode, I see 2 subscriptions (and I see similar thing in real code) and I don’t see the cleanup in condition (I’m sure the cleanup is still running, but for the initial strict mode mount and unmount the condition is never true). So I’m getting double subscription there. Also any development change, that is triggering Vite HMR is adding one more additional subscription without cleanup again.

P.S. I have also state counter there to detect subscription, but it shows correct for initial strict mode double mount (which I don’t understand, as logs tell different story), but HMR still increments the counter, while I was thinking that unmounting will also decrement the counter.
P.P.S. The real code depends not on the state, but on state management library to store subscription state and initiate the subscription to use it in different components and still have only 1 active subscription. So this is simplification which shows the same results which I see in real code with state management either.

Can anyone please explain me what’s going on, and how to handle this correctly. (Of course, without disabling strict mode).

Why is ECONNREFUSED error occurring when connecting the frontend and backend during Vite app startup?

Background:

I’m using Vite to run a React frontend and an Express backend on different ports. The frontend uses Vite’s proxy settings to send API requests to the backend. My environment includes Node.js, Express, MongoDB, TypeScript, and React.

I use the following commands to run my application:

concurrently "vite" "ts-node-dev --respawn --transpile-only server/server.ts"

Problem:

During the app startup, I’m encountering an ECONNREFUSED error.

[0] 22:44:22 [vite] http proxy error: /api
[0] AggregateError [ECONNREFUSED]:
[0]     at internalConnectMultiple (node:net:1122:18)
[0]     at afterConnectMultiple (node:net:1689:7)

After investigation, I found that the error is related to the chunks key in the _metadata.json file.

Attempts:

  1. Restarted the app multiple times without changing any source code; sometimes the error occurred, and sometimes it did not.
  2. Reinstalled dependencies and updated all packages, including Vite, to the latest versions, but the issue persisted.
  3. Deleted the entire .vite folder before starting the app, which temporarily resolved the issue.
  4. Further investigation within the .vite folder led to identifying that deleting the chunks and optimized keys in _metadata.json consistently prevented the error from occurring.

Seeking:

I wanted to share my findings with the community and see if anyone else has encountered this issue or has insights into the specific cause of this problem.