How do i can build js projects? [closed]

I am a beginner in web development and learning js after finishing it which is the most useful method or technique to build projects? Some guys are saying that see YouTube video and add some code form your side and other are saying that do all with your own like you have to make projects with out any YouTube videos and all just put your mind but how can i do it with my own? Give me some suggestions please

I expecting some useful suggestions to improve my learning

On hover, display overlay element but it is blinking

I have a canvas and overlay text on top of it. I want to display the text only if mouse is hovering on the canvas.
It is working on simple HTML https://codepen.io/Ariel-Ariel-the-bold/pen/MYgvvxZ

.rootc {
  display: flex;
}

.container {
  flex-grow: 1;
  flex: 1;
  position: relative;
  background-color: green;
}

.div1 {
  width: 100%;
  background-color: red;
}

.div1:hover ~ .div2 {
  display: flex;
}

.div2 {
  position: absolute;
  background-color: yellow;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  align-items: center;
  justify-content: center;
  display: none;
}
<div class="rootc">
  <div class="container">
    <canvas class="div1"></canvas>
    <div class="div2">
      <h1>This is a text</h1>
    </div>
  </div>
  <div class="container">
  </div>
  <div class="container">
  </div>
</div>

but not on vue app https://codesandbox.io/p/sandbox/blue-surf-h8gm3l
on vue app, it is blinking
how do I achieve this on vue app ?

Input field loses focus when I type

I am building a React application that uses an input field to allow users to type a recipe title, then, when they submit the form, the recipe should be added to a RecipeList.

At the moment, my code isn’t working correctly because I can only type 1 word before the input field loses focus. If I had to guess, I would guess that the form is re-rendering somehow when I type a letter.
I want to continue typing without my input losing focus.

Here is my code:

function RecipeApp() {

  const [recipes, setRecipes] = useState(initialRecipes);
  const [recipeInput, setRecipeInput] = useState("");
  const [summaryInput, setSummaryInput] = useState("");
  const [ingredientsInput, setIngredientsInput] = useState([]);
  const [cookTimeInput, setCookTimeInput] = useState("");

  function handleAddRecipe(e) {
    e.preventDefault();
    if (!recipeInput || !summaryInput || !ingredientsInput || !cookTimeInput) {
      alert("Please fill out all fields!");
      return;
    }

    const newRecipe = {
      id: recipes.length,
      title: recipeInput,
      summary: summaryInput,
      ingredients: ingredientsInput.split(",").reduce(
        (acc, ing, idx) => ({
          ...acc,
          [`ingredient${idx + 1}`]: ing.trim(),
        }),
        {}
      ),
      cookTime: parseInt(cookTimeInput, 10),
    };

    setRecipes([...recipes, newRecipe]);
    setRecipeInput("");
    setSummaryInput("");
    setIngredientsInput("");
    setCookTimeInput("");
  }

  function InputForm() {
    return (
      <form onSubmit={handleAddRecipe}>
        <p>Recipe name</p>
        <input
          value={recipeInput}
          onChange={(e) => setRecipeInput(e.target.value)}
        />

        <p>Summary</p>
        <input value={summaryInput} placeholder="Enter a description" />

        <p>Ingredients</p>
        <input
          value={ingredientsInput}
          placeholder="List up to four ingredients, seperated by a comma"
        />

        <p>Cook Time (minutes)</p>
        <input value={cookTimeInput} placeholder="Time in minutes" />

        <button>Add</button>
      </form>
    );
  }

  function RecipeList() {
    return (
      <ul>
        {recipes.map((recipe) => (
          <li key={recipe.id}>
            {recipe.title}
            <button>View</button>
            <button>Delete</button>
          </li>
        ))}
      </ul>
    );
  }

  return (
    <div className="RecipeApp">
      <InputForm />
      <h2>List of recipes:</h2>
      <RecipeList />
    </div>
  );
}

Why can’t we fully control the navigation bar color on Android in standalone mode for PWAs in 2024?

I’m currently developing a Progressive Web App (PWA) and trying to customize the navigation bar color (the bar at the bottom of the screen) on Android. While it’s possible to control the color of the status bar (at the top), I haven’t been able to achieve the same level of control in standalone mode.

My questions are:

  1. Why is it still not possible in 2024 to reliably control the navigation bar color on Android in standalone mode for PWAs? Is there a technical or security reason behind this limitation?

  2. Why does the navigation bar is hidden in fullscreen mode but not in standalone mode? It feels like we’re forced to choose between display both or display nothing.

  3. Is there a workaround to control the navigation bar color in standalone mode while keeping the status bar visible? Or is this just build like this and nobody question it ?

Any insight into this issue or updates on the state of navigation bar styling for PWAs would be greatly appreciated. Thank you!

I can’t write code for a telegram bot on node js so that it works correctly

I want to make it so that when I press the keyboard next to the text input field, the bot sends inline_keyboard, but I can’t, if I try to write the code, then when I press /menu, it sends the message ‘Bot menu’ several times and increases by one, help me solve the problem

bot.onText(//menu/, (msg) => {
   bot.sendMessage(msg.chat.id, "Bot menu", mainKeyboard);
});

const mainKeyboard = {

  reply_markup: { 

   keyboard: [
   [{text:'⭐️ chapter1', callback_data:'section1'}],
   [{text:'⭐️ chapter2', callback_data:'section2'}],
   [{text:'⭐️ chapter3', callback_data:'section3'}],
     ]
   }
}

bot.on('message', (msg) => {
   const chatId = msg.chat.id
   const text = msg.text

   if (text === '⭐️ chapter1') {
       const section1Keyboard = {
           reply_markup: {
               inline_keyboard: [
                   [{ text: 'subsection 1.1', callback_data: 'subsection1_1' }],
                   [{ text: 'subsection 1.2', callback_data: 'subsection1_2' }],
                   [{ text: 'Назад', callback_data: 'back' }],
               ],
           },
       }
       bot.sendMessage(chatId, 'you have chosen chapter 1', section1Keyboard);
   } else if (text === '⭐️ chapter2') {
       bot.sendMessage(chatId, 'you have chosen chapter 2');
   } else if (text === '⭐️ chapter3') {
       bot.sendMessage(chatId, 'you have chosen chapter 3');
   }
})

I’m just trying to make bots, I don’t know what can be done in this situation, I’m starting to doubt that it is possible to connect a keyboard and inline_keyboard

Issue with Cropping and Rotating an Image While Maintaining Correct Dimensions

I am implementing a cropping functionality where I work with a downscaled version of the original image. The original image is stored in IndexedDB, and every time I fetch it, it is in its initial dimensions and fully intact. My goal is to apply cropping and rotation operations on the downscaled image and then use the cropping dimensions to crop the corresponding area from the original image.

Everything works as expected when performing a single crop operation. However, the problem arises when I crop the downscaled image at 0° rotation, then rotate it to 90°, and crop again. When I download the cropped image after these operations, the cropped area does not match the expected part of the original image. I believe this issue stems from incorrect calculations when converting dimensions between the downscaled and original image, or during rotation adjustments.

Below is the relevant code I am using:

const adjustCumulativeCropForRotation = (cumulativeCrop, originalWidth, originalHeight, rotation) => {
    if (!cumulativeCrop) return null; // No cumulative crop to adjust

    const { x, y, width, height } = cumulativeCrop;

    switch (rotation) {
        case 90:
            return {
                x: y, 
                y: originalWidth - width - x,
                width,
                height
            };
        case 180:
            return {
                x: originalWidth - width - x,
                y: originalHeight - height - y,
                width,
                height,
            };
        case 270:
            return {
                x: originalHeight - height - y,
                y: x,
                width: height,
                height: width,
            };
        case 0:
        default:
            return cumulativeCrop; // No adjustment needed
    }
};

// Handle processing the original image
const handleProcessOriginalImage = async () => {
    try {
        const { originalImage, originalImageName } = await getOriginalImageFromDB();

        if (!originalImage) {
            console.error("Original image not found");
            return;
        }

        // Rotate the original image before cropping
        const rotatedImageBlob = await rotateImage(
            originalImage instanceof Blob ? URL.createObjectURL(originalImage) : originalImage,
            rotation
        );

        const dimensions = await getImageDimensions(URL.createObjectURL(rotatedImageBlob));
        const { width: originalWidth, height: originalHeight } = dimensions;

        const { width: downscaledWidth, height: downscaledHeight } = downscaledImageDimensions;
        const { x: cropX, y: cropY, width: cropWidth, height: cropHeight } = cropDimensions;

        // Set the current image dimensions
        let currentOriginalWidth = currentCroppedImageDimensions?.width || originalWidth;
        let currentOriginalHeight = currentCroppedImageDimensions?.height || originalHeight;

        if (rotation === 90 || rotation === 270) {
            currentOriginalWidth = currentCroppedImageDimensions?.height || originalHeight;
            currentOriginalHeight = currentCroppedImageDimensions?.width || originalWidth;
        }

        // Scale the current crop to the original image dimensions
        const scaledCrop = {
            x: Math.round((cropX / downscaledWidth) * currentOriginalWidth),
            y: Math.round((cropY / downscaledHeight) * currentOriginalHeight),
            width: Math.round((cropWidth / downscaledWidth) * currentOriginalWidth),
            height: Math.round((cropHeight / downscaledHeight) * currentOriginalHeight),
        };

        let recalculatedCumulativeCrop = cumulativeCrop;

        if (rotation !== 0 && recalculatedCumulativeCrop) {
            recalculatedCumulativeCrop = adjustCumulativeCropForRotation(
                recalculatedCumulativeCrop,
                originalWidth, // Always use initial original dimensions
                originalHeight,
                rotation
            );
        }

        // Update cumulative crop with new crop
        let updatedCumulativeCrop = recalculatedCumulativeCrop
            ? {
                x: recalculatedCumulativeCrop.x + scaledCrop.x,
                y: recalculatedCumulativeCrop.y + scaledCrop.y,
                width: scaledCrop.width,
                height: scaledCrop.height,
            }
            : { ...scaledCrop };

        // Update the state asynchronously
        setCumulativeCrop(updatedCumulativeCrop);

        setCurrentCroppedImageDimensions({
            width: scaledCrop.width,
            height: scaledCrop.height,
        });

        // Process the original image using the updated crop
        const processedBlob = await processOriginalImage(rotatedImageBlob, updatedCumulativeCrop, rotation);

        // Download the processed image
        downloadImage(processedBlob, originalImageName);
    } catch (error) {
        console.error("Error processing original image:", error);
    }
};

I suspect the issue is in how I calculate or adjust the cropping dimensions when the image is rotated or scaled. How can I correctly compute the cropping area to ensure the selected portion matches the original image, regardless of rotation or scaling? Any insights or suggestions to fix this would be highly appreciated!

Trying to add data to an input field

Trying to answer to this question adding data to an input field

I thought maybe useful to remind the data-* attributes.

Imagine to add to my html element having id “myElement” a customized data “data-myData”. it’d become very easy to handle the added information by JavaScript.

Here an example of js code:

<script> var myData = document.getElementById("myElement").getAttribute("data-myData") </script>

while the html element could be something like that:

<input type="text" id="myElement" value="any value" data-myData="extra information">

I hope it helps like it is useful for me.

Any better ideas or hints?

API request is made when copy button is clicked

I have this React template page:

import Button from '@mui/material/Button';

import { useNavigate, useParams } from 'react-router-dom';

import Box from '@mui/material/Box';
import Divider from '@mui/material/Divider';
import Typography from '@mui/material/Typography';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import format from 'date-fns/format';

// eslint-disable-next-line import/order
import { getRandomBackgroundImage } from '../utils';
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';
import { queryAPIKeyByID } from 'app/api/queries/api-keys';
import FuseLoading from '@fuse/core/FuseLoading';

function APIKeyView() {
  const { id: APIKeyID } = useParams();
  const [copied, setCopied] = useState(false);
  const { data: apiKeyData, isLoading } = useQuery({
    queryKey: ['APIKeyByID', APIKeyID],
    queryFn: () => queryAPIKeyByID(APIKeyID),
  });
  const navigate = useNavigate();

  if (isLoading) {
    return (
      <div className="flex items-center justify-center w-full h-screen">
        <FuseLoading />
      </div>
    );
  }

  const handleCopy = () => {
    navigator.clipboard.writeText(apiKeyData.api_token);
    setCopied(true);

    // Reset label back to "Copy" after 2 seconds
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <>
      <Box
        className="relative w-full h-160 sm:h-192 px-32 sm:px-48"
        sx={{
          backgroundColor: 'background.default',
        }}
      >
        <img
          className="absolute inset-0 object-cover w-full h-full"
          src={getRandomBackgroundImage()}
          alt="user background"
        />
      </Box>
      <div className="relative flex flex-col flex-auto items-center p-24 pt-0 sm:p-48 sm:pt-0">
        <div className="w-full max-w-3xl">
          <div className="flex flex-auto items-end">
            <div className="flex items-center ml-auto relative bottom-20">
              <Button
                variant="contained"
                color="secondary"
                onClick={() => navigate('edit', { state: apiKeyData })}
              >
                <FuseSvgIcon size={20}>heroicons-outline:pencil-alt</FuseSvgIcon>
                <span className="mx-8">Edit</span>
              </Button>
            </div>
          </div>

          <Typography className="mt-12 text-4xl font-bold truncate">
            {apiKeyData.username}
          </Typography>

          <Divider className="my-32" />

          <div className="flex flex-col space-y-32">
             
            {apiKeyData.email && (
              <div className="flex items-center gap-24 leading-6">
                <FuseSvgIcon>heroicons-outline:mail</FuseSvgIcon>
                <a
                  className="hover:underline text-primary-500"
                  href={`mailto: ${apiKeyData.email}`}
                  target="_blank"
                  rel="noreferrer"
                >
                  {apiKeyData.email}
                </a>
              </div>
            )}

            {apiKeyData.api_token && (
                <div className="flex items-center gap-24 leading-6">
                  <FuseSvgIcon>heroicons-outline:key</FuseSvgIcon>
                  {`${apiKeyData.api_token.substring(0, 9)  } .... ${  apiKeyData.api_token.substring(apiKeyData.api_token.length - 4)}`}

                  <Button
                      onClick={handleCopy}
                      className="ml"
                  >
                    {copied ? "Copied!" : "Copy"}
                  </Button>
                </div>
            )}
              </div>
        </div>
      </div>
    </>
  );
}

export default APIKeyView;

I added a button to copy the api_key value into clipboard. But when I press the copy button API request is made to get the data again. This action is incorrect. I just need to copy the text. Do you know how I can prevent this?

infer from array of predicates

classes with static check methods. wanted to find the
matching one and return to it in wrap method.

this architecture perfectly suits for my need of OOP-ifying.
here is the way that I used to do it in JavaScript:

class A {
    static check = arg => typeof arg == "number"
    constructor(arg) { this.value = arg}
    log = () => console.log("A/", this.value)
}

class B {
    static check = arg => typeof arg == "string"
    constructor(arg) { this.note = arg }
    log = () => console.log("B/", this.note)
}

function wrap(arg, classes) {
    for (const Class of classes) 
        if (Class.check(arg))
            return new Class(arg)
}

const classes = [A, B]

wrap("adaptive", classes).log()
wrap(999, classes).log()

lately, I thought maybe TypeScript could help me with that. what
I expected was that the wrap call would indicate the return
value (since it’s all about types with no logic)

unfortunately, it was not the case. the closest I could get was that:

type PayloadA = number
type PayloadB = string
type PayloadGeneric = PayloadA | PayloadB

class A {
    static check(arg:PayloadGeneric): arg is PayloadA {
        return typeof arg == "number"
    }
    value: number
    constructor(arg: PayloadA) { this.value = arg}
    log = () => console.log("A/", this.value)
}

class B {
    static check(arg:PayloadGeneric): arg is PayloadB {
        return typeof arg == "string"
    }
    note: string
    constructor(arg: PayloadB) { this.note = arg}
    log = () => console.log("B/", this.note)
}

function wrap(arg: PayloadGeneric) {
    if (A.check(arg)) {   // understands that arg is number
        return new A(arg) // and that arg is suitable for PayloadA
    } 
    if (B.check(arg)) {   // understands that arg is string
        return new B(arg) // and that arg is suitable for PayloadB
    }
}

const instanceA = wrap(999) // but here it doesn't understand the type
const instanceB = wrap("adaptive") // neither here

(I know PayloadA and PayloadB may look silly since they’re just
number and string but the whole point is about having custom interfaces
and predicates. number and string is just for the example and wrapping them
into a type demonstrates my purpouse better)

look wrap can only accept PayloadA or PayloadB. inside, we have 2 ifs.
one checks for PayloadA and understands and returns, the other checks
for PayloadB and again understands well and returns. but TypeScript
doesn’t stop there and continues reading the rest, like it may get executed.
even thinks it may return undefined since I’m not handling the default
return, but logically, I do – I only accept A or B and handled both. so,

  • how can I make ts understand that all the cases are handled
  • and how to use arrays instead of hard coded ifs
  • is there a better way for TypeScript? the problem might be my js brain

and please don’t tell me “just put return type A | B” I don’t wanna hardcode
cuz the given list can change. I may put classes that none of their check method
matches, in this case I’d like to see that the return type is going to be undefined.
or I put one that matches, then I’d like to see what the return type is gonna be.

thanks in advance

Typed arrays do not dynamically refresh if using ArrayBuffers as a basis

I’m working on a little project that requires access to bytes at a low level. The code below should inform many here what I’m aiming to achieve in the long term, but I’ve alighted upon an interesting short term problem that’s resulted in some head scratching. Here’s the “constructor” for my object, which is invoked in the time honoured fashion:

function CPU_Z80(memSize,IOSize)
{
//This is the constructor for the ORIGINAL Z80 CPU, as used in CP/M computers and old 8-bit Z80 home computers.
//Use this to emulate the behaviour of that CPU.

this.registerSet = new ArrayBuffer(26);

this.reg_AF = new Uint16Array(this.registerSet, 0, 1);
this.reg_BC = new Uint16Array(this.registerSet, 2, 1);
this.reg_DE = new Uint16Array(this.registerSet, 4, 1);  
this.reg_HL = new Uint16Array(this.registerSet, 6, 1);  

this.reg_AF_Alt = new Uint16Array(this.registerSet, 8, 1);
this.reg_BC_Alt = new Uint16Array(this.registerSet, 10, 1);
this.reg_DE_Alt = new Uint16Array(this.registerSet, 12, 1); 
this.reg_HL_Alt = new Uint16Array(this.registerSet, 14, 1); 

this.reg_A = new Uint8Array(this.registerSet, 0, 1);
this.reg_B = new Uint8Array(this.registerSet, 2, 1);
this.reg_C = new Uint8Array(this.registerSet, 3, 1);
this.reg_D = new Uint8Array(this.registerSet, 4, 1);
this.reg_E = new Uint8Array(this.registerSet, 5, 1);
this.reg_H = new Uint8Array(this.registerSet, 6, 1);
this.reg_L = new Uint8Array(this.registerSet, 7, 1);
this.reg_F = new Uint8Array(this.registerSet, 1, 1);

this.reg_A_Alt = new Uint8Array(this.registerSet, 8, 1);
this.reg_B_Alt = new Uint8Array(this.registerSet, 10, 1);
this.reg_C_Alt = new Uint8Array(this.registerSet, 11, 1);
this.reg_D_Alt = new Uint8Array(this.registerSet, 12, 1);
this.reg_E_Alt = new Uint8Array(this.registerSet, 13, 1);
this.reg_H_Alt = new Uint8Array(this.registerSet, 14, 1);
this.reg_L_Alt = new Uint8Array(this.registerSet, 15, 1);
this.reg_F_Alt = new Uint8Array(this.registerSet, 9, 1);

this.reg_I = new Uint8Array(this.registerSet, 16, 1)
this.reg_R = new Uint8Array(this.registerSet, 17, 1);

this.reg_IX = new Uint16Array(this.registerSet, 18, 1);
this.reg_IY = new Uint16Array(this.registerSet, 20, 1);
this.reg_SP = new Uint16Array(this.registerSet, 22, 1);
this.reg_PC = new Uint16Array(this.registerSet, 24, 1);

this.memSpace = new Uint8Array(memSize);
this.IOSpace = new Uint8Array(IOSize);

this.clock = new Uint32Array(2);        //Counter being used as a surrogate for the CPU clock.

this.assembler = null;

//End "constructor"
}

My thought, upon reading the basic docs for typed arrays, was that I could create an ArrayBuffer of raw bytes, then use the typed array mechanism to access them at will as either 8-bit or 16-bit values, while maintaining control over enndianness myself (so that I can use the same approach for writing code to simulate a CPU with different endianness to a Z80).

But, I’ve made a discovery.

If I create a CPU_Z80 object, using

myCPU = new CPU_Z80(65536,65536) // example instantiation

and then issue the following code in the console:

myCPU.reg_AF = 0x3AD2;
console.log(myCPU.reg_AF);

the value I entered is returned. But if I issue the following:

myCPU.reg_AF = 0x3AD2;
console.log(myCPU.reg_A);

the value returned is zero, not the expected 0x3A.

This, of course, defeats entirely the object of using the definitions above to make register access nice and convenient. Instead of providing a means of accessing the underlying data in the ArrayBuffer as and when I want it, the various TypedArray constructors simply generate an object, load that object with data from the ArrayBuffer once, and always return that loaded value from that point on.

But, worse still, in order to investigate matters further, I tried examining the underlying ArrayBuffer. The following:

myCPU.reg_AF = 0x3AD2;
console.log(myCPU.registerSet[0]);

yielded, to my astonishment, the value undefined.

So, the code above, which I thought was storing the value 0x3AD” in the registerSet ArrayBuffer, was actually doing nothing of the sort. A quick check reveals that this behaviour is identical on Chrome and Firefox.

Yet, if instead of referencing an ArrayBuffer, I define a TypedArray as a fixed number of relevant units, accessing the contents thereof via array indices works as expected.

What is going on with ArrayBuffers? And if they don’t provide the functionality I was expecting, what’s the point of them? Because the behaviour of typed arrays completely changes if one uses ArrayBuffers, and does so in a counter-intuitive (and infuriating) manner. Worse still, none of the documentation (Mozilla is usually far better in this respect) alerted me to any of this, or offers solutions.

Can someone explain:

  1. what use are ArrayBuffers if they don’t work as expected; and
  2. how to reference raw byte data with proper refreshing of the views?

Failed to connect Firebase services after exiting Firebase Emulators

I’ve follow the instructions on Firebase Emulators Suite’s doc from https://firebase.google.com/docs/emulator-suite/install_and_configure. The Firebase Emulators works, e.g. Auth and Firestore. But when I exit emulators using Ctrl + C, and then try to add new data to Firestore. It doesn’t work. Hope someone can help!

import { initializeApp } from "firebase/app";
import { connectAuthEmulator, getAuth, GoogleAuthProvider } from "firebase/auth";
import {connectFirestoreEmulator, getFirestore} from 'firebase/firestore';

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
export const googleProvider = new GoogleAuthProvider()

connectAuthEmulator(auth,'http://localhost:9099');
connectFirestoreEmulator(db, 'localhost', 8080);

The error message in the console:

firebase_firestore.js?v=b5d4dfea:1775 
POST http://localhost:8080/google.firestore.v1.Firestore/Write/channel?VER=8&database=projects%2Fsalon-project-43926%2Fdatabases%2F(default)&RID=85555&CVER=22&X-HTTP-Session-Id=gsessionid&zx=rqw6jovafaan&t=1 net::ERR_CONNECTION_REFUSED

Have tried reopen the terminal

How to Register Multiple Service Workers in a Shell App for Micro Frontends (MFE) Without CORS Errors?

I’m working on a Micro Frontend (MFE) architecture where each MFE has its own Service Worker. I need to register all these Service Workers in the shell app, but I’m encountering CORS errors even though the CORS headers are correctly set.

Here is the code snippet I’m using to register the Service Workers:

navigator.serviceWorker.register('http://localhost:3000/service-worker1.js', { scope: '/mfe1/' })
  .then(function(registration) {
    console.log('Service Worker for MFE1 registered with scope:', registration.scope);
  })
  .catch(function(error) {
    console.log('Service Worker registration for MFE1 failed:', error);
  });

navigator.serviceWorker.register('http://localhost:3001/service-worker2.js', { scope: '/mfe2/' })
  .then(function(registration) {
    console.log('Service Worker for MFE2 registered with scope:', registration.scope);
  })
  .catch(function(error) {
    console.log('Service Worker registration for MFE2 failed:', error);
  });

The error message I receive is:

SecurityError: Failed to register a ServiceWorker: The origin of the provided scriptURL ('http://localhost:3000') does not match the current origin ('http://localhost:4000').

I understand that Service Workers need to be registered from the same origin for security reasons. However, in an MFE setup, each MFE might be served from different domains or ports. Is there any workaround or method to achieve this while keeping the Service Worker scripts on their respective domains?

Any insights or solutions would be greatly appreciated!