Having trouble getting JS function to validate numbers

First, I am not a professional. This is a personal project, so apologies if my terminology or POV of the issue is wrong. I’m working on a special kind of calculator that requires user input for the calculations. The calculator is made of several tables, but the first table is the most important one. It simply requires the user to divide a single “unit” into ratios that will be used throughout the rest of the calculator. These numbers must equal 100 or else the rest of the output will be incorrect.

I would like for the “TOTAL (%)” to have a function that checks that the number calculated in the textbox equals 100. If it doesn’t, I would like for the number to turn red along with a warning.

The issue I’m having is that the function doesn’t seem to be working. I’ve concluded that it could be one or more of the following reasons:

  1. The function simply doesn’t work as written.
  2. The function isn’t being triggered on the right element.
  3. The function isn’t being triggered by the right event.

Here’s the code that I’ve written:

function valTotal() {
  var x = document.getElementById('t1total');
  var y = 100;

  if (x > y) {
    alert("Warning! Your total is greater than 100%. Please adjust your ratios above so that the total equals 100% before continuing.");
    x.style.color = "#cf0911";
    return false;
  }
}

function myFun0() {
  let water = document.getElementById('t1water').value;
  let ingr1 = document.getElementById('t1ingr1').value;
  let ingr2 = document.getElementById('t1ingr2').value;
  let ingr3 = document.getElementById('t1ingr3').value;
  let ingr4 = document.getElementById('t1ingr4').value;
  let ingr5 = document.getElementById('t1ingr5').value;
  let ingr6 = document.getElementById('t1ingr6').value;
  let total = Number(water) + Number(ingr1) + Number(ingr2) + Number(ingr3) + Number(ingr4) + Number(ingr5) + Number(ingr6);

  document.getElementById("t1total").value = total;
}
body {
  font-family: monospace;
}

table,
td {
  border: 1px solid black;
  border-collapse: collapse;
}

.invis {
  border-style: hidden;
}

.txcenter {
  text-align: center;
}

.txbox1 {
  width: 50px;
}
<!DOCTYPE html>
<html>

<head>


</head>
<!--Calculator-->

<body>
  <div>
    <h2 id="calc">Substrate Calculator</h2>
    <table class="invis">
      <tr>
        <td>
          <!--Table 1: SUB RATIO-->
          <table style="float: left">
            <tr style="background-color:#751e0b">
              <td colspan="2">
                <div class="txcenter">
                  <b style="color:white">SUB RATIO</b>
                </div>
              </td>
            </tr>
            <!--Table 1: SUB RATIO > Water (%)-->
            <tr>
              <td>
                <div>Water (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1water" onkeyup="myFun0();myFun1();myFun2()" onchange="myFun0();myFun1();myFun2()">
              </td>
            </tr>
            <!--Table 1: SUB RATIO > Ingredient 1 (%)-->
            <tr>
              <td>
                <div>Ingredient 1 (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1ingr1" onkeyup="myFun0();myFun1();myFun2()" onchange="myFun0();myFun1();myFun2()">
              </td>
            </tr>
            <!--Table 1: SUB RATIO > Ingredient 2 (%)-->
            <tr>
              <td>
                <div>Ingredient 2 (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1ingr2" onkeyup="myFun0();myFun1();myFun2()" onchange="myFun0();myFun1();myFun2()">
              </td>
            </tr>
            <!--Table 1: SUB RATIO > Ingredient 3 (%)-->
            <tr>
              <td>
                <div>Ingredient 3 (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1ingr3" onkeyup="myFun0();myFun1();myFun2()" onchange="myFun0();myFun1();myFun2()">
              </td>
            </tr>
            <!--Table 1: SUB RATIO > Ingredient 4 (%)-->
            <tr>
              <td>
                <div>Ingredient 4 (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1ingr4" onkeyup="myFun0();myFun1();myFun2()" onchange="myFun0();myFun1();myFun2()">
              </td>
            </tr>
            <!--Table 1: SUB RATIO > Ingredient 5 (%)-->
            <tr>
              <td>
                <div>Ingredient 5 (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1ingr5" onkeyup="myFun0();myFun1();myFun2()" onchange="myFun0();myFun1();myFun2()">
              </td>
            </tr>
            <!--Table 1: SUB RATIO > Ingredient 6 (%)-->
            <tr>
              <td>
                <div>Ingredient 6 (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1ingr6" onkeyup="myFun0();myFun1();myFun2()" onchange="myFun0();myFun1();myFun2()">
              </td>
            </tr>
            <!--Table 1: SUB RATIO > TOTAL (%)-->
            <tr>
              <td>
                <div>TOTAL (%)</div>
              </td>
              <td>
                <input class="txbox1" type="textbox" id="t1total" onchange="valTotal()" readonly>
              </td>
            </tr>
          </table>
        </td>
      </tr>
    </table>
  </div>
</body>

</html>

is just the first table of the calculator. The issue I’m having is with the “TOTAL (%)” field which is the only place where the validation function will be used.

Many many thanks.

EDIT: The function in question is the valTotal() function.

Full qualified names for DOM’s FIle interface and Node.js’s File interface

From v18, Node.js the experimental File interface has been added. It could conflict with DOM’s File interface in full-stack projects.

Currently, the TypeScript correctly understand which File means, but the ESLint could complain:

try {

  // ESLint: The 'File' is still an experimental feature and is not supported until Node. js 20.0.0. 
  // The configured version range is '>=16.0.0'
  newBase64EncodedFiles = await Promise.all(
    newFiles.map(async (file: File): Promise<string> => encodeFileToBase64(file))
  );

} catch (error: unknown) {

  // ...
  
}

Besides persuading of ESLint, for the code readability it’s better to specify the full qualified name like NodeJS.File and Window.File. However, there is not Window namespace.

Is it possible to dynamically change the color of a font glyph used to make an animated video in webm format, in JavaScript? If so, how?

I have a webm video made somewhat like this:

  1. Import .ttf or .otf font into Adobe Illustrator (AI).
  2. Copy/paste individual font glyph from AI to Photoshop.
  3. Create layers of the font glyph for animation in After Effects (AE) next.
  4. Animate the glyph in AE.
  5. Generate a .webm file from the AE animation.

The final result is basically this:

enter image description here

I would like to make this somewhat feel interactive if possible, so when you hover it, it changes color to purple (animates color from black to purple). Sort of like these glyphs which are just font glyphs in HTML with CSS transitions on hover:

enter image description here

Is anything like that possible in this context, using the .webm format? (I’ve never used .webm, or even video really, much before).

Some ideas:

  1. Render/play video on HTML5 canvas at runtime, and somehow animate the black pixels to be purple on hover? (Is anything like that feasible?).
  2. Maybe the .webm format is like an SVG in some ways, and I can just set some color variable on hover, after the .webm file has already been created? Like set some variables at runtime in JS or CSS.
  3. Maybe I can do some CSS overlay/blend-mode magic, to create the purple effect on hover.
  4. Other things?

Wondering if there is any approach to make this a reality.

I found a bug in Replit

When I run my code, the webview appears as the chrome connect was reset error.

I ran my code, I expected it to work, but the the chrome connect was reset error appeared in the webview.

I do not know what is happening and I need help.

Session NOT persisting Redis-Server in React App

I created a react app that works with the spotify API. Within it, I am setting up a backend to handle my Spotify auth using a redis-server. It seems to be working fine except for when I set up this middleware to check access tokens in which it checks the session for an existing access token.

Every time it checks the session contains no variables although they do get properly set after the token is generated. I am not sure if I am understanding sessions correctly or if there is an issue with my code, but essentially, I’d like for the middleware to work when using other endpoints to persist the access token across the backend.

Here is my code for reference:

Redis set up

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET || 'fallback_default_secret_key',
  resave: false,
  saveUninitialized: true,
  cookie: {
    secure: process.env.NODE_ENV === 'production', // Ensure cookies are only sent over HTTPS in production
    httpOnly: true, // Prevent client-side JavaScript from accessing the cookie
    maxAge: 3600000 // Optional: Set a 1-hour expiration for cookies
  }
}));

Middleware to refresh token if expired, currently not working as session data is not persisting:

const checkAccessToken = async (req, res, next) => {
  console.log('Session data:', req.session); // Log the session object to verify

  let accessToken = req.session.accessToken;
  const refreshToken = req.session.refreshToken;
  const tokenExpiresIn = req.session.tokenExpiresIn;

  if (!accessToken || Date.now() >= tokenExpiresIn) {
    try {
      // Use the refresh token to get a new access token
      const authString = `${process.env.CLIENT_ID}:${process.env.CLIENT_SECRET}`;
      const base64Auth = Buffer.from(authString).toString('base64');

      const response = await axios.post('https://accounts.spotify.com/api/token',
        new URLSearchParams({
          grant_type: 'refresh_token',
          refresh_token: refreshToken
        }).toString(), {
          headers: {
            'Authorization': 'Basic ' + base64Auth,
            'Content-Type': 'application/x-www-form-urlencoded'
          }
        });

      // Update session with new access token and expiration time
      accessToken = response.data.access_token;
      req.session.accessToken = accessToken;
      req.session.tokenExpiresIn = Date.now() + response.data.expires_in * 1000;

      req.accessToken = accessToken;
      next(); // Proceed to the next middleware

    } catch (error) {
      console.error('Error refreshing access token:', error.response ? error.response.data : error.message);
      return res.status(500).json({ error: 'Failed to refresh access token. Please re-authenticate.' });
    }
  } else {
    req.accessToken = accessToken;
    next();
  }
};

Setting access token to session:

router.post('/token', async (req, res) => {
  const { code, codeVerifier } = req.body;

  try {
    const authString = `${clientId}:${clientSecret}`;
    const base64Auth = Buffer.from(authString).toString('base64');

    const response = await axios.post('https://accounts.spotify.com/api/token', 
      new URLSearchParams({
        grant_type: 'authorization_code',
        code,
        redirect_uri: redirectUri,
        code_verifier: codeVerifier
      }).toString(), {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Basic ' + base64Auth
      }
    });
    const { access_token, refresh_token, expires_in } = response.data;
    req.session.accessToken = access_token;
    req.session.refreshToken = refresh_token;
    req.session.tokenExpiresIn = Date.now() + expires_in * 1000;

    res.json({ message: 'Token generated successfully' });
  } catch (error) {
    console.error('Error exchanging code for token:', error.response ? error.response.data : error.message);
    res.status(500).json({ error: 'Failed to exchange code for token' });
  }
});

// Callback route to handle redirect from Spotify
router.get('/callback', (req, res) => {
    const code = req.query.code;
    const state = req.query.state;
    res.redirect(`http://localhost:5173/callback?code=${code}&state=${state}`);
});

Session persisting Redis-Server in React App

I created a react app that works with the spotify API. Within it, I am setting up a backend to handle my Spotify auth using a redis-server. It seems to be working fine except for when I set up this middleware to check access tokens in which it checks the session for an existing access token.

Every time it checks the session contains no variables although they do get properly set after the token is generated. I am not sure if I am understanding sessions correctly or if there is an issue with my code, but essentially, I’d like for the middleware to work when using other endpoints to persist the access token across the backend.

Here is my code for reference:

Redis set up

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET || 'fallback_default_secret_key',
  resave: false,
  saveUninitialized: true,
  cookie: {
    secure: process.env.NODE_ENV === 'production', // Ensure cookies are only sent over HTTPS in production
    httpOnly: true, // Prevent client-side JavaScript from accessing the cookie
    maxAge: 3600000 // Optional: Set a 1-hour expiration for cookies
  }
}));

Middleware to refresh token if expired, currently not working as session data is not persisting:

const checkAccessToken = async (req, res, next) => {
  console.log('Session data:', req.session); // Log the session object to verify

  let accessToken = req.session.accessToken;
  const refreshToken = req.session.refreshToken;
  const tokenExpiresIn = req.session.tokenExpiresIn;

  if (!accessToken || Date.now() >= tokenExpiresIn) {
    try {
      // Use the refresh token to get a new access token
      const authString = `${process.env.CLIENT_ID}:${process.env.CLIENT_SECRET}`;
      const base64Auth = Buffer.from(authString).toString('base64');

      const response = await axios.post('https://accounts.spotify.com/api/token',
        new URLSearchParams({
          grant_type: 'refresh_token',
          refresh_token: refreshToken
        }).toString(), {
          headers: {
            'Authorization': 'Basic ' + base64Auth,
            'Content-Type': 'application/x-www-form-urlencoded'
          }
        });

      // Update session with new access token and expiration time
      accessToken = response.data.access_token;
      req.session.accessToken = accessToken;
      req.session.tokenExpiresIn = Date.now() + response.data.expires_in * 1000;

      req.accessToken = accessToken;
      next(); // Proceed to the next middleware

    } catch (error) {
      console.error('Error refreshing access token:', error.response ? error.response.data : error.message);
      return res.status(500).json({ error: 'Failed to refresh access token. Please re-authenticate.' });
    }
  } else {
    req.accessToken = accessToken;
    next();
  }
};

Setting access token to session:

router.post('/token', async (req, res) => {
  const { code, codeVerifier } = req.body;

  try {
    const authString = `${clientId}:${clientSecret}`;
    const base64Auth = Buffer.from(authString).toString('base64');

    const response = await axios.post('https://accounts.spotify.com/api/token', 
      new URLSearchParams({
        grant_type: 'authorization_code',
        code,
        redirect_uri: redirectUri,
        code_verifier: codeVerifier
      }).toString(), {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Basic ' + base64Auth
      }
    });
    const { access_token, refresh_token, expires_in } = response.data;
    req.session.accessToken = access_token;
    req.session.refreshToken = refresh_token;
    req.session.tokenExpiresIn = Date.now() + expires_in * 1000;

    res.json({ message: 'Token generated successfully' });
  } catch (error) {
    console.error('Error exchanging code for token:', error.response ? error.response.data : error.message);
    res.status(500).json({ error: 'Failed to exchange code for token' });
  }
});

// Callback route to handle redirect from Spotify
router.get('/callback', (req, res) => {
    const code = req.query.code;
    const state = req.query.state;
    res.redirect(`http://localhost:5173/callback?code=${code}&state=${state}`);
});

Maps & Minification Problem (Teavm JS & Java)

js files are from eaglercraft’s src code

(minecraft java –> Eaglercraft HTML/JS) (How? teavm)

i have two js files a .js and a .js.map, i want to unminify it but its easier said then done.

— what i tried to do —

i found sokra’s source-map-visualization a site for un-minifying (<- if thats the right word) so i upload the js then the map then it askes me to upload the java files so i started to look at the needed file name, go through the folders to find it, select it and upload it

a little in i realized it may require a hell of alot files so i checked with sources.length and and i was right i would have to manually find and upload 1258 java files so i figured might as well as others if they know what would work in this specific situation


Code in Google Drive

google drive contains: rar with the src code for eaglercraft 1.5.2 (and mc 1.5.2 i believe), & teavm from the github + a readme.md before i moved it to google drive as a precaution to any copyright claims

virustotal if need be


if you helped or atleast tried thanks – kaleb1583

How to store user input using React state with dynamic number of questions

I’m trying to create a form and store the user inputs for each textfield into states. The questions will be retrieved from an external source so I store all of them into an object where the questions and answers are represented as key-value pairs. The problem with this implementation is that I can’t type anything into the textfields. They all remain empty no matter what I type.

import Box from "@mui/system/Box";
import Stack from "@mui/system/Stack";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import TextField from "@mui/material/TextField";
import { useState } from "react";
import HRMButton from "../Button/HRMButton";
import { fonts } from "../../Styles";


export default function OnboardingSurvey({prev, next, style}) {
    //The form could contain any number of questions not just the four used in this example
    const [form, setForm] = useState({
        "What suggestions do you have for improving the company culture or work environment?": "",
        "Do you have any feedback on your manager or team that you'd like to share?": "",
        "How was your experience working here?": "",
        "Is there anything else you would like to share that we haven't discussed?": "" 
    });

    return (
        <Box sx={{...{
            border: "1px solid #EBEBEB",
            borderRadius: "10px",
            minWidth: "1003px",
            paddingX: "113px",
            paddingY: "63px",
            fontFamily: fonts.fontFamily
        }, ...style}}>
            <h4 style={{textAlign: "center", marginTop: 0}}>Please answer the questions below as detailed as possible</h4>
            <p style={{textAlign: "center", marginBottom: "50px"}}>
                Your answers are going to be used to further improve our process.
            </p>
            {Object.keys(form).map((question) => (
                <>
                    <p style={{textAlign: "left"}}>{question}</p>
                    <TextField 
                        id={`${question}-textfield`}
                        value={form[question]}
                        placeholder={"Your answer here"}
                        onChange={(e) => {
                            setForm({
                                ...form,
                                question: e.target.value
                            });
                        }}
                        rows={4}
                        multiline
                        sx={{
                            marginBottom: "50px",
                            width: "100%"
                        }}
                    />
                </>
            ))}
            <Stack direction="row" alignContent="center" justifyContent="space-between">
                <HRMButton mode="secondaryB" startIcon={<ArrowBackIcon />} onClick={prev}>Previous</HRMButton>
                <HRMButton mode="primary" onClick={next}>Save and next</HRMButton>
            </Stack>
        </Box>
    );
};

Php to echo data back to javascript without the echo being visible to the user on the browser, but available to javascript

  1. User clicks a button on the webpage
  2. javascript uses XMLHtppRequest to send the user’s choice to the server
  3. php searches the database & finds the data

That all works & the php currently displays the data by echo onto the browser, but I don’t want the data visible at this time.

What I can’t figure out is how to deliver the data so that javascript can then handle the display.

I did manage to do this by sending cookies, but I don’t think that is a good idea.

In some tests I could send the data embedded in javascript but that worked when the php file was loaded into the browser, but failed when the php file was called by XMLHttpRequest. I don’t know why it fails when called by XMLHttpRequest. It also requires html formatting in the php which I would rather not do.

Everything works except for preventing the echo just displaying to the user & assigning it to a js variable.

I am using Xampp, html, css, javascript, php, MariaDb (mySQL)

I have successfully implemented this with cookies, but that only worked with small data.

I tried embedding the $row data in js & html. This worked if the php script was loaded into the browser, but not if called in the background, as it is with XMLHtppRequest, I have no idea where the data was sent, it didn’t seem to reach the browser.

I have tried json_encode, but it still displays & I tried to use response.Text, but that only produced either “connection success” or null.

I don’t think the specific code that I have written is relevant, I think I am missing some key points.

Jest unit test: how mock fetch() with abort signal

I have the following typescript function that

  1. tries to fetch the content of a URL,
  2. should time out after certain time with AbortController.

Now I want to write a unit test that simulates the timeout behavior: controller.signal should be called. I should expect an “AbortError” to be thrown.

export async function fetchScriptContents(
    scriptUrl: string,
    timeout?: number
): Promise<string> {
    const controller = new AbortController();
    const fetchTimeout = setTimeout(() => {
        controller.abort(`AbortError: Fetch timeout after ${timeout} ms`);
    }, timeout ?? 3000);

    return await fetch(scriptUrl, { signal: controller.signal, cache: "no-cache" })
        .then(async (response: Response) => {
            //do something
         }
        .catch(async (e: Error) => {
            clearTimeout(fetchTimeout);
            if (e.name === "TimeoutError") {
                throw new Error(`fetchScriptContents: timeout error fetching script ${scriptUrl}`);
            } else if (e.name === "AbortError") {   
                throw new Error(`fetchScriptContents: Aborted by user action or fetch timeout: ${e.message}`);
            } else if (e.name === "TypeError") {
                throw new Error("fetchScriptContents: TypeError, method is not supported");
            } else {
                // A network error, or some other problem.
                throw e;
            }        
        });
}

This is my (multiple) attempt(s) at writing this test.
I defined a timeout of 500ms and mock the fetch implementation to resolve long after timeout (timeout + 1500ms).

if I don’t have an await on fetchScriptContents like so:

const resultPromise = fetchScriptContents("https://vzexample.com/script.js", timeout);

I would get a resolved value but no timeout will be hit.

but if I add an await before it
the test will always timeout. why is that? something wrong with my fetch mock? How do I properly write the test?


   beforeEach(() => {
        jest.clearAllMocks();
        jest.useFakeTimers();  // Use fake timers for controlling timeouts
    });

    afterEach(() => {
        jest.useRealTimers();
    });
    
    it('should throw', async () => {
        const timeout = 500;

        (global.fetch as jest.Mock) = jest.fn(() => new Promise((resolve, _reject) => setTimeout(() => resolve(mockResponse), timeout + 1500)));
        
        const abortSpy = jest.spyOn(AbortController.prototype, 'abort'); 
        jest.spyOn(global, 'setTimeout');

        const resultPromise = fetchScriptContents("https://vzexample.com/script.js", timeout);
        jest.runAllTimers();
        expect(setTimeout).toHaveBeenCalledTimes(1);
        expect(abortSpy).toHaveBeenCalledTimes(1);

        await expect(fetchScriptContents("https://vzexample.com/script.js", timeout))
            .rejects
            .toThrow('Aborted by user action or fetch timeout');
});

I’ve read a lot about jest these recent days but I am out of ideas. Please help, thanks!

Use pipeThrough/pipeTo to transform then stream to Azure Blob

I’m trying to stream some data from an API, transform it, then upload to Azure blob using an an Azure function. The limitation I face using Azure Blobstore is that there’s no ‘writeStream’, only uploadStream. There’s no obvious point in the pipeline to upload to a stream.

Example –

   function MyWriter(writer) {
      return new WritableStream({
        write(chunk) {
          console.log('writing');
          writer.write(chunk);
        },
      });
    }

    export const generate = async (iterable) => { 
      await ReadableStream.from(iterable);
        .pipeThrough(new TextDecoderStream())
        .pipeThrough(new MyTransformer())
        .pipeThrough(new CompressionStream('gzip'))
        .pipeTo(MyWriter(writeOutStream));

    /*. await blockBlobClient.uploadStream(); */
    }

Am i missing something obvious?

Azure upload stream docs – https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-upload-javascript#upload-a-block-blob-from-a-stream

Tried various combinations of pipelining, but there’s no obvious point to upload the stream.

calculating equiangular spiral between two known points in JavaScript, based on Julia example

I’d like to calculate x and y coordinates along an equiangular spiral between two known points in javascript.

I’m attempting to convert convert the Julia code found here to javascript:
Drawing an equiangular spiral between two known points in Julia

I’m stuck on this term: exp.(-t)

The natural logarithm of a negative number is undefined, and t should be a positive number.

The other terms appear to be straightforward, but maybe there are other mistakes.

This is what I’ve got, so far:

function hypC(a, b) {
  return Math.sqrt(a * a + b * b);
}

let p1 = [1000,1000]
, p2 = [0,0]
, r = hypC(p2[0]-p1[0],p2[1]-p1[1])
, theta_offset = Math.atan((p1[1]-p2[1])/(p1[0]-p2[0]))

//# Number of points
, rez = 1500
//# Number of revolutions
, rev = 5
//# Radius as spiral decreases
, t = 0
, tRange = r
//# Angle as spiral decreases
, theta = 0 + theta_offset
, thetaRange = 2*(Math.PI)*rev
, x , y
;

spiral()
function spiral(){
 
  for (let i=1; i<rez + 1; i++) {  
      t = tRange  * (i/rez)
      , theta = thetaRange  * (i/rez)
      , x = Math.cos(theta) * r * Math.log(-t) + p2[0] 
      , y = Math.sin(theta) * r * Math.log(-t) + p2[1] 
      console.log(x,y)  
    }
  
}

nodejs AsyncLocalStorage misunderstanding

I stumbled upon AsyncLocalStorage and wrote some test code to play around with it.

My assumption was, that I can use it to set a state for a code chain and this state will magically exist in all code called inside of that chain without passing it explicitly as parameters. My assumption or code is wrong.

import { AsyncLocalStorage } from "async_hooks";

const als = new AsyncLocalStorage();

async function logStorePromise(prefix: string): Promise<void> {
    console.log(`${prefix} ${JSON.stringify(als.getStore())}`);
    await new Promise<void>(r => r());
}

als.run(
    { foo: "bar" },
    async () => {
        logStorePromise("start")
            .then(() => logStorePromise("then"));

        als.disable();
        console.log("> disabled");
        
    }
);

This logs

start {"foo":"bar"}
> disabled
end undefined
then undefined

My assuption was

start {"foo":"bar"}
> disabled
end undefined
then {"foo":"bar"}

Can someone explain to me where my misunderstanding lies?

I understand, that the then in my Promise chain places my () => logStorePromise("then") on the microtask queue. At the moment it is called, the Store is already disabled. Same as if I would use a global object that I clear when disabled is called.

Real use-case scenario
Let’s say I have an express server and use the AsyncLocalStorage to add a trackingId to each incoming request.
Let’s also say that request starts some asynchronous tasks and returns before they are finished.
After my test code above I would assume, that I should not clear the tracking id when the request is done as it would also remove that state from still running tasks.
But I feel like I should clear that state, because of memory and because I fear that express reuses that context somehow and the tracking id could still be set on some other call.

How to initialize redux slice with enum value?

I am trying to set initial state of redux slice with a enum value, but it throws Uncaught TypeError: undefined has no properties.

The code is:

export const themeTokenSlice = createSlice({
    name: "themeToken",
    initialState: darkThemeToken,
    reducers: {
        toggleThemeToken: (state) => {
            switch (state.theme) {
                case Themes.dark:
                    state = darkThemeToken;
                    break;
                case Themes.light:
                    state = lightThemeToken;
                    break;
                default:
                    return state;
            }
        }
    }
});

export const darkThemeToken: themeState = {
    theme: Themes.dark
};


export interface themeState {
    theme: Themes;
}

export enum Themes {
    dark = "dark",
    light = "light",
}

The way it works:

export const darkThemeToken: themeState = {
    theme: "dark" as Themes.dark
};

Is there a way to initialize the state with enum?

Playing an audio file before it is fully loaded on the JS page

I’m currently working on the ability to voice text at the click of a button. I have a button that should play text when clicked. I get the audio file through the API after the click, everything works well in this regard, I get the url of the audio file. The problem appears when I try to make this audio work on click. My code now looks something like this:

speech.addEventListener('click', () => {
  let speechText = "Привет!";
  const audio = new Audio();
  const audioURL = await getOpenaiVoice(voiceAPI, voiceID, speechText);
  audio.src = audioURL;
  audio.play();
}

The code waits for the URL to return from getOpenaiVoice and assigns it to audio.src, but then audio.play(); is triggered instantly, which causes an error: NotSupportedError: Failed to load because no supported source was found.

If you click on the button again after a while, the audio will play normally, because it has already been fully uploaded to the site, and there will be no errors.

I tried to rewrite the code with promises, wrap elements in async await, but I have too little knowledge of asynchronous programming for me to immediately see what the problem is. I’ve been working on this problem all day and I don’t see any options.

function audioLoad(audio, url) {
        let prom = new Promise((resolve, reject) => {
           audio.src = url;
           audio.addEventListener('loadeddata', () => {
                resolve(audio);
           });
        });
            
        prom.then((value) => {
            value.play();
        })
}
  
async function audioCreation() {
        let speechText = "Привет!";
        const audio = new Audio();
        const audioURL = await getOpenaiVoice(voiceAPI, voiceID, speechText);
        audio.src = audioURL;
        audio.play();
}
        
audioCreation();

The code above works, however, only if you click after the file is fully downloaded. On the first click there are always an error, because it starts downloading only after the click. The general idea is: The user clicks on the voice button, all requests are sent to the API, etc., the code waits for the response to return, and after the response returns, either the audio file starts playing immediately, or it just hangs waiting for full download, and after downloading it starts playing. Thank you in advance.