How to allow my Polotno custom editor to edit individual parts of SVG’s

I am making a custom Polotno editor for editing heraldry blazons using React JS and open source Polotno code. In the original Polotno editor they show off on their website (https://studio.polotno.com), uploading an SVG file and selecting it on the canvas allows editing of its individual colors using squares on the top toolbar. In my own custom editor, I am struggling to achieve this.

I am using React js and have also installed Polotno modules with npm install polotno. Currently, I have a working template of the editor that uses local svg files I store on my pc. I can drag them onto the canvas, but the color picking boxes do not appear on the top toolbar even upon clicking them, therefore I cannot edit their colors.

I have read the polotno documentation (https://polotno.com/docs) and used chatGPT to no avail.

here is my src folder

src
│   App.css
│   App.js
│   App.test.js
│   Editor.js
│   index.css
│   index.js
│   logo.svg
│   reportWebVitals.js
│   Sections.js
│   setupTests.js
│
└───utils

Here is App.js

// src/App.js
import React from "react";
import Editor from "./Editor";

function App() {
  return <Editor />;
}

export default App;

Here is editor.js

// src/Editor.js

import React from "react";
import { PolotnoContainer, SidePanelWrap, WorkspaceWrap } from "polotno";
import { Toolbar } from "polotno/toolbar/toolbar";
import { PagesTimeline } from "polotno/pages-timeline";
import { ZoomButtons } from "polotno/toolbar/zoom-buttons";
import { SidePanel } from "polotno/side-panel";
import { Workspace } from "polotno/canvas/workspace";

import { createStore } from "polotno/model/store";

import { sections } from "./Sections"; // <-- Import the organized sections

import "@blueprintjs/core/lib/css/blueprint.css";

const store = createStore({
  key: "YOUR_API_KEY", // go to https://polotno.com/cabinet/ and generate a free API key
  showCredit: true,
});

store.addPage();

const Editor = () => {
  console.log("Custom Tabs loading:");

  return (
    <PolotnoContainer style={{ width: "100vw", height: "100vh" }}>
      <SidePanelWrap>
        <SidePanel store={store} sections={sections} defaultSection="Gabarits" />
      </SidePanelWrap>
      <WorkspaceWrap>
        <Toolbar store={store} downloadButtonEnabled />
        <Workspace store={store} />
        <ZoomButtons store={store} />
        <PagesTimeline store={store} />
      </WorkspaceWrap>
    </PolotnoContainer>
  );
};

export default Editor;

Sections.js contains long code specifying my side pannels, their subsections, and content. It is probably irrelevant. If you require the contents of any other files, I can add them.

I am suspecting that the relevant functionality lies within node-modules/polotno/… as this is where most of the editor’s native imports come from. The folder is huge though, and so far the few files mentioning svgs have been of little help.

Is there some useEffect and useState behavior that explains why only one of these instances work?

I’m using useEffect to fetch data. This data is passed to a state, then used as a prop for a component to render/populate accordingly. Here is the code that works:

const [projects, setProjects] = useState([]);

useEffect(() => {
  const q = query(collection(db, "projects"), orderBy("update", "desc"));
  const unsubscribe = onSnapshot(q, (QuerySnapshot) => {
    const fetched = [];
    QuerySnapshot.forEach((doc) => {
      fetched.push({ ...doc.data(), id: doc.id });
    });
    const sortedProjects = fetched.sort((a, b) => a.update - b.update);
    setProjects(sortedProjects);
  });
  return () => unsubscribe;
}, []);

The code above correctly fetches the data and then passes it to a component that then uses map to display a list of the projects. In terms of streamlining, I wanted to see if I could do the same with the resume data. Here is the code for that:

const [edu, setEducation] = useState([]);

useEffect(() => {
  const q = query(
    collection(db, "resume/resume/education"),
    orderBy("startDate")
  );
  const unsubscribe = onSnapshot(q, (QuerySnapshot) => {
    const fetched = [];
    QuerySnapshot.forEach((doc) => {
      fetched.push({ ...doc.data(), id: doc.id });
    });
    const sortedEdu = fetched.sort(
      (a, b) => a.startDate.nanoseconds - b.startDate.nanoseconds
    );
    setEducation(sortedEdu);
  });
  return () => unsubscribe;
}, []);

This one, for some reason, does not work. I checked that data is being retrieved (it is), and the useEffect and useState appear to be working as they should. I even added a log in to the component, and indeed, the data shows up from that end, but I’m still getting a map error, saying the array is undefined, and preventing the react from rendering. I also know that these components work when I try and input data directly. What could be happening to cause this?

I’ve literally copy/pasted then tweaked values, and still get the same problem. For clarity’s sake, here is the component in question:

export const ResumeItemLister = ({ items, sectionTitle }) => {
  return (
    <div>
      <h2 className="text-xl text-left">{sectionTitle}</h2>
      <hr />
      <table>
        {items.map(({ title, location, date, bullets }) => (
          <tr className="pt-10">
            <div className="grid grid-cols-3">
              <td className="text-left">{date}</td>
              <td className="col-span-2">
                <div className="text-left">
                  {title ? (
                    <p>
                      <bold className="font-bold">{title}</bold>, {location}
                    </p>
                  ) : (
                    <p>{location}</p>
                  )}
                  <ul>
                    {bullets.map((text) => (
                      <li className="list-disc list-inside"> {text}</li>
                    ))}
                  </ul>
                </div>
                <br />
              </td>
            </div>
          </tr>
        ))}
      </table>
    </div>
  );
};

This works, as long as I explicitly set the items in the parent component. Using the fetched state however, I get:

Uncaught TypeError: Cannot read properties of undefined (reading 'map')

I understand this means items is undefined, but as mentioned, that isn’t the case. Any help is deeply appreciated

launchWebAuthFlow in Chrome extension: OAuth2 window and popup close after user clicks username

Chrome extension using manifest v3 with popup written with React.

service-worker.js

async function startJoltAuth(sendResponse, force_refresh = false) {
    var manifest = chrome.runtime.getManifest();
    var scopes = encodeURIComponent(manifest.oauth2.scopes.join(' '));
    var clientId = encodeURIComponent(MyClientId);
    const chromeRuntimeChromium = 'https://' + chrome.runtime.id + '.chromiumapp.org'
    var redirectUri = encodeURIComponent(chromeRuntimeChromium);
    var url = 'https://accounts.google.com/o/oauth2/v2/auth' +
        '?client_id=' + clientId +
        '&response_type=code' +
        '&access_type=offline' +
        '&redirect_uri=' + redirectUri +
        '&scope=' + scopes;
    if (force_refresh) {
        url += '&prompt=consent'
    }
    chrome.identity.launchWebAuthFlow(
        {
            'url': url,
            'interactive': true
        },
        async function (redirectedTo) {
            let message = { type: "auth_tokens" }
            if (chrome.runtime.lastError) {
                // Example: Authorization page could not be loaded, or "user did not approve access"
                message.body = {
                    error: chrome.runtime.lastError.message
                }
                sendResponse(message)
                return;
            }
            const authorizationCode = extractCodeFromRedirectUri(redirectedTo);
            const tokens = await getTokensUsingCode(authorizationCode);
            if (tokens) {
                const expires_in = +(tokens.expires_in ?? (3599 - 5 * 60)) * 1000
                const expire_date = new Date(new Date() - (-expires_in)).toISOString()
                message.body = {
                    access_token: tokens.access_token,
                    refresh_token: tokens.refresh_token,
                    id_token: tokens.id_token,
                    expire_date,
                    login_type: "google"
                }
            } else {
                console.log("Failed to exchange authorization code for tokens.");
                message.body = {
                    error: "Failed to exchange authorization code for tokens."
                }
            }

            sendResponse(message)
        }
    );
}

function extractCodeFromRedirectUri(redirectUrl = "") {
    const params = new URL(redirectUrl).search;
    const tokenParams = new URLSearchParams(params);
    return tokenParams.get("code");
}

async function getTokensUsingCode(code) {
    //fetch and return tokens from 'https://oauth2.googleapis.com/token'
    ...
}

chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {        
        if (request.func == "startJoltAuth") {
            startJoltAuth(sendResponse, request.force_refresh).then(() => {
                // response sent within startJoltAuth already
            })
        }
        // indicates that sendResponse will be used to reply to the sender of the message
        // and that sendResponse was used asynchronously
        return true 
    }
);

auth.js (imported within App.tsx)

async function fullLogin(session_info, force_refresh = false) {
  if (!force_refresh) {
    force_refresh = session_info ? false : true
  }
  const response = await chrome.runtime.sendMessage({
    func: "startJoltAuth", force_refresh
  })
  if (response.body.error) {
    return { error: response.body.error }
  }
  const signupResponse = await signup(response.body)
  ...
  return {
    new_session_info: result,
    prev_session_info: session_info
  }
}

manifest.json: Note that MyChromeExtensionClientId is different from MyClientId because it is registered as a Chrome extension. Chrome store makes me do this, iirc, otherwise I’d use MyClientId.

{
  ...
  "manifest_version": 3,
  "oauth2": {
    "client_id": MyChromeExtensionClientId,
    "scopes": [
      "https://www.googleapis.com/auth/presentations",
      "https://www.googleapis.com/auth/userinfo.email",
      "https://www.googleapis.com/auth/userinfo.profile",
      "openid"
    ]
  },
  "permissions": ["tabs", "identity", "identity.email", "storage"],
  "background": {
    "service_worker": "service_worker.js"
  }
  ...
}

Following this post for guidance, MyClientId is registered as a Web Application instead of a Chrome extension. This is so that launchWebAuthFlow works with Google OAuth2.

When a user opens the extension’s popup (main application) and clicks “Sign up with Google”, the Google OAuth window opens.

Bug:
Sometimes, right when a user clicks a username in the auth window, the auth window closes and the extension closes. What we expect is for them to be prompted to the next screen where they accept the permissions (the flow we are all familiar with in a Google auth flow).

I did a demo for a class of pre-service teachers who all downloaded the extension. About half of them experienced the bug. Most of the users who experienced the bug were eventually able to complete the sign-in, with the max number of attempts being something like 12 (12 times re-opening the extension and trying to sign in). The bug seemed to occur on Mac.

We have been unable to reproduce the bug on anyone’s computer while using “Inspect”.

Extension link:
https://chromewebstore.google.com/detail/jolt-education-test/domhnpgbfcjbnedflfkalafpfjphokfk

Ublock Origin replace-node-text

I’m on a website with an overly sensitive profanity filter that sticks periods in the middle of words like doc.u.ments or as.sas.sin. There is no option to turn off the profanity filter. I wanted to use something like Ublock Origin to remove the periods in the middle of words. I am not sure what the best approach is.

I tried this first:
##+js(replace-node-text, #text, “.”, “”)

Which cleaned up the periods in the middle of words, but it also removed all of the regular end of sentence periods as well.

So then I tried this:
##+js(replace-node-text, #text, /[.][a-z]+/, “”)

but this just turned doc.u.ments into: doc.ments, not what I wanted. I was wondering if anyone had any ideas.

Demuxing an RTP audio stream using WebRTC API

I am developing a voice chat application.
After the server receives an RTP packet from a client, it sends this packet unmodified to all the other clients. Each client has a different SSRC.

The following code works (i.e. all audio streams received from server are played correctly):

function join_vc(offer){
    const rtc_conn = new RTCPeerConnection({
        bundlePolicy: 'max-compat'
    })

    rtc_conn.ontrack = (ev) => {
        const vc_audio = $("#vc_audio")[0]
        vc_audio.srcObject = ev.streams[0]
        vc_audio.play()
    }

    rtc_conn.onicegatheringstatechange = (state) => {
        if(rtc_conn.iceGatheringState === "complete"){
            const answer = rtc_conn.localDescription
            vc_sock.send(JSON.stringify(answer))
        }
    }

    await rtc_conn.setRemoteDescription(offer)

    const media = await navigator.mediaDevices.getUserMedia({audio: true})
    console.log("tracks", await navigator.mediaDevices.enumerateDevices())
    media.getTracks().forEach(track => {rtc_conn.addTrack(track, media)})

    const answer = await rtc_conn.createAnswer()
    await rtc_conn.setLocalDescription(answer)

}

However, the streams are played as one, and I couldn’t find a way to separate them. The RTCPeerConnection instance has a single RTCReceiver, which has a single RTCTransport.

Is there a way to separate multiplexed streams (to enable client-side muting / volume adjustment) by SSRCs using WebRTC API? Re-negotiating all RTCPeerConnections whenever a new participant joins a voice channel seems expensive; keeping separate connections is even more expensive (O(N^2)).

I tried using transformers, but they are not available for Chrome.

React – Updating an object inside an array that’s been filtered

I’m working on this Frontend Mentor challenge and I have most of the functionality down. I can filter the data by it’s isActive property to display the correct items based on whether they’re active or not. What I can’t figure out is how to update an individual item’s isActive status and have it rerender the app upon toggle to move the item into the correct filter.

Here is the code for App.jsx:

   function App() {
  const [data, setData] = useState([]);
  const [filteredData, setFilteredData] = useState(data);
  const [activeFilter, setActiveFilter] = useState("All");

  // Fetch and set the data.
  useEffect(() => {
    fetch("../data.json")
      .then((response) => response.json())
      .then((data) => setData(data))
      .catch((error) => console.error("error", error));
  }, []);

  // Filter the data
  useEffect(() => {
    if (activeFilter === "All") {
      setFilteredData(data);
    } else if (activeFilter === "Active") {
      setFilteredData(data.filter((item) => item.isActive === true));
    } else {
      setFilteredData(data.filter((item) => item.isActive === false));
    }
  }, [activeFilter, data]);

  return (
    <div className="w-full h-full bg-linear-to-b from-[#040918] to-[#091540] py-6 px-3 flex flex-col text-white">
      {/* Header */}
      <Header />

      {/* Options */}
      <Options activeFilter={activeFilter} setActiveFilter={setActiveFilter} />

      {/* Cards */}
      {filteredData &&
        filteredData.map((item) => (
          <Card
            logo={item.logo}
            name={item.name}
            description={item.description}
            isActive={item.isActive}
            key={item.name}
            setFilteredData={setFilteredData}
            filteredData={filteredData}
          />
        ))}
    </div>
  );
}

and here is the Card.jsx with the toggle switch

  const Card = ({
  logo,
  name,
  description,
  isActive,
  filteredData,
  setFilteredData,
}) => {
  const [activeState, setActiveState] = useState(isActive);

  function handleClick() {
    setActiveState(!activeState);
  }

  return (
    <div className="bg-neutral-700 p-4 rounded-xl border border-neutral-600 mt-4">
      <div className="flex items-start gap-4">
        {/* Logo */}
        <img src={logo} alt="Extension Image" />

        {/* Name and Description */}
        <div className="flex flex-col gap-2 mb-8">
          <h2 className="font-semibold text-xl">{name}</h2>
          <p className="text-sm font-light">{description}</p>
        </div>
      </div>

      {/* Remove Button */}
      <div className="flex justify-between items-center">
        <div className="border border-neutral-600 rounded-full px-3 py-1 flex items-center justify-center">
          <button>Remove</button>
        </div>

        {/* Is Active Toggle */}
        <div className="flex items-center justify-center">
          <label
            htmlFor={`${name}Toggle`}
            className="flex items-center cursor-pointer "
          >
            <div className="relative">
              <input
                id={`${name}Toggle`}
                type="checkbox"
                className="sr-only"
                onClick={handleClick}
              />
              <div
                className={`flex items-center ${
                  activeState ? "bg-red-400 " : "bg-gray-600 "
                } w-11 h-6 rounded-full transition-all px-[2px]`}
              >
                <div
                  className={`bg-white w-5 h-5 rounded-full ${
                    activeState ? "translate-x-5" : "translate-x-0"
                  } transition-all`}
                ></div>
              </div>
            </div>
          </label>
        </div>
      </div>
    </div>
  );
};

I was attempting to figure out same way inside the Card’s handleclick function that would match the name of the specific card with the item’s name in filteredData and….flip it’s isActive status? I couldn’t figure how to get that to work and I’m not even sure that’s the best way to do it.

In FE existing screen, I have added field CGT,it’s dropdown yes or no,it has to be no by default,when changed to yes,3 extra fields has to be enabled

I have added old html and javascript code as my organisation uses that, but when I’m hovering on that drop-down option in frontend it’s directly becoming 0, and when I click on yes the enabled fields are also becoming 0.
But I want CGT as default ‘no’ even when I hover, what should I change in my fieldset.suggest.
I have tried using strict, position: absolute, selected,but not able to bypass the error

How to style unselected line points in Mapbox draw gl?

Initially, when drawing a line in Mapbox GL Draw, the vertex points are only visible when the line is selected. However, I want the points to remain visible even when the line is unselected, and to style them based on a condition (a custom user property) — for example, making them red or blue. I have already figured out how to apply styling to all points.

  {
    id: "gl-draw-all-points",
    type: "circle",
    paint: {
      "circle-color": ["case", ["==", ["get", "special"], "true"], red, blue],
      "circle-radius": 4,
      "circle-stroke-width": 2,
      "circle-stroke-color": white,
    },
  },

Currently, I don’t apply a filter to my layer, which allows me to style all circles (vertices) globally. However, the problem is that I need to style the points conditionally. With my current setup, the points always appear blue, even when some vertices have the special property set.
Meanwhile, I have another styling approach that correctly applies conditional styling based on properties.

  {
    id: "gl-draw-waypoints",
    type: "circle",
    filter: ["all", ["==", "$type", "Point"], ["==", "meta", "vertex"]],
    paint: {
      "circle-color": ["case", ["==", ["get", "special"], "true"], red, blue],
      "circle-radius": ["case", ["==", ["get", "special"], "true"], 8, 5],
      "circle-stroke-color": white,
      "circle-stroke-width": 2,
    },
  },

How can I eliminate One drive picker v8 button error AADSTS900023?

I’m attempting to implement changes as suggested by this post to use the One Drive JS picker with SDK v8 but having one button/link instead of two. I’ve run into multiple errors, and currently seeing this error:

invalid_request: 900023 – [2025-04-23 20:55:37Z]: AADSTS900023: Specified tenant identifier ‘consumer’ is neither a valid DNS name, nor a valid external domain. Trace ID: b139cb9f-e16b-4a40-b3ec-1c4e1c2c2100

Honestly this feels like a big game of whack-a-mole… -_-. Has anyone been successful in loading the One drive picker for both consumer and organization accounts?

Below is the script used to handle clicks on the picker button (rendered from a Laravel Blade PHP template – so sections like {!! !!} will be replaced with a value):

const baseUrl = "https://onedrive.live.com/picker";
    const msalConsumerAuthority = 'https://login.microsoftonline.com/consumer';
    const msalOrgAuthority = 'https://login.microsoftonline.com/common';
    let currentUser;
    let type = 'org';
    const delegateId = '{!! $delegateId !!}';
    const msalParams = {
        auth: {
            authority: "https://login.microsoftonline.com/common",
            clientId: "{!! $CLIENT_ID !!}",
            redirectUri: `${window.location.origin}${window.location.pathname}`
        },
    }

    const instance = new msal.PublicClientApplication(msalParams);
    const scopes = ['.default']; // 'https://graph.microsoft.com/.default'
    const oneDriveConsumerScopes = ['OneDrive.ReadWrite'];
    const oneDriveOrgScopes = ['.default']; // 'https://graph.microsoft.com/.default'
    // For personal accounts this will always be the tid. Refer https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference
    function checkPersonalAccount({ idTokenClaims: { tid } }) {
        return tid === "9188040d-6c67-4c5b-b112-36a304b66dad";
    }

    function checkOrgAccount({ idTokenClaims: { tid } }) {
        return tid !== "9188040d-6c67-4c5b-b112-36a304b66dad";
    }
    /**
     * Combines an arbitrary set of paths ensuring and normalising the slashes
     * This is used for getting scopes based on the resource type in Onedrive Picker
     * @param paths 0 to n path parts to combine
     */
    function combine(...paths) {
        return paths
            .map((path) => path.replace(/^[\|/]/, "").replace(/[\|/]$/, ""))
            .join("/")
            .replace(/\/g, "/");
    }

    /**
     *
     * @param {Object} Properties
     * @param {import("@azure/msal-browser").IPublicClientinstancelication} Properties.instance MSAL Instance to use
     * @param {string[]} Properties.scopes Scopes to ask for in the token. This is overridden when type and resource are passed
     * @param {import("@azure/msal-browser").AccountInfo} Properties.currentUser User Account to get the access token for
     * @param {string} Properties.Authority URL to use for OAuth
     * @param {string | undefined} Properties.type Which Type of Resource to fetch from, this is only used in the onedrive file picker event listener. This will be used to get access tokens according to the resource
     * @param {string | undefined} Properties.resource Which Resource to scope for, this is only used in the one drive file picker event listener.
     * @returns {Promise<string>} Access Token for the particular scope
     */
    async function getOneDriveAccessToken({
                                                     currentUser,
                                                     instance,
                                                     scopes,
                                                     authority,
                                                     type,
                                                     resource,
                                                 }) {
        let accessToken = "";
        let currentScopes = scopes;
        console.log('getOneDriveAccessToken() - type:', type, 'resource: ', resource, ' scopes: ', scopes, ' authority: ', authority);
        switch (type) {
            case "SharePoint":
            case "SharePoint_SelfIssued":
                currentScopes = [`${combine(resource, ".default")}`];
                break;
            default:
                break;
        }
        const popupOptions = {
            scopes: currentScopes,
            authority: type === 'org' ? msalOrgAuthority : msalConsumerAuthority
        };
        if (currentUser) {
            popupOptions.account = currentUser
        }
        try {
            console.log('calling instance.acquireTokenSilent() with scopes: ', currentScopes);
            // See if we have already the id token saved
            const resp = await instance.acquireTokenSilent(popupOptions);
            console.log('setting active account after callling acquireTokenSilent(): ', resp.account);
            instance.setActiveAccount(resp.account);
            accessToken = resp.accessToken;
        } catch (e) {
            console.log(' getOneDriveAccessToken() - caught e:', e);
            if (e.message.includes('invalid_grant') || e.message.includes('no_account_error')) {
                console.log('calling acquireTokenPopup()  with options: ', popupOptions, 'type: ', type);
                return instance.acquireTokenPopup(popupOptions);
            } else {
                throw e;
            }
        }
        return accessToken;
    }
    function checkUser(data) {
        console.log('checkUser() - data: ', data);
        const personalAccount = instance.getAllAccounts().find(checkPersonalAccount);
        const orgAccount = instance.getAllAccounts().find(checkOrgAccount);
        currentUser = personalAccount || orgAccount;
        console.log('checkUser() - personalAccount: ', personalAccount, ' orgAccount: ', orgAccount);
        if (personalAccount) {
            type = 'personal';
            scopes.length = 0;
            scopes.push('OneDrive.ReadOnly');
        } else {
            if (data?.resource) {
                scopes.length = 0;
                oneDriveOrgScopes.length = 0;
                scopes.push(`${combine(data.resource, ".default")}`);
                oneDriveOrgScopes.push(`${combine(data.resource, ".default")}`);
                console.log('pushed combined scope to scopes', scopes);
            } else {
                scopes.push('Files.Read.All', 'Files.Read', 'User.Read');
                console.log('pushed default files scopes to scopes');
            }
        }
    }

    // the options we pass to the picker page through the querystring
    const oneDrivePickerOptions = {
        sdk: "8.0",
        entry: {},
        authentication: {},
        // prompt: 'consent',
        messaging: {
            origin: window.location.href,
            channelId: "27"
        },
        selection: {
            mode: 'multiple',
            enablePersistence: true
        },
        typesAndSources: {
            mode: "files",
            pivots: {
                oneDrive: true,
                recent: true,
            },
        },
    };

    const onedrivePortEventListener = ({ port, oneDriveWindow, type }) => async (message) => {
        console.log('onedrivePortEventListener() - message:', message, ' type: ', type);
        switch (message.data.type) {
            case "notification":
                break;
            case "command": {
                port.postMessage({
                    type: "acknowledge",
                    id: message.data.id,
                });
                const {
                    command,
                    items, // This is the files picked from the picker
                    type: commandType,
                    resource,
                } = message.data.data;
                if (command === 'authenticate') {
                    checkUser(message.data?.data);
                }
                // This is the place, Where the documentation missed out on a key detail but it will be used in their sample codes. They don't explain why it is needed.
                const tokenOptions =
                    type === "personal"
                        ? {
                            scopes: oneDriveConsumerScopes,
                            authority: msalConsumerAuthority,
                            currentUser,
                            instance,
                        }
                        : {
                            scopes: oneDriveOrgScopes,
                            authority: msalOrgAuthority,
                            currentUser,
                            instance,
                            type: commandType, // In the getOneDriveAccessToken, you would have seen one switch statement based on type. For tenant users we can't use the same resource rather the picker emits this resource and their access type for that we have to get an access token.
                            resource,
                        };
                switch (command) {
                    case "authenticate": {
                        // Based on the token options above, we can send the token to the picker
                        const token = await getOneDriveAccessToken(tokenOptions);
                        console.log('authenticated command - token: ', token);
                        if (token != null) {
                            port.postMessage({
                                type: "result",
                                id: message.data.id,
                                data: {
                                    result: "token",
                                    token,
                                },
                            });
                        } else {
                            console.log(`Could not get auth token for command: ${command}`);
                        }
                        break;
                    }

                    case "close":
                        oneDriveWindow.close();
                        break;

                    case "pick": {
                        // You can use the items from message.data.data and get the files picked by the users.
                        port.postMessage({
                            type: "result",
                            id: message.data.id,
                            data: {
                                result: "success",
                            },
                        });
                        oneDriveWindow.close();
                        break;
                    }

                    default:
                        port.postMessage({
                            result: "error",
                            error: {
                                code: "unsupportedCommand",
                                message: command,
                            },
                            isExpected: true,
                        });
                        break;
                }
                break;
            }
            default:
                break;
        }
    };
    let oneDriveWindow = null;
    let port = null;

    async function launchPicker(e) {
        //const authToken = await getAuthToken();
        oneDriveWindow = window.open("", "Picker", "width=800,height=600")
        const authToken = await getOneDriveAccessToken({
            instance,
            currentUser,
            type,
            authority: msalOrgAuthority, //msalConsumerAuthority,
            scopes: ['User.Read'] //oneDriveOrgScopes //oneDriveConsumerScopes,
        }); //*/
        const queryString = new URLSearchParams({
            filePicker: JSON.stringify({
                ...oneDrivePickerOptions,
                entry: { // See the entry difference for org
                    oneDrive: {
                        files: {},
                    },
                },
            }),
            locale: "en-us",
        });

        const url = `${baseUrl}?${queryString}`;

        const form = oneDriveWindow.document.createElement("form");
        form.setAttribute("action", url);
        form.setAttribute("method", "POST");
        oneDriveWindow.document.body.append(form);

        const input = oneDriveWindow.document.createElement("input");
        input.setAttribute("type", "hidden")
        input.setAttribute("name", "access_token");
        input.setAttribute("value", authToken);
        form.appendChild(input);
console.log('launchPicker() - submitting form');
        form.submit();
        sendMessageToParentWindow({oneDrivePickerOpened: true});

        window.addEventListener("message", (event) => {
            console.log('message received: ', event);
            if (event.source && event.source === oneDriveWindow) {
                const message = event.data;
                if (message.type === "initialize" && message.channelId === oneDrivePickerOptions.messaging.channelId) {
                    port = event.ports[0];
                    port.addEventListener("message", onedrivePortEventListener({port, oneDriveWindow, type}));
                    port.start();
                    port.postMessage({
                        type: "activate",
                    });
                }
            }
        });
        e.preventDefault();
    }
    function sendMessageToParentWindow(message) {
        if (window.parent != window) {
            if (delegateId) {
                message.delegateId = delegateId;
            }
            window.parent.postMessage(message, '*');
        }
    }
    window.addEventListener('DOMContentLoaded', function() {
        document.addEventListener('click', launchPicker);
    });

TypeError: Cannot read properties of undefined (reading ‘id’) when using createAdapter() from @socket.io/cluster-adapter

I’m trying to set up Socket.IO with cluster support using PM2, following this docs.

Here’s my code:

import { createAdapter } from "@socket.io/redis-adapter";
import { createAdapter as createClusterAdapter } from "@socket.io/cluster-adapter";
import { setupWorker } from "@socket.io/sticky";

const app = express();
export const server = http.createServer(app);
export const io = new Server(server, {
    transports: ['websocket', 'polling'],
    adapter: createAdapter(redisClient, redisSubClient)
});

io.adapter(createClusterAdapter()); // src/server.ts:76:4
setupWorker(io);

While I try to run my code, it showing this error

TypeError: Cannot read properties of undefined (reading 'id')
    at new ClusterAdapter (/home/michioxd/backend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@socket.io/cluster-adapter/dist/index.js:61:34)
    at new <anonymous> (/home/michioxd/backend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@socket.io/cluster-adapter/dist/index.js:40:16)
    at Namespace._initAdapter (/home/michioxd/backend/node_modules/.pnpm/[email protected]/node_modules/socket.io/dist/namespace.js:99:24)
    at Server.adapter (/home/michioxd/backend/node_modules/.pnpm/[email protected]/node_modules/socket.io/dist/index.js:188:17)
    at file:///home/michioxd/src/server.ts:76:4
    at ModuleJob.run (node:internal/modules/esm/module_job:274:25)
    at onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:644:26)

I’m running the server using PM2 with the following command:

pm2 start index.js -i max

Why does this error occur? Am I using createAdapter() incorrectly? How should I properly set up Socket.IO with cluster support and PM2?

Show different tabs when selecting in dropdown

I want to display different tab based on the selected item. This is working before when I am using buttons but when I changed it to dropdown menu it stopped working.

HTML:

<option class="tab">
        <select class="tablinks" onclick="openCity(event, 'AT3')" id="defaultOpen">Transatlantic 3</select>
        <select class="tablinks" onclick="openCity(event, 'AT4')">Transatlantic 4</select>
         <select class="tablinks" onclick="openCity(event, 'AL5')">Transatlantic 5</select>
      </option>

            <div id="AT3" class="tabcontent">
                <img src="images/AT3 (via CoGH)_20250118.jpg" class="service-map">
            </div>

            <div id="AT4" class="tabcontent">
                <img src="images/AT4 (via CoGH)_20250118.jpg" class="service-map">
            </div>

            <div id="AL5" class="tabcontent">
                <img src="images/AL5 (via CoGH)_20250118.jpg" class="service-map">
            </div>

JS:

    function openCity(evt, cityName) {
  var i, tabcontent, tablinks;
  tabcontent = document.getElementsByClassName("tabcontent");
  for (i = 0; i < tabcontent.length; i++) {
    tabcontent[i].style.display = "none";
  }
  tablinks = document.getElementsByClassName("tablinks");
  for (i = 0; i < tablinks.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" active", "");
  }
  document.getElementById(cityName).style.display = "block";
  evt.currentTarget.className += " active";
}
document.getElementById("defaultOpen").click();

Authorization for NextJS Application

I am a newbie in Next.JS & I want to integrate Authentication & Authorization in my Application. FYI for now the project structure is like this –

├── .next
├── node_modules
├── public
├── src
│   ├── Components
│   │   └── LandingPage
│   │   │   ├── LoginForm.jsx
│   │   │   └── LoginForm.module.css
│   │   └── DashBoard
│   │       ├── DashBoard.jsx
│   │       └── DashBoard.module.css
│   ├── pages
│   │   ├── index.jsx
│   │   ├── _app.jsx
│   │   ├── _document.jsx
│   │   └── dashboard
│   │       └── index.jsx
│   └── styles
│       └── globals.css
├── .env
├── .gitignore
├── eslint.config.mjs
├── jsconfig.json
├── next.config.mjs
├── package-lock.json
├── package.json
└── README.md

& I have two APIs for the Authorization & Authentication. My task is to use JWT token for Authentication – role & session ID for Authorization

This is the API response from POSTMAN for login –

{
    "success": true,
    "message": "Logged in successfully",
    "data": {
        "access_token": "JWT token", //hiding actual token for security purpose
        "user_info": {
            "user_id": 1000,
            "username": "pritam",
            "role": "1000",
            "last_login": "2025-04-28T10:35:59.578Z",
            "created_at": "2024-12-18T09:19:29.000Z"
        }
    }
}

This is the API response from POSTMAN for session using bearer token –

{
    "success": true,
    "message": "Session active.",
    "sid": "E-lqAr78APRFyNDFPagBOnPkofwJeilZ",
    "cookies": {
        "parsed": {
            "connect.sid": "s:E-lqAr78APRFyNDFPagBOnPkofwJeilZ.ZeRm/rjOjxerh/6x7ltsU980Fo5osr9VSWc2UoABnlk"
        },
        "raw": "connect.sid=s%3AE-lqAr78APRFyNDFPagBOnPkofwJeilZ.ZeRm%2FrjOjxerh%2F6x7ltsU980Fo5osr9VSWc2UoABnlk"
    },
    "data": {
        "id": 1000,
        "username": "pritam",
        "role": "1000"
    }
}

Now I wrote this function for login form submit –

  const handleLogin = async () => {
  
    try {
      const payload = {
        username: formData.username,
        password: formData.password,
      };
  
      const res = await axios.post(
        'http://localhost:3000/api/v1/login/superadmin',
        payload,
        {
          headers: {
            'Content-Type': 'application/json'
          }
        }
      );
  
      if(res.data.success) {
        const { access_token, user_info } = res.data.data;
        Cookies.set('access_token', access_token, { expires: 1, secure: true, sameSite: 'Strict' });
        Cookies.set('role', user_info.role, { expires: 1, secure: true, sameSite: 'Strict' });

        router.replace('/dashboard');
      }
      else {
        toast.error('Login failed. Please check credentials.', {
          position: 'top-right',
          autoClose: 3000,
        });
      }

    } catch (err) {
      toast.error('Login failed. Please check credentials.', {
        position: 'top-right',
        autoClose: 3000,
      });
    }
  };

Now what I planned is that I’ll create a middleware & every time react route in a different page (except ‘/’ this route which is by default login form landing page), it will trigger a useEffect that will validate if cookies data have the correct role which is role === 1000 & the login user’s session is valid or not. If both are false then automatically route to login page (again login for a new token or role is not authorized to access the webpages) & if both yes then only the route will allow. So this way we can protect every route calling in the application. Now I have done this _app.jsx code for this logic –

_app.jsx –

function MyApp({ Component, pageProps }) {
  const router = useRouter();

  useEffect(() => {
    const checkSession = async () => {

      // Skip session check on login page ('/') 
      if (router.pathname === '/') {
        return;
      }

      const token = Cookies.get('access_token');
      const role = Cookies.get('role');

      // If token or role missing or invalid, redirect to login
      if (!token || role !== '1000') {
        router.replace('/');
        toast.error('You do not have Authentication to access this page, Please Login First !!!', {
          position: 'top-right',
          autoClose: 4000,
        });
        return;
      }

      try {
        console.log(token);
        const res = await axios.get('http://localhost:3000/api/v1/check-session/superadmin', {
          headers: {
            Authorization: `Bearer ${token}`,
          },
          withCredentials: true
        });

        if (res.data.success) {
          console.log("response",res);
          Cookies.set('session_id', res.data.sid, { expires: 1, secure: true, sameSite: 'Strict' });
          // router.replace('/dashboard');
        } else {
          toast.error('You do not have Authentication to access this page, Please Login First !!!', {
            position: 'top-right',
            autoClose: 4000,
          });
          router.replace('/');
        }
      } catch (error) {
        toast.error('Network error !!! Please try again...', {
          position: 'top-right',
          autoClose: 2000,
        });
        router.replace('/');
      }
    };

    checkSession();
  }, [router.pathname]); // runs whenever route changes

  return (
    <>
      <Component {...pageProps} />
      <ToastContainer />
    </>
  );
}

But this is not working. I am having a CORS error which is this –

Access to XMLHttpRequest at
‘http://localhost:3000/api/v1/check-session/superadmin’ from origin
‘http://localhost:3030’ has been blocked by CORS policy: Response to
preflight request doesn’t pass access control check: The value of the
‘Access-Control-Allow-Origin’ header in the response must not be the
wildcard ‘*’ when the request’s credentials mode is ‘include’. The
credentials mode of requests initiated by the XMLHttpRequest is
controlled by the withCredentials attribute.Understand this error
C:UsersTitas
SahaDocumentsdevLIMS-Super-Admin-Frontendsrcpages_app.jsx:37

         GET http://localhost:3000/api/v1/check-session/superadmin net::ERR_FAILED

I did some R&D about this & found that this is common issue when dealing with cookie data for security purpose. I made changes into the backend server.js code, but still facing this issue.

I have changed this line –
from this >

app.use(cors('*'));
app.use((req, res, next) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    next();
});

To this >

app.use(cors({
  origin: 'http://localhost:3030',
  credentials: true,
}));

app.options('*', cors({
  origin: 'http://localhost:3030',
  credentials: true,
}));

But in the end I give up on this & can’t fix. IDK whether I am doing wrong logic or is there something else that I don’t know about. Can anyone help me with this ?

How to access the DOM of an CORS ?

I want to access the document of an iframe that is loaded from a different domain than the main page.
I know that this is normally impossible due to browser security policies, but I found some workarounds using Puppeteer to interact with the iframe like a real user.
However, I want to go beyond simply interacting — I want to actually access the iframe’s DOM.

My idea was: Start Puppeteer with DevTools open, then simulate clicking inside the iframe using Puppeteer’s mouse actions.
This would trigger Chrome’s behavior where DevTools automatically switches context to the iframe (as it normally does when you manually select an element inside an iframe using the inspect tool).
After that, I should be able to use page.evaluate() or a similar function to access the iframe’s DOM directly.

Is there a way to implement this idea using Puppeteer?

How to fix manual input for datepicker

I am trying to use the primevue datepicker in a form submission form. I am using zod as schema validation.
The problem is, I cannot enter the date by hand. When I do this, a day already appears and add onto the date I want to enter.

<template>

      <Form
        v-slot="$form"
        :initialValues="initialValues"
        :resolver="resolver"
        :validateOnValueUpdate="true"
        :validateOnBlur="false"
        @submit="onFormSubmit"
      >

            <DatePicker
              aria-label="Startdatum"
              inputId="start_date"
              id="start_date"
              v-model="initialValues.eventStartDate"
              name="start_date"
              placeholder="Tag/Monat/Jahr"
              :minDate="currentDate"
              dateFormat="dd/mm/yy"
              showIcon
              fluid
              manualInput
              :showOnFocus="true"
            />
      </Form>
</template>

<script setup lang="ts">

const initialValues = ref<FormValues>({
  start_date: null,
});

interface FormValues {
  start_date: null;
}

const formSchema = z
  .object({
       start_date: z
      .union([
        z
          .string()
          .regex(/^d{2}/d{2}/d{4}$/, "Das eingegebene Datum ist ungültig.")
          .refine((value) => value !== null, "Enddatum ist erforderlich."),
        z.date(),
        z.null(),
      ])
      .refine((value) => value !== null, "Die Anfangszeit ist erforderlich."), // Stellt sicher, dass es nicht null bleibt

</script>

When I remove the “name” or “format” attribute, I am able to enter a date by myself without any trouble. So I feel it has something to do with the validaiton form.