No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Vue3 with VIte + ExpressJS

I have a problem, I’m developing a web page which is made up of a server and a client.
Server: It’s a restful API with expressjs. I have a route there
//* @route GET api/auth/
Which helps me log in to Google, from Postman it’s possible, but the problem is when I access it with Axios from my client (Vue) and it shows me the error:
Access to XMLHttpRequest at ‘https://accounts.google.com/o/oauth2/v2/auth?…..’
(redirected from ‘http://localhost:5173/api/auth/’) from origin ‘http://localhost:5173’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
VM554:1 Login with Google failed
AxiosError {message: ‘Network Error’, name: ‘AxiosError’, code: ‘ERR_NETWORK’, config: {…}, request: XMLHttpRequest, …}

In my index.js (from server)

const cors = require(‘cors’)
const corsOptions ={
origin:’*’,
credentials:true,
optionSuccessStatus:200,
}

app.use(cors(corsOptions))

An idea to solve my problem

Frame player with s3 images

I’m building a React application that needs to render multiple frame players simultaneously (up to 25 players). Each player displays a sequence of images (10-100 frames) stored in S3, playing at 2 frames per second.

Current Implementation:

  • Multiple frame players in a grid layout
  • Each player loads and plays image sequences from S3
  • Implemented preloading strategy for better performance
  • Target framerate: 2 FPS

Technical Specs:

  • React (latest version)
  • Images hosted on S3
  • Each frame player is independent
  • Number of players: up to 25
  • Frames per player: 10-100

The Issue:

When rendering multiple players with high frame counts simultaneously, some frame player gets stuck. The network tab shows numerous cancelled requests.

Current Frame Player Implementation:

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { formatTime } from '../utils';
export interface FrameData {
  id: string;
  frameUrl: string;
  createdTime: number;
}
interface Props {
  frames: Array<FrameData>;
  currentFrameIndex: number;
  fps: number;
  timeZone: string;
  loop?: boolean;
  onComplete?: () => void;
  width?: number | string;
  height?: number | string;
  className?: string;
  onFrameChange: (idx: number) => void;
  elaborateFrames?: boolean;
  preloadCount?: number;
}

interface FrameStatus {
  loaded: boolean;
  error: boolean;
}

export function FramePlayer({
  frames,
  currentFrameIndex,
  timeZone,
  fps,
  loop = true,
  onComplete,
  width = '100%',
  height = '100%',
  className = '',
  onFrameChange,
  elaborateFrames,
  preloadCount = 8,
}: Props) {
  const [isPlaying, setIsPlaying] = useState(true);
  const frameStatusRef = useRef<Record<string, FrameStatus>>({});
  const requestRef = useRef<number>();
  const previousTimeRef = useRef<number>();
  const preloadingRef = useRef<Set<string>>(new Set());
  const frameDuration = 1000 / fps;

  // Preload frames around current index
  useEffect(() => {
    const preloadFrames = async () => {
      const startIdx = Math.max(0, currentFrameIndex);
      const endIdx = Math.min(frames.length, currentFrameIndex + preloadCount);
      // const frameStatus = frameStatusRef.current;

      for (let i = startIdx; i < endIdx; i++) {
        const frame = frames[i];
        const frameKey = frame.frameUrl;

        // Skip if already loaded or currently preloading
        if (
          frameStatusRef.current[frameKey]?.loaded ||
          // frameStatus[frameKey]?.error ||
          preloadingRef.current.has(frameKey)
        ) {
          continue;
        }

        preloadingRef.current.add(frameKey);

        const img = new Image();
        img.src = frame.frameUrl;

        img.onload = () => {
          frameStatusRef.current = {
            ...frameStatusRef.current,
            [frameKey]: { loaded: true, error: false },
          };
          preloadingRef.current.delete(frameKey);
        };

        img.onerror = () => {
          frameStatusRef.current = {
            ...frameStatusRef.current,
            [frameKey]: { loaded: false, error: true },
          };
          preloadingRef.current.delete(frameKey);
        };
      }
    };

    preloadFrames();
  }, [currentFrameIndex, frames, preloadCount]);

  // Check if current frame is loaded before advancing
  const shouldAdvanceFrame = useCallback(() => {
    const frameStatus = frameStatusRef.current;
    const currentFrame = frames[currentFrameIndex];
    return currentFrame ? frameStatus[currentFrame.frameUrl]?.loaded : false;
  }, [currentFrameIndex, frames]);

  const animate = useCallback(
    (time: number) => {
      if (previousTimeRef.current === undefined) {
        previousTimeRef.current = time;
        requestRef.current = requestAnimationFrame(animate);
        return;
      }

      const deltaTime = time - previousTimeRef.current;

      if (deltaTime >= frameDuration && shouldAdvanceFrame()) {
        let nextFrame = currentFrameIndex + 1;
        if (nextFrame >= frames.length) {
          if (loop) {
            nextFrame = 0;
          } else {
            setIsPlaying(false);
            onComplete?.();
            nextFrame = currentFrameIndex;
          }
        }
        onFrameChange(nextFrame);
        previousTimeRef.current = time;
      }
      requestRef.current = requestAnimationFrame(animate);
    },
    [
      currentFrameIndex,
      frameDuration,
      frames,
      loop,
      onComplete,
      onFrameChange,
      shouldAdvanceFrame,
    ]
  );

  useEffect(() => {
    if (isPlaying) {
      requestRef.current = requestAnimationFrame(animate);
    } else if (requestRef.current) {
      cancelAnimationFrame(requestRef.current);
      requestRef.current = undefined;
      previousTimeRef.current = undefined;
    }

    return () => {
      if (requestRef.current) {
        cancelAnimationFrame(requestRef.current);
      }
    };
  }, [isPlaying, animate]);

  const frame = useMemo(
    () => (frames.length > 0 ? frames[currentFrameIndex] : undefined),
    [currentFrameIndex, frames]
  );

  const handleImageLoad = (frameKey: string) => () => {
    if (!frameStatusRef.current[frameKey]?.loaded) {
      frameStatusRef.current = {
        ...frameStatusRef.current,
        [frameKey]: { loaded: true, error: false },
      };
    }
  };

  return (
    <div
      className={`frame-player relative flex items-center justify-center ${className}`}
      style={{ width, height }}
    >
      {frame ? (
        <div className="h-full">
          {elaborateFrames && (
            <span className="bg-ondark-bg-2 text-ondark-text-1 absolute top-0 right-0 rounded text-xs">
              {formatTime(new Date(frame.createdTime), timeZone, true)}
            </span>
          )}
          <img
            src={frame.frameUrl}
            alt={`Frame ${currentFrameIndex + 1}`}
            style={{ objectFit: 'contain', height: '100%' }}
            onLoad={handleImageLoad(frame.frameUrl)}
          />
        </div>
      ) : (
        <p className="text-ondark-text-1 absolute inset-0 flex animate-pulse items-center justify-center text-xs">
          Events loading...
        </p>
      )}
    </div>
  );
}

export default FramePlayer;

Questions:

What’s the best approach to handle multiple image sequence players efficiently?

How can I optimize the preloading strategy to prevent request cancellations?

Are there better alternatives to manage multiple simultaneous intervals?

Any suggestions for performance optimization or alternative approaches would be greatly appreciated.

Attached network screenshot Network tab screenshot

Angular insist I add standalone = true, despite using Angular 19

My angular config:

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, JsonPipe, HeadComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {

bootstrapApplication(AppComponent, appConfig)
  .catch((err) => console.error(err));

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes)]
};

But my ide, intellij insists that all components be marked with standalone = true?

Why is that?

enter image description here

Cannot Connect banking Via Plaid TomorrowIdeas

All I’m getting is an error from plaid

Something went wrong, Interal error occurred.

I do not know what to do and or how to fix it.

Error

Here is the what I have tried on the front end with javascript.

<button id="link-button">Link Bank Account</button>
<script type="text/javascript" src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
<script type="text/javascript">
    var linkHandler = Plaid.create({
        clientName: '<?php echo $case->name; ?>',
        env: '<?php echo getenv("PLAID_ENVIRONMENT"); ?>', // or 'development' or 'production' based on your environment
        key: '<?php echo getenv("PLAID_CLIENT_ID"); ?>', // Replace with your Plaid public key
        product: ['balance'], // or other products like 'transactions', 'balance', etc.
        onSuccess: function(public_token, metadata) {
            console.log('Plaid Link success!');
            console.log(public_token);
            console.log(metadata);
            // This is where you handle the success
            // Send the public_token to your backend to exchange it for an access_token
            fetch('/admin/api/plaid.php', {
                    method: 'POST',
                    body: JSON.stringify({
                        public_token: public_token
                    }),
                    headers: {
                        'Content-Type': 'application/json'
                    }
                })
                .then(response => response.json())
                .then(data => {
                    console.log('Access token received:', data.access_token);
                });
        },
        onExit: function(err, metadata) {
            console.log('Plaid Link exited');
            console.log(err, metadata);
        }
    });

    document.getElementById('link-button').onclick = function() {
        console.log('Button clicked, opening Plaid Link...');
        linkHandler.open();
    };
</script>

Here is the file that is tryng to fetch plaid.php
I am not sure if this has anything to do with the error or not.

I wasn’t able to find much documatation for plaid and PHP.
When I called plaid they were very not helpful.

require 'vendor/autoload.php';

use TomorrowIdeasPlaidPlaid;

$plaid = new Plaid(
    getenv("PLAID_CLIENT_ID"),
    getenv("PLAID_CLIENT_SECRET"),
    getenv("PLAID_ENVIRONMENT")
);

// Get the public_token from the request body
$data = json_decode(file_get_contents('php://input'), true);
$public_token = $data['public_token'];

try {
    $response = $plaid->items->get("access_token");
    print_r($response);
} catch (TomorrowIdeasPlaidPlaidRequestException $e) {
    echo "Error: " . $e->getMessage() . "n";
    var_dump($e); // Inspect the exception object for available properties
}
$item = $plaid->items->get("itm_1234");
exit();

Why the intercom widget disappear from the page?

I did a simple HTML page that includes the intercom widget, but when I open the page the widget disappears in less than a second. Does anyone know how to fix this or why it happens? The code I’m using:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Intercom Widget Example</title>
</head>
<body>
  <h1>Welcome to My Website</h1>
  <p>This is a simple webpage with an Intercom widget integrated.</p>

  <!-- Intercom Widget Code -->
  <script>
  window.intercomSettings = {
    api_base: "https://api-iam.intercom.io",
    app_id: "XXXXXXXX",
  };
</script>


<script>
'https://widget.intercom.io/widget/xxxxxxxx'
  (function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/xxxxxxxx';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
</script>
</body>
</html>

PrimeVue v-tooltip – Need help understanding error

I’m testing out PrimeVue. Created a simple timestamp template that is being imported and displayed in the main App.vue template. See the following code. timestamp and local_timestamp show up correctly in the template. However, the tooltip is not working. If I use a string instead of the computed property, it shows up fine. The console gives the following error: main.js:29 TypeError: r.value.value.trim is not a function

That is referencing the following line of code from PrimeVue’s Tooltip component.
https://github.com/primefaces/primevue/blob/master/packages/primevue/src/tooltip/Tooltip.js#L25

Any idea what I’m missing here?

<template>
  <div v-tooltip="{ value: local_timestamp }">
    {{ timestamp }}
  </div>
  {{ local_timestamp }}
</template>

<script setup>
import { computed, defineProps } from 'vue'

const props = defineProps({
  timestamp: {
    type: String,
    required: true
  }
})

const local_timestamp = computed(() => {
  return new Date(props.timestamp)
})
</script>

I’ve been referencing the PrimeVue documentation here: https://primevue.org/tooltip/

Firefox clipboard: test whether the content is same-origin before attempting read() or readText()?

A web app allows copying and pasting objects between dialogs. It stores JSON in the clipboard and reads it via JavaScript when Ctrl+V is pressed.

Pasting works fine in Chrome and Firefox when the clipboard actually contains the expected, same-origin JSON. But when the clipboard contains cross-origin content from different application, Firefox pops up a “Paste” dialog the breaks keyboard focus and is generally annoying.

The paste dialog is a security measure according to https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/read#browser_compatibility:

A paste prompt is displayed when the clipboard is read. If the
clipboard contains same-origin content, the prompt is suppressed.

Is it possible to know the clipboard origin before attempting an operator that will trigger the dialog? The web app doesn’t need to read data from a different origin. I’d like to avoid the annoying dialog.

Reset cursor/slider position after each image transition in before and after slider gallery

In the gallery the slider is positioned on the far right side and set to follow the cursor, the after image is reveled as mouse is moved to the left, the slider does reset to the far right after every image transition but the problem is that cursor doesn’t so when it is moved the slider jumps to the position the cursor is, partly or fully reveling the after image

I created the code for pointer locker as proof of concept that worked even with transitioning via clicks on chrome, when clicked on site the pointer locker is activated and red ball becomes the custom cursor and when clicked both the background color is changed and cursor position is reset to far right, background color transition could be image transition and slider could follow the custom cursor, ideally simpler solution not requiring pointer locker would be nice, but if not then aid with integrating pointer locker solution with gallery code would be appreciated

Pointer locker

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pointer Lock Example</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: pink;
            height: 100vh;
        }

        #dot {
            position: absolute;
            width: 20px;
            height: 20px;
            background-color: red;
            border-radius: 50%;
            pointer-events: none;
            z-index: 999;
            opacity: 0.8;
            visibility: hidden; /* Hidden before pointer lock */
        }

        #message {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            font-family: Arial, sans-serif;
            font-size: 16px;
            color: #333;
        }
    </style>
</head>
<body>

<div id="dot"></div>
<div id="message">Click anywhere to activate Pointer Lock. Press 'Q' to reset the dot position.</div>

<script>
    var dot = document.getElementById("dot");
    var x = window.innerWidth / 2;  // Start at the center horizontally
    var y = window.innerHeight / 2; // Start at the center vertically
    var pointerLocked = false;

    // Position the dot at the initial center of the screen
    dot.style.left = `${x}px`;
    dot.style.top = `${y}px`;

    // Function to toggle the background color and reset the dot to the far right
    function toggleBackgroundColor() {
        if (document.body.style.backgroundColor === "pink") {
            document.body.style.backgroundColor = "purple";
        } else {
            document.body.style.backgroundColor = "pink";
        }
        resetCursorToFarRight(); // Move the cursor to the far right on color change
    }

    // Function to reset the cursor to the far right
    function resetCursorToFarRight() {
        x = window.innerWidth - 20; // Set the x position to the far right
        dot.style.left = `${x}px`;
    }

    // Lock the pointer and toggle background color on click
    document.body.addEventListener("click", function () {
        toggleBackgroundColor();

        // If pointer is not locked, request pointer lock
        if (!pointerLocked) {
            document.body.requestPointerLock(); // Lock the pointer
        }
    });

    // Listen for pointer lock change to handle the cursor state
    document.addEventListener("pointerlockchange", function () {
        if (document.pointerLockElement === document.body) {
            pointerLocked = true; // Pointer is now locked
            dot.style.visibility = "visible"; // Show the controlled dot during pointer lock
            document.body.style.cursor = "none"; // Hide the system cursor
            document.getElementById('message').innerText = "Move your mouse to move the dot. Press 'Q' to reset. Press Esc to exit Pointer Lock.";
        } else {
            pointerLocked = false; // Pointer is unlocked
            dot.style.visibility = "hidden"; // Hide the dot (custom cursor) after pointer lock is exited
            document.body.style.cursor = "auto"; // Show the system cursor
            document.getElementById('message').innerText = "Click anywhere to activate Pointer Lock.";
        }
    });

    // Track mouse movement when pointer is locked
    document.addEventListener("mousemove", function (event) {
        if (document.pointerLockElement === document.body) {
            x += event.movementX; // Move horizontally
            y += event.movementY; // Move vertically, but this will be restricted later

            // Restrict the cursor horizontally
            if (x < 0) {
                x = 0; // Restrict movement at the left edge
            }
            if (x > window.innerWidth - 20) {
                x = window.innerWidth - 20; // Restrict movement at the right edge
            }

            // Keep the y position fixed (vertically locked)
            y = window.innerHeight / 2;

            dot.style.left = `${x}px`;
            dot.style.top = `${y}px`;
        }
    });

    // Listen for keyboard input to reset dot position to the far right (press 'Q')
    document.addEventListener("keydown", function (event) {
        if (document.pointerLockElement === document.body && event.key.toLowerCase() === 'q') {
            resetCursorToFarRight(); // Reset the dot position to the far right
        }
    });

    // Handle Escape key to exit pointer lock manually
    document.addEventListener('keydown', function (event) {
        if (event.key === 'Escape' && document.pointerLockElement === document.body) {
            document.exitPointerLock();
        }
    });

</script>

</body>
</html>

Gallery, switch to slider mode by pressing W. To recrate just create two folders named “1 before” and “1 after” put two jpg images in each folder named “1” and “2” put them into the same folder as html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dual Mode Gallery</title>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background: #000;
            font-family: sans-serif;
            color: #fff;
        }

        .fullscreen-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: none; /* Default to transparent cursor */
        }

        .image-container {
            position: relative;
            width: 100%;
            height: 100%;
        }

        .gallery-image {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: contain;
        }

        .before-image,
        .after-image {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: contain;
        }

        .after-image {
            clip-path: polygon(0 0, 0 100%, var(--clip-value) 100%, var(--clip-value) 0);
        }

        .slider {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 100%;
            transform: translateX(-100%);
            width: 10px;
            background: transparent;
            cursor: none; /* Transparent cursor */
        }

        .slider-gallery {
            display: none; /* Initially hide slider mode */
        }

        .quick-menu {
            position: fixed;
            top: 10px;
            right: 10px;
            background: rgba(0, 0, 0, 0.8);
            padding: 5px;
            border-radius: 5px;
            font-size: 12px;
            display: none; /* Initially hidden */
        }

        .quick-menu h4 {
            margin: 0;
            font-size: 14px;
            text-align: center;
        }

        .quick-menu ul {
            padding: 0;
            list-style: none;
            margin: 5px 0 0;
        }

        .quick-menu li {
            margin: 0;
        }

    </style>
</head>
<body>

<div class="fullscreen-container alternating-gallery">
    <div class="image-container">
        <img class="gallery-image" src="">
    </div>
</div>

<div class="fullscreen-container slider-gallery">
    <div class="image-container">
        <img class="before-image" src="1 before/1.jpg">
        <img class="after-image" src="1 after/1.jpg" style="--clip-value: 100%;">
        <div class="slider"></div>
    </div>
</div>

<div class="quick-menu" id="quickMenu">
    <h4>Menu</h4>
    <ul>
        <li>Left-click: Next</li>
        <li>Right-click: Previous</li>
        <li>Arrow keys: Navigate</li>
        <li>W: Toggle mode</li>
        <li>R: Toggle menu</li>
    </ul>
</div>

<script>
    let mode = 'Alternating'; // Default mode
    let currentImageIndex = 0;
    let menuVisible = false;

    const alternatingContainer = document.querySelector('.alternating-gallery');
    const sliderContainer = document.querySelector('.slider-gallery');
    const quickMenu = document.getElementById('quickMenu');
    const fullscreenContainers = document.querySelectorAll('.fullscreen-container');

    // --- Alternating Gallery Mode Variables ---
    const alternatingGalleryImage = document.querySelector('.gallery-image');
    const alternatingImages = [
        { folder: '1 before', name: '1.jpg' },
        { folder: '1 after', name: '1.jpg' },
        { folder: '1 before', name: '2.jpg' },
        { folder: '1 after', name: '2.jpg' }
    ];

    function loadAlternatingImage() {
        const image = alternatingImages[currentImageIndex];
        alternatingGalleryImage.src = `${image.folder}/${image.name}`;
    }

    function nextAlternatingImage() {
        currentImageIndex = (currentImageIndex + 1) % alternatingImages.length;
        loadAlternatingImage();
    }

    function prevAlternatingImage() {
        currentImageIndex = (currentImageIndex - 1 + alternatingImages.length) % alternatingImages.length;
        loadAlternatingImage();
    }

    // --- Slider Gallery Mode Variables ---
    const sliderBeforeImage = document.querySelector('.before-image');
    const sliderAfterImage = document.querySelector('.after-image');
    const slider = document.querySelector('.slider');
    const sliderImages = ['1.jpg', '2.jpg'];

    function updateSliderPosition(x) {
        slider.style.left = `${x}px`;
        sliderAfterImage.style.setProperty('--clip-value', `${x}px`);
    }

    function nextSliderImage() {
        currentImageIndex = (currentImageIndex + 1) % sliderImages.length;
        sliderBeforeImage.src = `1 before/${sliderImages[currentImageIndex]}`;
        sliderAfterImage.src = `1 after/${sliderImages[currentImageIndex]}`;
        updateSliderPosition(sliderContainer.offsetWidth); // Reset slider to far right
    }

    function prevSliderImage() {
        currentImageIndex = (currentImageIndex - 1 + sliderImages.length) % sliderImages.length;
        sliderBeforeImage.src = `1 before/${sliderImages[currentImageIndex]}`;
        sliderAfterImage.src = `1 after/${sliderImages[currentImageIndex]}`;
        updateSliderPosition(sliderContainer.offsetWidth); // Reset slider to far right
    }

    sliderContainer.addEventListener('mousemove', (e) =>
        updateSliderPosition(e.clientX - sliderContainer.offsetLeft)
    );

    sliderContainer.addEventListener('click', () => nextSliderImage());

    // --- Mode Switching ---
    function toggleMode() {
        if (mode === 'Alternating') {
            mode = 'Slider';
            alternatingContainer.style.display = 'none';
            sliderContainer.style.display = 'flex';
            currentImageIndex = 0; // Reset index
            nextSliderImage(); // Load the first slider image
        } else {
            mode = 'Alternating';
            sliderContainer.style.display = 'none';
            alternatingContainer.style.display = 'flex';
            currentImageIndex = 0; // Reset index
            loadAlternatingImage(); // Load the first alternating image
        }
        console.log(`Mode switched to: ${mode}`);
    }

    // --- Toggle Menu Visibility ---
    function toggleMenu() {
        menuVisible = !menuVisible;
        quickMenu.style.display = menuVisible ? 'block' : 'none';
        fullscreenContainers.forEach(container => {
            container.style.cursor = menuVisible ? 'default' : 'none';
        });
    }

    document.addEventListener('keydown', (e) => {
        if (e.key === 'w') {
            toggleMode();
        } else if (e.key === 'ArrowRight') {
            mode === 'Alternating' ? nextAlternatingImage() : nextSliderImage();
        } else if (e.key === 'ArrowLeft') {
            mode === 'Alternating' ? prevAlternatingImage() : prevSliderImage();
        } else if (e.key === 'r') {
            toggleMenu();
        }
    });

    alternatingContainer.addEventListener('click', nextAlternatingImage);
    alternatingContainer.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        prevAlternatingImage();
    });

    sliderContainer.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        prevSliderImage();
    });

    // Initialize default mode
    loadAlternatingImage();
</script>

</body>
</html>

How to download a file into a subfolder in the Downloads directory using Angular? [duplicate]

I’m working on an Angular application, and I have a method to download a file. I already have the blob, so I don’t need a server; everything is handled locally. However, I want the downloaded file to be saved in the Downloads directory but inside a specific subfolder. The name of this subfolder will be passed as a parameter.

I’ve tried several approaches, but none seem to work as expected. Here is my current code:

downloadPdf(blob: Blob, fileName: string, folderName: string): void {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${folderName}/${fileName}`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    window.URL.revokeObjectURL(url);
}

When I run this, the file downloads into the main Downloads folder, but it doesn’t create or save it inside the specified subfolder. Is there a way to achieve this in Angular or JavaScript?

What I’ve tried so far:

  • Adding the folderName as part of the download attribute, like in the code above.
  • Looking for options to define the download path in JavaScript, but I couldn’t find anything.

Notes:

  • The goal is to save the file locally, ideally without requiring additional server interaction.
  • If it’s not possible due to browser limitations, are there any workarounds?

Any help would be greatly appreciated!

White page, routing issues, and missing environment variables in browser

I’m working on a Next.js 14 project that works perfectly in development mode (next dev). However, after building the project (next build) and starting it (next start or as a standalone version with node server.js), several issues arise that I cannot resolve.

Setup:

  • Next.js 14
  • Standalone version started with node .next/standalone/server.js
  • Environment variables are passed at both build time and runtime, e.g.:
    NEXT_PUBLIC_API_URL=http://localhost:3000 HOSTNAME="0.0.0.0" node server.js
    
  • The project exists since Next.js 12 and my job was it to refactor the dependencies from 12 to 14. I had to add next-auth too, but it does not seem to be a problem here. As mentioned, it works in development mode.

Issues:

  1. White main page:

    • The main page (/de or /en) shows a completely white screen and the HTML is not served correctly (only <script/> tags seen in the inspector).
    • In the browser console, the following errors appear:
      • Specify NEXT_PUBLIC_API_URL in your .env file (even though the variable is set both at runtime and build time).
      • JavaScript files (.next/static) are served correctly, but the page doesn’t render.
  2. Routing issues:

    • When I visit /de/someinvalidroute, I get a redirect loop error:
      “The page isn’t redirecting properly.”
    • For example, the route /de/someinvalidroute redirects to /de/de, then to /de/de/de, and so on.
  3. Standalone setup:

    • I build the project locally using next build and then copy the contents of the .next/standalone folder to the server. I start the server using node server.js.
    • All necessary files (.next/static, public, etc.) are copied correctly, and environment variables are set.

I have tried:

  1. Cleared build cache and rebuilt:

    rm -rf .next
    next build
    
  2. Set environment variables at build time:

    NEXT_PUBLIC_API_URL=http://localhost:3000 next build
    
  3. Adjusted middleware:

    • I configured the next-intl middleware to exclude static files:
      export const config = {
        matcher: ['/((?!_next/static|_next/image|favicon.ico|images|api).*)'],
      };
      
  4. Local debugging:

    • I created a simple test page (pages/test.js) that does not work:
      export default function Test() {
        return <div>Test page is working!</div>;
      }
      
    • I can’t reach any other page than the main page under /de or /en, otherwise I get redirected.
  5. Cleared browser cache:

    • Tested in incognito mode and cleared the browser cache, but the white page and console errors persist.
  6. Using standard build output, not standalone:

    • I already tried to build the project without the standalone output option. Does not work either.

My questions are:

  1. Why are NEXT_PUBLIC_* variables not recognized in the browser, even though they are set at both build and runtime?
  2. How can I fix the redirect loop (/de/de/de) issue?
  3. Why does the main page remain blank, even though the HTML is served correctly?
  4. Are there known issues with Next.js 14, next-intl, or the standalone mode that could be relevant here?

Snyk is flagging Angular’s navigateByUrl() as a potential open redirect vulnerability despite sanitizing the URL

While working on an Angular project, Snyk is flagging the use of navigateByUrl() in one of my functions as a potential open redirect vulnerability.

Despite implementing sanitization and validation, the issue persists. Here’s the function I’m using:

getDocs() {
  this._service.getDocs(this.docID).subscribe(res => {
    if (res) {

      const sanitizedDocID = this.sanitizedDocID(this.docID);

      if (res.lstDocs.length) {

        const redirectUrl = `doc/${sanitizedDocID}`;
        if (this.isValidRedirectUrl(redirectUrl)) {
          this._router.navigateByUrl(redirectUrl);
        }

      } else if (!res.lstDocs.length && res.ListNewDocs.length) {

        const redirectUrl = `new-doc/${sanitizedDocID}`;
        if (this.isValidRedirectUrl(redirectUrl)) {
          this._router.navigateByUrl(redirectUrl);
        }

      } else {
        this._router.navigateByUrl('dashboard');
      }
    }
  });
}

isValidRedirectUrl(url) {
  const trustedUrls = ['doc', 'new-doc'];
  return trustedUrls.indexOf(url) > -1;
}

sanitizedDocID(docId) {
  return docId.replace(/[^0-9]/g, '');
}

Snyk is flagging the navigateByUrl(redirectUrl) calls in this function, despite sanitizing the docID and validating the URLs using isValidRedirectUrl().

What else can be done to resolve this? Kindly assist.

Do Electron apps enforce CORS restrictions in the renderer process?

I’m developing an Electron (v33.0.2) application and am struggling to understand how CORS is handled in the renderer process.

When making requests from the renderer process (not the main process), does Electron enforce CORS restrictions like a regular browser would? When I run my app locally (using vite) it consistently sends pre-flight requests with origin localhost:5173. I can see this in the developer tools network tab. However when I bundle and run my app (as a customer would run it in production), I do not see any pre-flight requests at all in the network tab. I’ve tested sending “complex” requests which should on paper be pre-flighted. I’m sure requests are being sent from the renderer process as the requests are being sent via React hooks (using fetch).

I’ve also noticed we have the nodeIntegration flag set on webPreferences which may have an impact here.

Would appreciate clarification on:

  1. Whether CORS is enforced in the Electron renderer process.
  2. Is CORS used on the file:// protocol? One difference between our dev and packaged app is that the origin in dev is http://localhost:5173, but the origin in the packaged app is file://.
  3. If setting nodeIntegration influences CORS behaviour.

When should I use BigInt64Array over Float64Array for numbers less than Number.MAX_SAFE_INTEGER?

In JavaScript there are two typed array constructors that can store rather large numbers:

const longs = new BigInt64Array()
const doubles = new Float64Array()

They both represent arrays of 64-bit signed integers and floating point numbers, respectively.

Is there any reason to use BigInt64Array if I’m not storing numbers larger than Number.MAX_SAFE_INTEGER?

The only upside I can think of to use BigInt64Array (even if your numbers are less than Number.MAX_SAFE_INTEGER) is if you want to enforce an integer type while storing your numbers, since the constructor will throw an error if one of the elements is a floating point number:

const arr = new BigInt64Array([900719925.4534]);
// Uncaught TypeError: Cannot convert 900719925.4534 to a BigInt

However, the downsides are that for each known integer you want to add, you have to remember to append n to it like so:

const arr = new BigInt64Array([523235n, 1093n, 3238n]);

In addition, if only the runtime knows the integers, you have to use the BigInt(value) function to convert them to the proper type:

const arr = new BigInt64Array([BigInt(a), BigInt(b), BigInt(c)]);

The upside to Float64Array is that it works right out of the gate with primitive JavaScript numbers (no number literal suffix or converter function is required).

Is my reasoning correct on the usefulness of BigInt64Array?