Join objects into 1 object [closed]

I have response from api that looks like this:

I’m trying to join all responses in one response. (due limits)

{
   list1: {
     'something1': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something2': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something3': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
    ...
   }
}

Then i receive another list which looks same but with different elements.

{
   list2: {
     'something4': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something5': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something6': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     }
   }
}

How to merge them to something like this?

{
   list1: {
     'something1': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something2': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something3': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something4': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something5': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
     'something6': {
       data1: [Object],
       data2: [Object],
       data3: [Object],
       data4: [Object]
     },
   }
}

I understand how to do that with normal arrays, but this..

Never thought i stuck on something like this

something is wrong when i try to use redux-toolkit

userSlice.js:22 Uncaught (in promise) TypeError: Cannot create property ‘error’ on string ‘User created successfully!’ at signInFailure (userSlice.js:22:19) at @reduxjs_toolkit.js?v=69e8a274:1773:26

#reducers for make requests to api in mern app

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  currentUser: null,
  error: null,
  loading: false,
};

const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {
    signInStart: (state) => {
      state.loading = true;
    },
    signInSuccess: (state, action) => {
      state.currentUser = action.payload;
      state.loading = false;
      state.error = null;
    },
    signInFailure: (state, action) => {
      state.error = action.payload; // Handle both object and string payloads
      state.loading = false;
    },

});

export const {
  signInStart,
  signInSuccess,
  signInFailure,

  signOutUserStart,
} = userSlice.actions;

export default userSlice.reducer;

i try make the payload is string but it not solve the broblem !

signInFailure: (state, action) => {  

  state.error = typeof action.payload === 'string' ? action.payload : 'An error occurred';  
  state.loading = false; 

Why is my setstate function causing other functions to malfunction in react.js?

const [lap, setLap] = useState([]);

function start(){
       if(!isStart){
            starting = Date.now() - elapsed;
            isStart = true;
            timer = setInterval(update, 1000);  
            console.log("timer is activated");
    }
}
 function stop(){
        if(isStart){
            isStart = false;
            clearInterval(timer);
    }
}
function reset(){
        isStart = false;
        elapsed = 0;
        document.getElementById("time").textContent = "00:00:00";
        clearInterval(timer);
        starting = 0;
    }
}
function update(){
        var curr = Date.now();
        elapsed = curr - starting;
        var min = Math.floor(elapsed/(1000*60));
        var sec = Math.floor((elapsed/1000)%60);
        var msec = Math.floor((elapsed%1000)/10);
        var time = [min, sec, msec]
        time = time.map((element) => pad(element));
        var time_str = time[0] + ":" + time[1] + ":" + time[2];
        document.getElementById("time").textContent = time_str;
        console.log(time_str)
    }
const lap_update = (val) => {
        setLap(lap => [...lap, val]);
    }
<div id="lap" onClick={() => lap_update(document.getElementById("time").textContent)}>LAP</div>

The above are the code snippets for a Stopwatch is lap functionality.

Unfortunately, my lap_update function when clicked, is causing the start/stop/reset functions to malfunction.

(Stop function and reset function not working, and start function is double rendering).

I really am not able to understand why, and what changes are needed in the setLap statment.

Please help!

Trying to figure out the issue with the setLap statement which is causing the other functions to malfunction (till Lap function is called, the program is running fine).

I cant do authorization with NEXTJS 14 and Apollo

I’m new with full stack approach and my fault can be silly and maybe its just about js syntax something.

I’m confused about how I cant get it. the “req.headers.authorization” came undefined but its not undefined that u can see with the console log on the below

Please help me

This is my code to authorization and also console.log on the below

import { ApolloServer } from "@apollo/server";
import { startServerAndCreateNextHandler } from "@as-integrations/next";
import { typeDefs } from "@/graphql/schema";
import { resolvers } from "@/graphql/resolvers";
import jwt from "jsonwebtoken";

const apolloServer = new ApolloServer({ typeDefs, resolvers });

const getUserFromToken = (token: string | undefined | null) => {
  try {
    if (token) {
      return jwt.verify(token.substring(7), process.env.JWT_SECRET as string);
    }
    return null;
  } catch (error) {
    return null;
  }
};

export const config = {
  api: {
    bodyParser: false,
  },
};

const handler = startServerAndCreateNextHandler(apolloServer, {
  context: async (req) => {
    console.log("Request Headers:", req.headers);

    const token = req ? req.headers.authorization : null;

    console.log("Token:", token);

    const user = getUserFromToken(token);
    console.log("User:", user);

    return { user };
  },
});

export { handler as GET, handler as POST }; ```

********
CONSOLE
********

``` Request Headers: _HeadersList {

cookies: null,

[Symbol(headers map)]: Map(23) {

'accept' => { name: 'accept', value: '*/*' },

'accept-encoding' => { name: 'accept-encoding', value: 'gzip, deflate, br, zstd' },

'accept-language' => {

name: 'accept-language',

value: 'tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7'

},

'authorization' => {

name: 'authorization',

value: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ODA0NjFmNmM2ZDlkNTM5ZGUwOTEwYiIsInVzZXJuYW1lIjoibWF2aW5lc2UiLCJmdWxsbmFtZSI6Ik1hdmkgTmVzZSIsImlhdCI6MTcyMjc3MjI1NywiZXhwIjoxNzIyODU4NjU3fQ.zDP46i46WV5Ue8OKPeTebI5XUywYA7H8RZocr_f1Rc0'

},

'cache-control' => { name: 'cache-control', value: 'no-cache' },

'connection' => { name: 'connection', value: 'keep-alive' },

'content-length' => { name: 'content-length', value: '321' },

'content-type' => { name: 'content-type', value: 'application/json' },

'host' => { name: 'host', value: 'localhost:3001' },

'origin' => { name: 'origin', value: 'http://localhost:3001' },

'pragma' => { name: 'pragma', value: 'no-cache' },

'referer' => { name: 'referer', value: 'http://localhost:3001/admin/login' },

'sec-ch-ua' => {

name: 'sec-ch-ua',

value: '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"'

},

'sec-ch-ua-mobile' => { name: 'sec-ch-ua-mobile', value: '?0' },

'sec-ch-ua-platform' => { name: 'sec-ch-ua-platform', value: '"macOS"' },

'sec-fetch-dest' => { name: 'sec-fetch-dest', value: 'empty' },

'sec-fetch-mode' => { name: 'sec-fetch-mode', value: 'cors' },

'sec-fetch-site' => { name: 'sec-fetch-site', value: 'same-origin' },

'user-agent' => {

name: 'user-agent',

value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'

},

'x-forwarded-for' => { name: 'x-forwarded-for', value: '::1' },

'x-forwarded-host' => { name: 'x-forwarded-host', value: 'localhost:3001' },

'x-forwarded-port' => { name: 'x-forwarded-port', value: '3001' },

'x-forwarded-proto' => { name: 'x-forwarded-proto', value: 'http' }

},

[Symbol(headers map sorted)]: [

[ 'accept', '*/*' ],

[ 'accept-encoding', 'gzip, deflate, br, zstd' ],

[ 'accept-language', 'tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7' ],

[

'authorization',

'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ODA0NjFmNmM2ZDlkNTM5ZGUwOTEwYiIsInVzZXJuYW1lIjoibWF2aW5lc2UiLCJmdWxsbmFtZSI6Ik1hdmkgTmVzZSIsImlhdCI6MTcyMjc3MjI1NywiZXhwIjoxNzIyODU4NjU3fQ.zDP46i46WV5Ue8OKPeTebI5XUywYA7H8RZocr_f1Rc0'

],

[ 'cache-control', 'no-cache' ],

[ 'connection', 'keep-alive' ],

[ 'content-length', '321' ],

[ 'content-type', 'application/json' ],

[ 'host', 'localhost:3001' ],

[ 'origin', 'http://localhost:3001' ],

[ 'pragma', 'no-cache' ],

[ 'referer', 'http://localhost:3001/admin/login' ],

[

'sec-ch-ua',

'"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"'

],

[ 'sec-ch-ua-mobile', '?0' ],

[ 'sec-ch-ua-platform', '"macOS"' ],

[ 'sec-fetch-dest', 'empty' ],

[ 'sec-fetch-mode', 'cors' ],

[ 'sec-fetch-site', 'same-origin' ],

[

'user-agent',

'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'

],

[ 'x-forwarded-for', '::1' ],

[ 'x-forwarded-host', 'localhost:3001' ],

[ 'x-forwarded-port', '3001' ],

[ 'x-forwarded-proto', 'http' ]

]

}

Token: undefined

User: null ```

Why onended event of MediaTrack is not fired?

I need to add mp3-file audio track to RtcPeerConnection.
This work fine, local computer loads mp3-file, converts it to blob,
creates Audio() object, a stream, sends it track[0], a peer listens it.

Also I need to capture onendedevent of the added track, but it is not fired.
However, the same event of the created Audio is captured normally – see comments in my code.

The question is – why newAudio onended is captured and newTrackToAdd‘s is not ?

My code is:

try {
    // blobURL - is the data of loaded mp3-file
    let newAudio = new Audio(blobURL);                   // CREATED OK
    newAudio.autoplay = true;

    newAudio.addEventListener("play", (e) => {
        DisplayEventMessage("play: ", "NewAudio");       // THIS EVENT IS FIRED - OK

    });

    
    newAudio.addEventListener("ended", (e) => {
        DisplayEventMessage("ended: ", "NewAudio");       // THIS EVENT IS FIRED - OK
        // IT IS FIRED !!!
    });
    
    // create a stream to pass mp3-file 
    const ctxAudio = new (window.AudioContext || window.webkitAudioContext)();
    const streamDest = ctxAudio.createMediaStreamDestination();
    const streamSource = ctxAudio.createMediaElementSource(newAudio);
    streamSource.connect(streamDest);
    const newStream = streamDest.stream;


    console.log("--- newStream ---");
    console.log(newStream);

    const addAudioTrack = true;


        allTracks = newStream.getAudioTracks();
………

            // get a track to add
            let newTrackToAdd = allTracks[0];

            newTrackToAdd.onended = function(e) {

                alert("onended!");                             // IT IS NOT FIRED !!! WHY ???
                DisplayEventMessage("onended: ", "newTrackToAdd");
            };

            // add the track to webrtc - it works, a peer listens the mp3-file
            rtc_addExtraTrack(curWebRtc, newTrackToAdd);

How to make dynamic width height iframe for chatbots

So i am building a chatbot that i want to integrate into the websites. What i want is an iframe code that can be copy pasted into any website and it enables the chatbot.

Chatbot is being built in Reactjs and Tailwindcss.

Problem:
When i integrate a chatbot using iframe on website, i get white background and a fixed height, width window. What i want is a chatbot icon, when clicked should open whole chatbot box.
enter image description here

iframe code:

<iframe 
      src='http://localhost:5173/'
      title='iframe'
      allowTransparency='true'
      allowFullScreen='true'
      style={{
        border: 'none',
        position: 'fixed',
        bottom: '5px',
        right: '5px',
        zIndex: '9999',
      }}
/>

my Reactjs Code:

import Button from './components/button/Button'
import useStore from './store/States'
import Chat from './components/chat/Chat'


export default function Bot() {
    const isOpened = useStore((state:any) => state.isOpened)
    return (
        <div className='bg-red-200'>
            {isOpened ? <Chat /> : null}
            <Button />
        </div>
    )
}

Please help me let me know what should be fixed so it starts behaving like other chatbots we see.

I tried removing mutiple divs.
I tried adjust iframe width and height

why npm version doesn’t work on terminal?

C:Usersraffi> node -v
v20.16.0

C:Usersraffi>npm -v
'CALL "C:Program Filesnodejs\node.exe" "C:Program Filesnodejs\node_modulesnpmbinnpm-prefix.js"' n’est pas reconnu en tant que commande interne
ou externe, un programme exécutable ou un fichier de commandes.

I wanted to check the npm version for my react project on my terminal but it is excecuting me a message error.

In the deployment environment revalidation request does not work in ‘/’ page

There is a component on the ‘/’ path that displays products, and the request is sent as follows:

const getRecentProducts = async () => {
  try {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
    const res = await fetch(`${baseUrl}/api/products/recent`, {
      next: { tags: ['products'], revalidate: 10 },
    });
    if (!res.ok) {
      console.error('API request failed:', res.status, res.statusText);
      throw new Error('Failed to fetch products');
    }
    const data = await res.json();
    console.log('HELLO = ', data);
    return data;
  } catch (error) {
    console.error('Error occurred while executing getRecentProducts function:', error);
    throw error;
  }
};

export default getRecentProducts;

Additionally, on the ‘/sell’ page, the API makes the following revalidation requests:

export async function POST(req) {
    revalidateTag('products');
    revalidatePath('/');
    revalidatePath('/shop');
    revalidatePath(`${process.env.NEXT_PUBLIC_BASE_URL}/`);
    revalidatePath(`${process.env.NEXT_PUBLIC_BASE_URL}/shop`);
}

At first, I tried only revalidateTag(‘products’); but the revalidation request did not work. Then I added revalidatePath(‘/’); and revalidatePath(‘/shop’);, and when that didn’t work, I added revalidatePath(${process.env.NEXTAUTH_URL}/); and revalidatePath(${process.env.NEXTAUTH_URL}/shop);.

Despite these attempts, the ‘/’ page data remains the same as it was during the build. The revalidate: 10 revalidation option also does not work.

Even router.refresh() is not working on the ‘/’ page, while it works fine on other pages.

However, everything works fine in the local development environment. In the deployment environment using
next run build and next run start
revalidation does not occur no matter what.

Additionally, we are using React Query, but even with staleTime = 0, it always fetches the previous data in CSR. This issue also occurs only on the ‘/’ page.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.6.0: Fri Jul  5 17:56:41 PDT 2024; root:xnu-10063.141.1~2/RELEASE_ARM64_T6000
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 22.3.0
  npm: 10.8.1
  Yarn: 1.22.19
  pnpm: 8.14.0
Relevant Packages:
  next: 14.2.5 // Latest available version is detected (14.2.5).
  eslint-config-next: 14.2.3
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.4.5
Next.js Config:
  output: N/A

Main Page Revalidation Request Not Working

Expected Behavior:
When the function
revalidateTag('products'); is executed, all requests using the ‘products’ tag should be revalidated.

How to create a dynamic chain select with strings using Select2

I am creating a DnD style calculator. There are class trees that are 3 classes deep. How would I be able to force Select 2 to only show level 1, level 2 and level 3 in their respective selects and also base it on the prior selection? I have seen this work with numbers before but I cannot get it to work with specific strings. I created a JSFiddle here: https://jsfiddle.net/3gma61wf/45/

The way the class array is set up:

const classes1 = [
    {
        id: '',
        text: 'Select ICQ...',
    },
    {
        id: 'fighter',
        text: 'Fighter',
        children: [
            {
                id: 'barbarian',
                text: 'Barbarian',
                children: [
                    { id: 'beserker', text: 'Beserker' }
                ],
                id: 'soldier',
                text: 'Soldier',
                children: [
                    { id: 'knight', text: 'Knight' }
                ]
            }
        ]
    },
    {
        id: 'archer',
        text: 'Archer',
        children: [
            {
                id: 'ranger',
                text: 'Ranger',
                children: [
                    { id: 'elite-ranger', text: 'Elite Ranger' }
                ],
                id: 'paladin',
                text: 'Paladin',
                children: [
                    { id: 'bard', text: 'Bard' }
                ]
            }
        ]
    },
    {
        id: 'mage',
        text: 'Mage',
        children: [
            {
                id: 'wizard',
                text: 'Wizard',
                children: [
                    { id: 'warlock', text: 'Warlock' }
                ],
                id: 'druid',
                text: 'Druid',
                children: [
                    { id: 'artificer', text: 'Artificer' }
                ]
            }
        ]
    },
];

On my local project I can get Class 1 to fill in. I want to be able to use Class 1 to find the “children” key and display all of the “text” values there. So on until the third level. I have tried also splitting the array into 3 parts, each for a different select field and then having a “parent” key that would match the prior wording in the array but for obvious reasons that would be more difficult to maintain. Basically, is there a way I can force Select2 to dynamically understand the way I’ve built the array and automatically update it?

I have all commented out code in the JSFiddle that I have attempted. I was able to get Class2 using the find() function but that only ever returns the first found value and when I attempted to switch it to use filter() it would always come back as undefined.

TypeError: _azure_openai__WEBPACK_IMPORTED_MODULE_0__.AzureKeyCredential is not a constructor

I’m doing a project to make a chatbot that can read my portfolio. I’m using the Azure OpenAi GPT-3.5 model and have already configured my apikey and endpoint. Im currently testing the api calls with a simple GET request. This code is in a file called “route.js” :

import { AzureCliCredential } from "@azure/identity";
import { OpenAIClient, AzureKeyCredential } from "@azure/openai";
import { NextResponse } from "next/server";

const endpoint = process.env.AZURE_OPENAI_ENDPOINT;
const apiKey = process.env.AZURE_OPENAI_API_KEY;
const model = process.env.AZURE_OPENAI_MODEL;

export async function GET(req) {
  const client = new OpenAIClient(endpoint, new AzureKeyCredential(apiKey));

  const messages = [
    {
      role: "system",
      content: "You are a helpful assistant.",
    },
    {
      role: "user",
      content: "Why is python such a popular coding language?",
    },
  ];

  const response = await client.getChatCompletions(model, messages, {
    maxTokens: 128,
  });

  return NextResponse.json({
    message: response.choices[0].message.content,
  });
}

However, I get this error message in the terminal:
terminal message

I have tried other methods but they all lead back to this 1 error.

Tried searching the documentation online and changing the import syntax, but the problem pretty much still remains.

How to click the next link with Zyte browser automation?

The Zyte tutorial “Create your first spider” crawls this page which has a pager with a “normal” next link. But what if the next link contains only a href="#" and executes JavaScript instead, like many websites nowadays do? In that case, you have no URL for your next_page_links and cannot execute response.follow_all, right?

The chapter “Handle JavaScript” of the Zyte Tutorial suggests to use browser automation, and the example given there demonstrates how this works with the scrollBottom action for http://quotes.toscrape.com/scroll.

Unfortunately, there is no example how to handle a click action on a next link to make the next results load with JavaScript. Basically, as a proof of concept, clicking the link would even work with a normal link like on http://books.toscrape.com.

I tried this like that:

import scrapy

from scrapy import Request


class BooksToScrapeSpider(scrapy.Spider):
    name = "books_toscrape"
    start_urls = [
        "http://books.toscrape.com/catalogue/category/books/mystery_3/index.html"
    ]

    def parse(self, response):
        # Extract book data
        for book in response.css("article.product_pod"):
            yield {
                "name": book.css("h3 a::attr(title)").get(),
                "price": book.css(".price_color::text").get(),
            }

        # Find the "next" link
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            self.logger.info(f"Found next page: {next_page}")
            yield Request(
                # response.urljoin(next_page),
                "http://books.toscrape.com/catalogue/category/books/mystery_3/index.html#",
                meta={
                    "zyte_api_automap": {
                        "browserHtml": True,
                        "actions": [
                            {
                                "action": "click",
                                "selector": {"type": "css", "value": "li.next a"},
                            },
                            {
                                "action": "waitForSelector",
                                "selector": {
                                    "type": "css",
                                    "value": "li.previous a",
                                },
                            },
                        ],
                    }
                },
                callback=self.parse,
            )
        else:
            self.logger.info("No next page found")

To perform Zyte browser automation, I first need a request, right? So it doesn’t work without an URL. In my fictitious case, the URL is http://books.toscrape.com/catalogue/category/books/mystery_3/index.html#, actually. But I do not want to fire a request and then perform an action. What I want is to perform an action without request (like an ‘onclick’ event does), and this action does something like, for example, a request.

I’ve been racking my brains for days on how to do this – to no avail. Does anyone have any ideas for me?

How to Enable Auto-Complete for Custom Path Functions in PHPStorm?

How can I enable auto-complete in PHPStorm for a function that takes a string input, where the string represents a file path?

For example, with JavaScript, I define a function like this:

function asset(path: string) {
    return `/public/static/${path}`;
};

The goal is to have the path values auto-completed as I type. How can this be achieved?

For instance, in Laravel, we have auto-completion like this:
Screenshot from php storm auto complete for asset helper function

Can we achieve the same for custom functions?

Why is my calculation in JavaScript wrong? [duplicate]

developers!
Please, help me to find out why when I subtract from the user_input (1.89) the price (1.87) my result is 0.019999999999999796 instead of 0.02?

When the user_input is 2.87 the result is correct, 1.



let price = 1.87;



const sale = () => {

  let user_input = Number(cash.value);

  let result = "";

  const cashMinusPrice = () => {result = user_input - price;

  console.log(result);

  return result};



I tried to use different user_inpit values, something happens when the result is not a flat number

How to add TextBox to fabric.js after loading from JSON

I am unable to load json into fabric.js (via canvas.loadFromJSON) and then create a TextBox which I add to the canvas. The image shows, but the TextBox does not show.

I believe the issue might be because I’m loading a type image. In the examples on fabricjs.com, it shows loading rect or circle. Or, whilst this is a difference, there is something else that is wrong entirely.

I’ve created some code that demonstrates this.

https://jsfiddle.net/cbp612wo/1/

<canvas id="c" width="600" height="600"></canvas>
<script>
let canvas = new fabric.Canvas('c');

const jsonWithShape = JSON.stringify({"objects":[{"type":"rect","originX":"center","originY":"center","left":300,"top":150,"width":150,"height":150,"fill":"#29477F","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":{"color":"rgba(94, 128, 191, 0.5)","blur":5,"offsetX":10,"offsetY":10},"visible":true,"clipTo":null,"rx":0,"ry":0,"x":0,"y":0}]});

const jsonWithImage = JSON.stringify({"objects":
    [{
        "type": "image",
        "version": "5.2.1",
        "originX": "left",
        "originY": "top",
        "left": 0,
        "top": 0,
        "width": 0,
        "height": 0,
        "fill": "rgb(0,0,0)",
        "stroke": null,
        "strokeWidth": 0,
        "strokeDashArray": null,
        "strokeLineCap": "butt",
        "strokeDashOffset": 0,
        "strokeLineJoin": "miter",
        "strokeUniform": false,
        "strokeMiterLimit": 4,
        "scaleX": 1,
        "scaleY": 1,
        "angle": 0,
        "flipX": false,
        "flipY": false,
        "opacity": 1,
        "shadow": null,
        "visible": true,
        "backgroundColor": "",
        "fillRule": "nonzero",
        "paintFirst": "fill",
        "globalCompositeOperation": "source-over",
        "skewX": 0,
        "skewY": 0,
        "cropX": 0,
        "cropY": 0,
        "src": "https://stackoverflow.design/assets/img/logos/so/logo-screen.svg",
        "crossOrigin": null,
        "filters": []
    }]
});

//Uncomment one or the other of the lines 2 lines below - the text only shows on the shape (rect) 
//canvas.loadFromJSON(jsonWithShape);   //this works as expected
canvas.loadFromJSON(jsonWithImage);     //this does not show the text

const txt = "Hello world";

const t1 = new fabric.Textbox(txt, {
    textAlign: 'center'
});

canvas.add(t1);
     
canvas.renderAll.bind(canvas)

</script>

In the example above, I’ve commented out one of the lines so you can essentially toggle between a working example (by commenting/uncommenting where specified)

What am I doing wrong? Why can’t I see text and the SO logo?