Unable to reference a function from a javascript controller with hotwire/stimulus

I’m not sure if this is a local setup issue, or if it’s a code issue, but I’m having trouble using a new function on an existing controller. It’s possible this controller was never successfully being imported in the first place. Even after having the controller specified in the layout and referencing it in the view, I’m being told that it’s referencing an undefined method:

Error

I have done the following for startup:

$ rm -rf public/assets
$ rails assets:precompile
$ bundle install
$ rails s

Here’s what I have

  # application.html.erb
  <body data-controller="modal data">
    
    <%= yield %>

  </body>
// javascript/controllers/modal_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["container", "background", "panel", "terms"]

  changeVisibility(element, visible) {
    console.log(element, hidden);
    if (visible) {
      $(element).on('click', function () {
        $('#terms_modal').removeClass('hidden');
      });
    } else {
      $(element).on('click', function () {
        $('#terms_modal').addClass('hidden');
      });
    }
  };
}
# app/views/home/girl.html.erb
<p>You agree to our <a class="text-red open_terms" data-action="click->modal#changeVisibility('terms_modal', true)">Terms and conditions</a></p>
// app/javascript/controllers/index.js


// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "./application"

// other controllers

import ModalController from "./modal_controller"
application.register("modal", ModalController)

Why does EmailJS send empty values in my React form?

I’m trying to send form data using EmailJS in a React app, but the emails are being sent with empty values. Here’s my form setup:

const handleSubmit = async (e) => {
  e.preventDefault();
  const templateParams = {
    name: formData.name,
    email: formData.email,
  };
  emailjs.send('service_id', 'template_id', templateParams, 'user_id');
};

The ‘formData’ is not being passed correctly. What am I doing wrong?

I’ve tried:

  • Logging ‘formData’ before calling ’emailjs.send’.
  • Checked if ‘formData’ is being updated correctly.

I want to send email to client when a form is submitted.

What should I do to fix this?

How to apply the DRY principle to async/await functions without encountering excessive overhead?

I have two async functions which duplicate a lot of code with each other, and I’m trying to figure out a way to break this duplicate code out into an alternate function which is called by both functions.

My first function looks like this (in reality there is a lot more code related to the message to aggregate):

const getAllMessageStats() = async (userId: string) => {
  const messageIds = await getUserMessageIds(userId);
 
  const messagesRead = 0;
  const reactionsTotal = 0;
  const moreStats = [];

  for (const messageId of messageIds) {
    const message = await getMessage(messageId);

    if (message.read) messagesRead++;
    reactionsTotal += message.reactions;

    const otherStats = [];
    for (const otherDataId of message.otherDataIds) {
      otherData = await getOtherData(otherDataId)
      otherStats.push(doLotsOfOtherCalcs(otherData));
    }
    moreStats.push(otherStats);
  }

  return {
    messagesRead,
    reactionsTotal,
    moreStats
  }
};

My second function looks something like this

const getMessageStats() = async (messageId: string) => {
  const message = await getMessage(messageId);
  const otherStats = [];
  for (const otherDataId of message.otherDataIds) {
    otherData = await getOtherData(otherDataId)
    otherStats.push(doLotsOfOtherCalcs(otherData));
  }
  const miscData = await getMiscData(messageId);

  return {
    read: message.read,
    reactions: message.reactions,
    otherStats,
    miscData
}

I tried the following:

const sharedFunction() = async (messageId: string) => {
  const message = await getMessage(messageId);
  const otherStats = [];
  for (const otherDataId of message.otherDataIds) {
    otherData = await getOtherData(otherDataId)
    otherStats.push(doLotsOfOtherCalcs(otherData));
  }
  return {
    read: message.read,
    reactions: message.reactions,
    otherStats
  }
};

const getMessageStats() = async (messageId: string) => {
  const { read, reactions, otherStats } = await sharedFunction(messageId);
  const miscData = await getMiscData(messageId);
  return {
    read,
    reactions,
    otherStats,
    miscData
  }
};

const getAllMessageStats() = async (userId: string) => {
  const messageIds = await getUserMessageIds(userId);
 
  const messagesRead = 0;
  const reactionsTotal = 0;
  const moreStats = [];

  for (const messageId of messageIds) {
    const { read, reactions, otherStats } = await sharedFunction(messageId);

    if (read) messagesRead++;
    reactionsTotal += reactions;
    moreStats.push(otherStats);
  }

  return {
    messagesRead,
    reactionsTotal,
    moreStats
  }
};

However, my function getAllMessageStats now takes over 3 times as long to execute, especially when I have thousands of messages for a user. After coming across this post about async/await overhead, I now understand why. But my question is: am I now stuck with my original duplicated code? Surely there is a more elegant solution right?

The displayed model color is inconsistent with the original model color

 1. When the model file only has one fbx file, the model texture displayed is different from that seen on the model platform.

2. When there are files in three formats: mtl, obj, and png, the displayed colors are inconsistent with the original model.

const ModelViewer = ({ zipUrl }) => {
  const mountRef = useRef(null);

  useEffect(() => {
    const initScene = () => {
      const width = mountRef.current.clientWidth;
      const height = mountRef.current.clientHeight;

      const scene = new THREE.Scene();
     
      // scene.background = new THREE.Color(0xffffff);
      const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
      const renderer = new THREE.WebGLRenderer({ antialias: true });

      renderer.setSize(width, height);
      mountRef.current.appendChild(renderer.domElement);

      const controls = new OrbitControls(camera, renderer.domElement);

      // Ambient Light with higher intensity
      const ambientLight = new THREE.AmbientLight(0x404040, 2); // higher intensity
      scene.add(ambientLight);

      // Directional Light to simulate sunlight
      const directionalLight = new THREE.DirectionalLight(0xffffff, 2); // higher intensity
      directionalLight.position.set(5, 10, 7.5);
      scene.add(directionalLight);

      // Point Light to add some more brightness
      // const pointLight = new THREE.PointLight(0xffffff, 2, 100); // higher intensity
      // pointLight.position.set(0, 10, 10);
      // scene.add(pointLight);

      camera.position.z = 5;
      controls.update();

      // return { scene, camera, renderer, controls };
      return { scene, camera, renderer };
    };

    const { scene, camera, renderer } = initScene();

    const animate = (object) => {
      // let animationId;
      const render = function () {
        requestAnimationFrame(render);
        // animationId = requestAnimationFrame(render);
        object.rotation.y += 0.01;
        renderer.render(scene, camera);
      };
      render();
      
      // document.addEventListener('click', function() {
      //   cancelAnimationFrame(animationId);
      // });
    };

    const loadModel = async (file, fileType, textureBlob) => {
      let loader;
      const fileBlob = await file.async("blob");
      const fileUrl = URL.createObjectURL(fileBlob);

      switch (fileType) {
        case 'fbx':
          loader = new FBXLoader();
          break;
        case 'obj':
          const mtlFileUrl = getMtlFileUrl(fileUrl);
          const mtlBlob = await fetchMtlBlob(mtlFileUrl);
          const mtlUrl = URL.createObjectURL(mtlBlob);

          const mtlLoader = new MTLLoader();
          mtlLoader.load(mtlUrl, (materials) => {
            materials.preload();
            const objLoader = new OBJLoader();
            objLoader.setMaterials(materials);
            objLoader.load(fileUrl, (object) => {
              applyTexture(object, textureBlob);
              // scene.add(object);
              sizeAndAddModel(object, scene);
              animate(object);
            });
          });
          return;
        case 'gltf':
        case 'glb':
          loader = new GLTFLoader();
          break;
        default:
          console.error(`Unsupported file type: ${fileType}`);
          return;
      }

      loader.load(fileUrl, (object) => {
        applyTexture(object, textureBlob);
        // scene.add(object);
        sizeAndAddModel(object, scene);
        animate(object);
      });
    };

    const sizeAndAddModel = (object, scene) => {
      
      const box = new THREE.Box3().setFromObject(object);
      const size = box.getSize(new THREE.Vector3());
      const targetSize = 4;
      const scale = targetSize / Math.max(size.x, size.y, size.z);

      object.scale.set(scale, scale, scale);
      scene.add(object);
    };

    const fetchMtlBlob = async (mtlFileUrl) => {
      const response = await fetch(mtlFileUrl);
      return await response.blob();
    };

    const applyTexture = (object, textureBlob) => {
      if (textureBlob) {
          const textureLoader = new THREE.TextureLoader();
          const textureUrl = URL.createObjectURL(textureBlob);
          textureLoader.load(textureUrl, (loadedTexture) => {
              object.traverse((child) => {
                  if (child.isMesh) {
                      child.material.map = loadedTexture;
                      child.material.needsUpdate = true;
                  }
              });
          });
      }
    };

    const getMtlFileUrl = (objFileUrl) => {
      return objFileUrl.replace('.obj', '.mtl');
    };

    const fetchAndUnzip = async (url) => {
      const response = await fetch(url.replace(/http:///g, 'https://'));
      const arrayBuffer = await response.arrayBuffer();
      const zip = await JSZip.loadAsync(arrayBuffer);

      let textureFile;
      const modelFiles = [];

      zip.forEach((relativePath, file) => {
        const ext = relativePath.split('.').pop().toLowerCase();
      
        if (ext === 'png' || ext === 'jpg' || ext === 'jpeg') {
          textureFile = file;
        
        } else if (ext === 'fbx' || ext === 'obj' || ext === 'glb' || ext === 'gltf') {
          modelFiles.push({ file, fileType: ext });
        
        } else if (ext === 'mtl') {
          modelFiles.push({ file, fileType: 'mtl' });
        }
      });

      const textureBlob = textureFile ? await textureFile.async("blob") : null;

      for (const { file, fileType } of modelFiles) {
        await loadModel(file, fileType, textureBlob);
      }
    };

    fetchAndUnzip(zipUrl);

    return () => {
      mountRef.current.innerHTML = '';
    };
  }, [zipUrl]);

  return <div ref={mountRef} style={{ width: 800, height: 600 }} />;
};

export default ModelViewer;

The following is the parsing of files in various formats

switch (fileType) {
        case 'fbx':
          loader = new FBXLoader();
          break;
        case 'obj':
          const mtlFileUrl = getMtlFileUrl(fileUrl);
          const mtlBlob = await fetchMtlBlob(mtlFileUrl);
          const mtlUrl = URL.createObjectURL(mtlBlob);

          const mtlLoader = new MTLLoader();
          mtlLoader.load(mtlUrl, (materials) => {
            materials.preload();
            const objLoader = new OBJLoader();
            objLoader.setMaterials(materials);
            objLoader.load(fileUrl, (object) => {
              applyTexture(object, textureBlob);
              // scene.add(object);
              sizeAndAddModel(object, scene);
              animate(object);
            });
          });
          return;
        case 'gltf':
        case 'glb':
          loader = new GLTFLoader();
          break;
        default:
          console.error(`Unsupported file type: ${fileType}`);
          return;
      }

When a 3D model contains files in three formats: mtl, obj, and png, the texture file is loaded correctly, but the display color is incorrect.

Has anyone faced out of memory javascript when doing next start for nextjs project

When the we do next start we get this error,

This is a sample stack trace:

info - Loaded env from /app/.env
Listening on port 3009

<--- Last few GCs --->

[1:0x7fd1df6e8330] 41527 ms: Mark-sweep (reduce) 497.4 (509.4) -> 497.1 (510.2) MB, 1868.8 / 0.1 ms (average mu = 0.063, current mu = 0.001) allocation failure scavenge might not succeed

<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

For more context: we run our next 12 project in a docker env and this is deployed by kubernetes

also as a temporary fix i have increased memory allocation using -max-old-space-size flag
initial memory usage – 500MB, increased memory to 2.5 GB

but as we started monitoring memory usage when server starts it drops down to 200-300 MB

why that might be happening, pls explain

scrollwidth and clientwidth remain the same while increasing fontsize

I have this function that increases the fontsize of a container depending on the scrollwidth. But When I print the scrollWidth and clientWidth in the while loop, it stays the same.

function adjustFontSize(textField) {
      let fontSize = 10; // Starting font size
      textField.style.fontSize = fontSize + "px";
      // Increment font size until textField no longer fits
      while (textField.scrollWidth <= textField.clientWidth) {
        fontSize++;
        textField.style.fontSize = fontSize + "px";
      }
    }

This is my html. I am trying to resize the direct children of playerInfoContainer (the playerInfo classes).

<div id="playerInfoContainer">
        <div class="playerInfo winner">
          <div class="playerInfoName">Sané</div>
          <div class="playerInfoPercentage">9 (40,9%)</div>
        </div>
        <div class="playerInfo">
          <div class="playerInfoName">Hamm</div>
          <div class="playerInfoPercentage">8 (36,4%)</div>
        </div>

        <div class="playerInfo">
          <div class="playerInfoName">Gulászci</div>
          <div class="playerInfoPercentage">5 (22,7%)</div>
        </div>
      </div>

This is the css. The parent container is a flexbox but even if I change it to grid it doesn’t help.

#playerInfoContainer {
      position: absolute;
      top: 172px;
      left: 0;
      width: 267px;
      height: 599px;
      text-align: center;
      display: flex;
      flex-direction: column;
      justify-content: space-around;
      align-items: center;
    }

.playerInfo {
      width: 200px;
    }

.playerInfoName {
      font-family: UberEatsFat;
    }

    .playerInfoPercentage {
      font-size: 25px;
      font-family: UberEatsRegular;
    }

I tried to set the childrens flex-basis to 0px and flex-shrink to 0, so that the width stays the same.
When I am looking in devTools the scrollwidth of the containers that exceed the clientWidth is actually greater so I am only getting the wrong readings when executing my code.

In telegram app in iPhone, element of long list getting cut unexpectedly while scrolling

In my telegram React/Next JS app in iPhone, element of long list getting cut unexpectedly while scrolling.

Some time list element cutting half or from anywhere, which seems to me render issue, because if it hide from CSS/JS complete element will either show or hide.

It happens some time and some time its working fine. I tried so many workaround but not able to fix correctly.

below code i am using to prevent app close in telegram while scrolling down.

 html.iphone-device{
      overflow: hidden ;  
      overscroll-behavior: none;
      body{
        height: 100dvh !important;
        overscroll-behavior: none;
        overflow: auto;
        touch-action: initial !important;
        position: relative;
        scroll-behavior: unset;
    }
    }

enter image description here

Expo raising really vague warns/errors

I recently tried to move my bare rn app to expo

But I m getting vague errors/warnings

 WARN  [AsyncStorage] Using object type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.
Key passed: [object Object]
 ERROR  Error checking auth status: [Error: com.facebook.react.bridge.ReadableNativeMap cannot be cast to java.lang.String]
 WARN  [AsyncStorage] Using object type for key is not supported. This can lead to unexpected behavior/errors. Use string instead.
Key passed: [object Object]
 WARN  The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuratio
n doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration.

Is there a way for it to tell me exactly what line/file raises the problem?

app.json, I enabled sourceMaps too, which supposedly would improve the error handling for such cases

But still its extremely vague:

ERROR  Error checking auth status: [ReferenceError: Property 'SecureStore' doesn't exist]
{
  "expo": {
    "name": "football-sim",
    "slug": "football-sim",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/images/icon.png",
    "scheme": "myapp",
    "userInterfaceStyle": "automatic",
    "packagerOpts": {
      "sourceExts": ["js", "json", "ts", "tsx", "jsx"],
      "sourceMaps": true
    },
    "splash": {
      "image": "./assets/images/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/images/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      }
    },
    "web": {
      "bundler": "metro",
      "output": "static",
      "favicon": "./assets/images/favicon.png"
    },
    "plugins": [
      "expo-router",
      "expo-secure-store"
    ],
    "experiments": {
      "typedRoutes": true
    }
  }
}

User input not sent to server

My controls look like this

return (
        <div className='contentDiv'>
            <Container>
                <Form onSubmit={getFormData}>
                    <Form.Group className="mb-3" controlId="formBasicName">
                        <Row><Form.Control name="id" type="number" value={3} readOnly={ true }></Form.Control></Row>
                        <Row>
                            <Col>
                                <Form.Label>Forname:</Form.Label> <Form.Control name="firstname" type="text" placeholder="navn"></Form.Control>
                            </Col>
                            <Col>
                                <Form.Label>Efternavn:</Form.Label> <Form.Control name="lastname" type="text" placeholder="Efternavn"></Form.Control>
                            </Col>                           
                        </Row>                                                                      
                    </Form.Group>
                    <Row><Button type="submit">Create user</Button></Row>                    
                </Form>
            </Container>
        </div>
    )

And the function that is called on submit looks like this

const getFormData = async (event) => {              
        const data = new FormData(event.currentTarget);
        event.preventDefault();

        await fetch(`/api/newMember`, {
            method: 'POST',
            headers: { 
                "Content-Type": "application/json" 
            },
            body: JSON.stringify(data)
        })
        .then(res => {
            console.log(res);
        })
        .catch(err => {
            console.log(err)
        })        
    };

If I do this in the client (and fill the form with id=3, firsname=Lucky lastname=Luke):

for (let [key, value] of data.entries()) {
            console.log(key, value);
        }

I get (in the console)

id 3
firstname Lucky
lastname Luke

The server post method that does not receive the data from the client, looks like this

const express = require('express');
const knex = require('knex');
const cors = require('cors');
const bodyParser = require('body-parser');

const app = express();
const port = 3001;

    app.post('/api/newMember', (req, resp) => {
        const { id, firstname, lastname } = req.body;
    
        console.log(req.body);            
        
        const newMember = {
            id: id,
            firstname: firstname,
            lastname: lastname,       
        };        
    
        db('members').insert(newMember)
        .then(res => {
            resp.send(res)        
        })
        .catch(err => {
            console.log(err);
            return;
        })              
    })

app.listen(port, () => {
    console.log('Server listening on port: ' + port);
});

In the server, I have this code: console.log(req.body);. It jsut prints {}

Can anyone figure out what I’m doing wrong? Thanks

Is this the only way to get DaisyUI colors based on the theme in Svelte?

I’m using Svelte with DaisyUI and need to retrieve the current theme colors dynamically. I rely on these colors in chroma-js, so it’s crucial to get them correctly. The code below works for me, but it feels like there might be a more efficient or idiomatic way to achieve this. Is this the only/best approach to get the current theme colors from DaisyUI?

Here’s the relevant code snippet:

<script lang="ts">
    import { onMount } from 'svelte';
    import { theme } from '$stores/global';
    import type { ThemeColor, ThemeColorMap } from "$type/shared";

    type ColorElement = {
        [key in ThemeColor]?: HTMLDivElement | undefined;
    };

    export let colors: ThemeColorMap;

    const colorKeys: ThemeColor[] = ['accent', 'info', 'neutral'];
    let colorElements: ColorElement = {} as ColorElement; // This will store references to the div elements

    function getColors(): ThemeColorMap {
        if (typeof window === 'undefined') {
            return {};
        }

        let newColors: ThemeColorMap = {};
        Object.entries(colorElements).forEach(([key, element]) => {
            if (element) {
                newColors[key as ThemeColor] = getComputedStyle(element).backgroundColor;
            }
        });
        return newColors;
    }

    onMount(() => {
        colors = getColors();
    });

    theme.subscribe(() => {
        colors = getColors();
    });
</script>

<div class="hidden">
    {#each colorKeys as key}
        <div class="bg-{key}" bind:this={colorElements[key]}></div>
    {/each}
</div>

This approach creates hidden div elements with DaisyUI color classes, and then I use getComputedStyle to extract the background color from these elements.

While this works, it feels a bit hacky, especially with the hidden elements. Is there a more direct or efficient way to access the current DaisyUI theme colors in Svelte, particularly for use with chroma-js?

Any suggestions would be appreciated!

How to Start Screen Recording Without Using getDisplayMedia in JavaScript?

I am working on a web application where I need to implement screen recording functionality, similar to how Microsoft Teams handles screen sharing and recording during meetings. Typically, getDisplayMedia is used to capture the screen, but in my case, I’m looking for alternative solutions. This could be due to browser restrictions or the need for more control over the capture process.

Does anyone know of a way to achieve this without using getDisplayMedia? For example, could it be done using WebRTC, third-party libraries, or lower-level browser APIs? I’m also open to solutions involving browser extensions or native apps, similar to how Microsoft Teams might handle screen recording.

Any guidance or alternative approaches would be greatly appreciated!

Aes-256-GCM OpenSSL > nodeJS

I’m trying to decrypt data that have been encrypted using C++/OpenSSL Aes-256-GCM from nodejs javascript.

C++ encryption implementation:

#include <string>
#include <sstream>
#include <iomanip>
#include <vector>
#include <iostream>
#include <openssl/rand.h>
#include <openssl/bio.h>
#include <openssl/buffer.h> 

std::string base64Encode(const unsigned char* input, int length)
{
    BIO *bio, *b64;
    BUF_MEM *bufferPtr;

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(bio, input, length);
    BIO_flush(bio);
    BIO_get_mem_ptr(bio, &bufferPtr);
    
    std::string result(bufferPtr->data, bufferPtr->length);

    BIO_free_all(bio);

    return result;
}

std::string bytesToHexString(const unsigned char* data, int len)
{
    std::stringstream ss;
    ss << std::hex << std::setfill('0');
    for (int i = 0; i < len; ++i)
    {
        ss << std::setw(2) << static_cast<int>(data[i]);
    }
    return ss.str();
}

bool encrypt(std::string& data, const std::vector<unsigned char>& password)
{
    constexpr int KEY_SIZE        = 32;  // AES-256
    constexpr int IV_SIZE         = 16;
    constexpr int TAG_SIZE        = 16;
    constexpr int ITERATION_COUNT = 10000;

    std::vector<unsigned char> key(KEY_SIZE);
    std::vector<unsigned char> iv(IV_SIZE);    
    std::vector<unsigned char> salt(16);
    RAND_bytes(salt.data(), 16);
 
    // PBKDF2 for key derivation
    if (PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(password.data()), password.size(),
        salt.data(), salt.size(), ITERATION_COUNT,
        EVP_sha256(), KEY_SIZE, key.data()) != 1)
        return false;

    if (RAND_bytes(iv.data(), IV_SIZE) != 1)
        return false;
    
    // encryption context
    std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)> ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
    if (!ctx)
        return false;
    
    if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr, key.data(), iv.data()) != 1)
        return false;

    std::vector<unsigned char> ciphertext(data.size() + EVP_MAX_BLOCK_LENGTH);
    int outLen1 = 0, outLen2 = 0;
    
    if (EVP_EncryptUpdate(ctx.get(), ciphertext.data(), &outLen1,
        reinterpret_cast<const unsigned char*>(data.data()),
        data.size()) != 1)
        return false;
    
    if (EVP_EncryptFinal_ex(ctx.get(), ciphertext.data() + outLen1, &outLen2) != 1)
        return false;
    
    //tag
    std::vector<unsigned char> tag(TAG_SIZE);
    if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, TAG_SIZE, tag.data()) != 1)
        return false;
    
    // debug:
    std::cout << "nSalt:n"           << bytesToHexString(salt.data(), salt.size())             << std::endl;
    std::cout << "nPassword (hex):n" << bytesToHexString(password.data(), password.size())     << std::endl;
    std::cout << "nDerived key:n"    << bytesToHexString(key.data(), KEY_SIZE)                 << std::endl;
    std::cout << "nIV:n"             << bytesToHexString(iv.data(),  IV_SIZE)                  << std::endl;
    std::cout << "nTAG:n"            << bytesToHexString(tag.data(), TAG_SIZE)                 << std::endl;
    std::cout << "nCiphertext:n"     << bytesToHexString(ciphertext.data(), outLen1 + outLen2) << std::endl;

    // combined: salt, IV, ciphertext, and tag
    std::vector<unsigned char> result;
    result.insert(result.end(), salt.begin(), salt.end());
    result.insert(result.end(), iv.begin(), iv.end());
    result.insert(result.end(), ciphertext.begin(), ciphertext.begin() + outLen1 + outLen2);
    result.insert(result.end(), tag.begin(), tag.end());
    
    data = base64Encode(result.data(), result.size());
    
    // erase buffers...
    return true;
}

int main()
{
    std::vector<unsigned char> password(9);
    RAND_bytes(password.data(), 9);

    std::string base64Password = base64Encode(password.data(), password.size());
    std::cout << "Password: " << base64Password << std::endl;

    std::string data = "Hello, World!";
    encrypt(data, password);

    std::cout << "nEncrypted data:n" << data;
    return 0;
}

javascript decryption implementation:

function decrypt(encryptedBase64, passwordBase64, SALT_SIZE) {
    const KEY_SIZE        = 32;  // AES-256
    const IV_SIZE         = 16;
    const TAG_SIZE        = 16;
    const ITERATION_COUNT = 10000;

    // Decode the base64 encrypted data
    const encryptedBuffer = Buffer.from(encryptedBase64, 'base64');    
    // Decode the base64 password
    const passwordBuffer  = Buffer.from(passwordBase64, 'base64');

    // Extract salt, IV, ciphertext, and tag
    const salt       = encryptedBuffer.slice(0, SALT_SIZE);
    const iv         = encryptedBuffer.slice(SALT_SIZE, SALT_SIZE + IV_SIZE);
    const tag        = encryptedBuffer.slice(-TAG_SIZE);
    const ciphertext = encryptedBuffer.slice(SALT_SIZE + IV_SIZE, -TAG_SIZE);

    // Derive the key using PBKDF2
    const key = crypto.pbkdf2Sync(passwordBuffer, salt, ITERATION_COUNT, KEY_SIZE, 'sha256');

    console.log('nSalt:n',             salt.toString('hex'));
    console.log('nPassword (hex):n',   passwordBuffer.toString('hex'));
    console.log('nDerived key:n',      key.toString('hex'));
    console.log('nIV:n',               iv.toString('hex'));
    console.log('nTAG:n',              tag.toString('hex'));
    console.log('nCiphertext:n',       ciphertext.toString('hex'));

    const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
    decipher.setAuthTag(tag);

    let decrypted = decipher.update(ciphertext);
    decipher.final()

    return decrypted.toString('utf8');
}


(async () =>
{
    const encryptedBase64 = 'p4lx3wi2bstgBR/puZBYOKcJU4Ua41spBbeR9etV9K7BaRWGsUqKUIymO+vaQ1PAlv/M1e9OxExVykGNzQ=='
    const password = 'Et5Q/1E6iTg5'
    try
    {
        const decryptedData = decrypt(encryptedBase64, password, 16)
        console.log('Decrypted data:', decryptedData)
    } catch (error)
    {
        console.error('Decryption failed:', error.message)
    }
}

I confirmed that on the javascript function everything matches the ones generated from the C++, i mean, the password, salt, iv, derived key, ciphertext, but the function fails at decipher.final()
with the error:

Error: Unsupported state or unable to authenticate data
    at Decipheriv.final 

My javascript implementation was mostly based on this question.

What i’m missing?

Access destructured value inside function body in vue

In the docs for vue’s v-model modifiers, they give this example and there is a playground link as well.

<script setup>
const [model, modifiers] = defineModel({
  set(value) {
    if (modifiers.capitalize) {
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

<template>
  <input type="text" v-model="model" />
</template>

This code can be edited so that the modifiers variable is now foo. What kind of pattern is this and how is this achievable (with pseudocode) since the value of const [model, foo] = ... is in a different scope than foo.capitalize?

<script setup>
const [model, foo] = defineModel({
//            ^^^
  set(value) {
    if (foo.capitalize) {
//      ^^^
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

<template>
  <input type="text" v-model="model" />
</template>

How do I mock Ora with Jest? I can’t get it to validate that start, succeed, info, stop were called

I’ve been trying for quite a while to create a mock implementation of Ora and then use jest to test that it calls several functions from it like start, stop, info. Also, I’m doing this with ES6.

Module:

import ora from 'ora'

export async function myModule({

  const spinner = ora({
    text: "this is a test",
  }).start();

  spinner.info('continue the test)

  spinner.stop()
});

Test:

import { beforeEach, describe, jest } from '@jest/globals'

jest.unstable_mockModule('ora', () => ({
  default: jest.fn(() => ({
    start: jest.fn().mockReturnThis(),
    info: jest.fn().mockReturnThis(),
    succeed: jest.fn().mockReturnThis(),
    fail: jest.fn().mockReturnThis(),
    stop: jest.fn().mockReturnThis(),
  })),
}));

const { default: ora } = await import('ora');
const { myModule } = await import('./myModule.js')

describe('my_test', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  const test = await myModule()
  
  expect(ora.start).toHaveBeenCalled();

I was eventually able to copy what someone else had done and it did work with this code below, but I don’t understand WHY this works. I would like to understand why this works so that I can apply that information to other modules that I may need to mock, and I want to do it with as little code as necessary.

import { beforeEach, describe, jest } from '@jest/globals'

const mockOra = {
  fail: jest.fn(),
  succeed: jest.fn(),
  info: jest.fn(),
  warn: jest.fn(),
  stop: jest.fn(),
};
mockOra.start = jest.fn(() => mockOra);
jest.mock('ora', () => jest.fn(() => mockOra));

const { default: ora } = await import('ora');
const { myModule } = await import('./myModule.js')

describe('my_test', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  const test = await myModule()
  
  expect(ora.start).toHaveBeenCalled();

Child Component function firing before parent state is updated

I have been trying to make a dropdown component that is reusable and decided to put 2 inputs in there so I can validate the range of value. The validation happens but the state passed as a prop does not update before setIsOpen function is fired.

// states
const { data, error, isLoading } = useRegionsData();
  const {
    filters,
    setFilters,
  } = useFilter();

  const [localFilters, setLocalFilters] = useState<FilterValues>({
    price: { min: '', max: '' },
    region: [],
    area: { min: '', max: '' },
    bedroom: '',
  });

  const [localErrors, setLocalErrors] = useState<FilterErrors>({
    area: false,
    bedroom: false,
    price: false,
  });

  console.log(localFilters.price);
  console.log(localErrors);

  /* this useffect initilizes local filter regions to look like a object {id,name,checked} */
  useEffect(() => {
    const savedFilters = localStorage.getItem('filters');
    if (savedFilters) {
      const filterData = JSON.parse(savedFilters) as FilterValues;
      setLocalFilters(filterData);
    } else if (data) {
      const regionData = data.map((region) => ({
        id: region.id,
        name: region.name,
        checked: false,
      }));

      setLocalFilters((prevState) => ({
        ...prevState,
        region: regionData,
      }));
    }
  }, [data, setLocalFilters]);

// update functions
const handlePriceConfirmClick = () => {
    if (!localFilters.price.min || !localFilters.price.max) {
      setLocalErrors((prevErrors) => ({
        ...prevErrors,
        price: true,
      }));

      return;
    }

    if (Number(localFilters.price.min) > Number(localFilters.price.max)) {
      setLocalErrors((prevErrors) => ({
        ...prevErrors,
        price: true,
      }));

      return;
    }

    // If everything is okay, submit the form
    setFilters(localFilters);
    localStorage.setItem('filters', JSON.stringify(localFilters));
  };

  

  const handlePriceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;

    setLocalFilters((prevState) => {
      const updatedPrice = {
        ...prevState.price,
        [name]: value,
      };

      const minPrice = Number(updatedPrice.min);
      const maxPrice = Number(updatedPrice.max);

      const hasError = minPrice >= maxPrice;
      setLocalErrors((prevErrors) => ({
        ...prevErrors,
        price: hasError,
      }));

      return {
        ...prevState,
        price: updatedPrice,
      };
    });
  };

so what i am interested in is NewDropdown component when handleSubmit happends the error should stop the function from compleating but for some reason the previous value of state is passed to it.

// dropdown component where error is suppose to decide if it closes it
import { useEffect, useRef, useState } from 'react';
import upIcon from '../../assets/images/DropdownIconUp.svg';
import downIcon from '../../assets/images/DropdownIconDown.svg';
import Button from '../button/Button';

type Props = {
  children: React.ReactNode;
  buttonText: string;
  dropdownTitle: string;
  onSubmit: () => void;
  error?: boolean;
};

const NewDropdown = ({
  children,
  dropdownTitle,
  buttonText,
  onSubmit,
  error = false,
}: Props) => {
  const [isOpen, setIsOpen] = useState(false);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);

  /* content outside of dropdown will trigger setIsOpen(false) */
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        buttonRef.current &&
        !buttonRef.current.contains(event.target as Node) &&
        dropdownRef.current &&
        !dropdownRef.current.contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, []);

  const handleClick = () => {
    setIsOpen((prevState) => !prevState);
  };

  const handleSubmit = () => {
    console.log('err', error);
    onSubmit();
    if (!error) {
      setIsOpen(false);
    }
  };

  return (
    <div className='relative'>
      <button
        ref={buttonRef}
        onClick={handleClick}
        className={`rounded-md py-2 px-[14px] flex justify-center items-center gap-1 text-text font-medium ${
          isOpen ? 'bg-selected' : 'bg-transparent'
        }`}
      >
        {buttonText}
        <span>
          <img
            src={isOpen ? upIcon : downIcon}
            alt={isOpen ? 'menu open icon' : 'menu closed icon'}
          />
        </span>
      </button>
      {isOpen && (
        <div
          ref={dropdownRef}
          className='absolute bottom-0 transform translate-y-full left-0 p-6 rounded-[10px] border border-border bg-white z-20'
        >
          <p className='font-firago font-medium mb-6'>{dropdownTitle}</p>
          {children}
          <div className='flex justify-end mt-[32px]'>
            <Button
              className='bg-primary text-white py-2 px-[14px] rounded-lg font-firago font-medium transition-colors duration-200 ease-linear hover:bg-primaryHover'
              onClick={handleSubmit}
            >
              არჩევა
            </Button>
          </div>
        </div>
      )}
    </div>
  );
};

export default NewDropdown;

My guess was that the state was old so I used let variable instead but with that the error does not update at all because it is not a react state