strapi racing condition prevents relation field to be filled

I am working on an e-shop where I have chosen strapi as backend and nextjs as frontend.
I noticed, that if I put some products in the basket simultaniously (I simulate it by using two different browsers), then when a racing condition occurs, the basket of the user which lost the race is not filled.

The solution that I am using right now is:

  1. I fetch the products collection type from strapi to show all of the products on the webpage:

    {
    “kind”: “collectionType”,
    “collectionName”: “products”,
    “info”: {
    “singularName”: “product”,
    “pluralName”: “products”,
    “displayName”: “Product”,
    “description”: “”
    },
    “options”: {
    “draftAndPublish”: true
    },
    “pluginOptions”: {},
    “attributes”: {
    “title”: {
    “type”: “string”,
    “required”: true,
    “maxLength”: 255
    },
    “price”: {
    “type”: “decimal”,
    “required”: true
    },
    “image”: {
    “type”: “media”,
    “multiple”: false,
    “required”: false,
    “allowedTypes”: [
    “images”
    ]
    },
    “product_categories”: {
    “type”: “relation”,
    “relation”: “oneToMany”,
    “target”: “api::product-category.product-category”,
    “mappedBy”: “product”
    },
    “slug”: {
    “type”: “string”,
    “required”: true,
    “unique”: true
    },
    “quantity”: {
    “type”: “integer”
    },
    “article_code”: {
    “type”: “string”,
    “required”: true,
    “unique”: true
    },
    “user_cart”: {
    “type”: “relation”,
    “relation”: “manyToOne”,
    “target”: “api::user-cart.user-cart”,
    “inversedBy”: “product”
    }
    }
    }

Just becouse the .json is not formatted as I would like to have it:

products collection type json

  1. Then I put the product and its amount to the users cart:

    {
    “kind”: “collectionType”,
    “collectionName”: “user_carts”,
    “info”: {
    “singularName”: “user-cart”,
    “pluralName”: “user-carts”,
    “displayName”: “User Cart”,
    “description”: “”
    },
    “options”: {
    “draftAndPublish”: true
    },
    “pluginOptions”: {},
    “attributes”: {
    “quantity”: {
    “type”: “integer”
    },
    “amount”: {
    “type”: “integer”
    },
    “product”: {
    “type”: “relation”,
    “relation”: “oneToMany”,
    “target”: “api::product.product”,
    “mappedBy”: “user_cart”
    },
    “session_id”: {
    “type”: “string”,
    “required”: true,
    “unique”: false
    },
    “price”: {
    “type”: “decimal”
    },
    “expiration_date”: {
    “type”: “datetime”
    }
    }
    }

user-cart collection json

and when I check on the user, who has lost the race, the product array is empty.
But the other user, where the leftover amount is not 0 get’s the relation data.

My question is, why am I not getting any relation information back? Like the title of the product?

Modifying myers diff by identifying compound units and treating them as 1 single insertion or removal

Myers diff algorithm works at character level and identifies insertion, removal or no-operation at each step. So for example, comparing PQRSXRSYRSZ (old string presented horizontally) and PQRSARSXRSY (new string presented vertically) leads to removal of X, insertion of A, removal of Y, insertion of X, removal of Z and insertion of Y.

      P Q R S X R S Y R S Z
    P ↘ · · · · · · · · · ·
    Q · ↘ · · · · · · · · ·
    R · · ↘ · · · · · · · ·
    S · · · ↘ → · · · · · ·
    A · · · · ↓ · · · · · ·
    R · · · · · ↘ · · · · ·
    S · · · · · · ↘ → · · ·
    X · · · · · · · ↓ · · ·
    R · · · · · · · · ↘ · ·
    S · · · · · · · · · ↘ →
    Y · · · · · · · · · · ↓

But if I replace RS in both the strings, the old string becomes PQXYZ and new becomes PQAXY, creating :

  P Q X Y Z
P ↘ · · · ·
Q · ↘ · · ·
A · ↓ · · ·
X · · ↘ · ·
Y · · · ↘ →

where only A is removed and Z is inserted.

Here is the O((M+N)D) version of Myers diff algorithm.

        class Keep {
            constructor(line) {
                this.type = 'Keep';
                this.line = line;
            }
        }

        class Insert {
            constructor(line) {
                this.type = 'Insert';
                this.line = line;
            }
        }

        class Remove {
            constructor(line) {
                this.type = 'Remove';
                this.line = line;
            }
        }

        // Represents a point on the frontier with its history
        class Frontier {
            constructor(x, history) {
                this.x = x;        // x-coordinate in edit graph
                this.history = history;  // sequence of operations to reach this point
            }
        }

        function myersDiff(old, current) {
            // Initialize the frontier with starting point
            // K=1 diagonal starts at (0,0) with empty history
            const frontier = { 1: new Frontier(0, []) };

            // Convert from 1-based to 0-based indexing
            // Algorithm uses 1-based indexing, but JavaScript uses 0-based
            function one(idx) {
                return idx - 1;
            }

            const aMax = old.length;  // horizontal size of edit graph
            const bMax = current.length;  // vertical size of edit graph

            // Main loop: try increasing numbers of edits
            for (let d = 0; d <= aMax + bMax + 1; d++) {
                // For each value of D, try all possible diagonals
                for (let k = -d; k <= d; k += 2) {
                    // Determine whether to go down or right
                    // This choice affects which diagonal we extend from
                    const goDown = (k === -d ||  // at left edge, must go down
                        (k !== d && frontier[k - 1].x < frontier[k + 1].x));  // compare positions

                    // Find starting point for this iteration
                    let oldX, history;
                    if (goDown) {
                        // Copy from k+1 diagonal (going down)
                        ({ x: oldX, history } = frontier[k + 1]);
                        var x = oldX;
                    } else {
                        // Copy from k-1 diagonal and move right
                        ({ x: oldX, history } = frontier[k - 1]);
                        var x = oldX + 1;
                    }

                    // Clone history to avoid modifying previous paths
                    history = [...history];
                    let y = x - k;  // Calculate y from x and k

                    // PQ RSX RSY RSZ
                    // PQ RSA RSX RSY
                    // Record the edit we made to get here
                    if (y >= 1 && y <= bMax && goDown) {
                        // Insert from new sequence when going down
                        history.push(new Insert(current[one(y)]));
                    } else if (x >= 1 && x <= aMax) {
                        // Remove from old sequence when going right
                        history.push(new Remove(old[one(x)]));
                    }

                    // Follow the snake: match diagonal moves as far as possible
                    // This is key optimization - extend path along matches
                    while (x < aMax && y < bMax &&
                        old[one(x + 1)].hashVal === current[one(y + 1)].hashVal) {
                        x += 1;
                        y += 1;
                        history.push(new Keep(current[one(y)]));
                    }

                    // Check if we've reached the end
                    if (x >= aMax && y >= bMax) {
                        return history;  // Found solution
                    } else {
                        // Save this point in the frontier
                        frontier[k] = new Frontier(x, history);
                    }
                }
            }

            throw new Error('Could not find edit script');
        }



        let text1 = [
            { hashVal: 'P', content: 'P'},
            { hashVal: 'Q', content: 'Q'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'X', content: 'T'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'Y', content: 'T'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'Z', content: 'Z'},
        ];

        let text2 = [
            { hashVal: 'P', content: 'P'},
            { hashVal: 'Q', content: 'Q'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'A', content: 'T'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'X', content: 'T'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'Y', content: 'Y'},
        ];

        const editScript = myersDiff(text1, text2);
        let diff2 = editScript.map(operation => {
            const element = operation.line;
            if (operation instanceof Keep) {
                element.status = 'unchanged';
            } else if (operation instanceof Insert) {
                element.status = 'inserted';
            } else if (operation instanceof Remove) {
                element.status = 'removed';
            }
            return element;
        });

        console.log(diff2.map(item => ({status: item.status, hashVal: item.hashVal})));

What I want to do is with this version and for PQRSXRSYRSZ (old string presented horizontally) and PQRSARSXRSY (new string presented vertically), I want to create the following path:

      P Q R S X R S Y R S Z
    P ↘ · · · · · · · · · ·
    Q · ↘ · · · · · · · · ·
    R · ↓ . · · · · · · · ·
    S · ↓ · · · · · · · · ·
    A · ↓ · · . · · · · · ·
    R · · ↘ · · · · · · · ·
    S · · · ↘ · · · · · · ·
    X · · · · ↘ · · · · · ·
    R · · · · · ↘ · · · · ·
    S · · · · · · ↘ · · · ·
    Y · · · · · · · ↘ → → →

such that:

  • when R matches with R and S matches with S but there is a mismatch between X and A, it attempts to insert A and insertion of RSA is treated as 1 single unit (or what can be called as compound units).
  • similarly, insertion of RSZ is treated as 1 single unit.
  • for every other case, it works the same way.

So, I want to modify the definition of minimum edit distance. Given this case, the definition of minimum edits required becomes 2 because it just inserts RSA and removes RSZ (both treated as a single unit). How can I achieve this?

I tried to modify the original Myers diff given above as follows:

        class Keep {
            constructor(unit) {
                this.type = 'Keep';
                this.unit = unit;
            }
        }

        class Insert {
            constructor(unit) {
                this.type = 'Insert';
                this.unit = unit;
            }
        }

        class Remove {
            constructor(unit) {
                this.type = 'Remove';
                this.unit = unit;
            }
        }

        class Frontier {
            constructor(x, history, path = []) {
                this.x = x;
                this.history = history;
                this.path = path;
            }
        }

        function getNextUnit(sequence1, pos1, sequence2, pos2) {
            console.log(`nChecking for unit at pos1=${pos1}, pos2=${pos2}`);
            
            if (pos1 < 0 || pos2 < 0 || pos1 >= sequence1.length) {
                console.log('Position out of bounds, returning null');
                return null;
            }
            
            // Look for compound unit only if we're at matching positions
            if (pos1 + 2 < sequence1.length && pos2 + 2 < sequence2.length &&
                sequence1[pos1].hashVal === sequence2[pos2].hashVal) {
                const firstMatch = sequence1[pos1].hashVal === sequence2[pos2].hashVal;
                const secondMatch = sequence1[pos1 + 1].hashVal === sequence2[pos2 + 1].hashVal;
                const thirdMismatch = sequence1[pos1 + 2].hashVal !== sequence2[pos2 + 2].hashVal;
                
                console.log(`Pattern check at ${pos1}:
                    First: ${sequence1[pos1].hashVal} vs ${sequence2[pos2].hashVal} = ${firstMatch}
                    Second: ${sequence1[pos1 + 1].hashVal} vs ${sequence2[pos2 + 1].hashVal} = ${secondMatch}
                    Third mismatch: ${sequence1[pos1 + 2].hashVal} vs ${sequence2[pos2 + 2].hashVal} = ${thirdMismatch}`);
                
                if (firstMatch && secondMatch && thirdMismatch) {
                    console.log('Found compound unit:', sequence1.slice(pos1, pos1 + 3).map(x => x.hashVal));
                    return {
                        elements: sequence1.slice(pos1, pos1 + 3),
                        size: 3,
                        isCompound: true
                    };
                }
            }
            
            console.log('Returning single unit:', sequence1[pos1].hashVal);
            return {
                elements: [sequence1[pos1]],
                size: 1,
                isCompound: false
            };
        }

        function unitsMatch(unit1, unit2) {
            if (!unit1 || !unit2) return false;
            if (unit1.elements.length !== unit2.elements.length) return false;
            
            return unit1.elements.every((elem, i) => 
                elem.hashVal === unit2.elements[i].hashVal
            );
        }

        function visualizePath(old, current, finalPath) {
            const matrix = [];
            const headerRow = [' '];
            for (let char of old) {
                headerRow.push(char.hashVal);
            }
            matrix.push(headerRow);

            for (let i = 0; i < current.length; i++) {
                const row = [current[i].hashVal];
                for (let j = 0; j < old.length; j++) {
                    row.push('·');
                }
                matrix.push(row);
            }
            
            let prevX = 0, prevY = 0;
            for (const [x, y] of finalPath) {
                if (y >= 0 && y < matrix.length && x >= 0 && x < matrix[0].length) {
                    if (x === prevX && y > prevY) {
                        matrix[y][x] = '↓';
                    } else if (x > prevX && y === prevY) {
                        matrix[y][x] = '→';
                    } else if (x > prevX && y > prevY) {
                        matrix[y][x] = '↘';
                    }
                }
                prevX = x;
                prevY = y;
            }

            console.log('nPath Visualization:');
            for (const row of matrix) {
                console.log(row.join(' '));
            }
        }

        function addPathPoints(path, startX, startY, endX, endY, isDiagonal = false) {
            if (isDiagonal) {
                for (let i = 0; i <= endX - startX; i++) {
                    path.push([startX + i, startY + i]);
                }
            } else {
                const xStep = Math.sign(endX - startX);
                const yStep = Math.sign(endY - startY);
                
                for (let x = startX; x !== endX; x += xStep) {
                    path.push([x, startY]);
                }
                for (let y = startY; y !== endY; y += yStep) {
                    path.push([endX, y]);
                }
            }
        }

        function myersDiffCompoundUnits(old, current) {
            let frontier = { 0: new Frontier(0, [], [[0, 0]]) };
            const aMax = old.length;
            const bMax = current.length;

            // Calculate max D based on compound units
            const COMPOUND_SIZE = 3;
            const maxD = Math.ceil((aMax + bMax) / COMPOUND_SIZE) + 1;

            console.log('nStarting diff with compound units:');
            console.log('Old:', old.map(x => x.hashVal).join(''));
            console.log('Current:', current.map(x => x.hashVal).join(''));
            console.log('Max D:', maxD);

            for (let d = 0; d <= maxD; d++) {
                console.log(`n=== Edit distance D = ${d} ===`);
                const newFrontier = {};
                
                for (let k = -d; k <= d; k += 2) {
                    console.log(`n--- Diagonal k = ${k} ---`);
                    
                    const goDown = (k === -d || 
                        (k !== d && frontier[k - 1]?.x < frontier[k + 1]?.x));
                    
                    let oldX, history, path;
                    if (goDown) {
                        ({ x: oldX, history, path } = frontier[k + 1] || { x: 0, history: [], path: [[0, 0]] });
                        var x = oldX;
                    } else {
                        ({ x: oldX, history, path } = frontier[k - 1] || { x: 0, history: [], path: [[0, 0]] });
                        var x = oldX + 1;
                    }

                    history = [...history];
                    path = [...path];
                    let y = x - k;

                    console.log(`Current position: x=${x}, y=${y}`);
                    
                    // Try snake movement first before any edits
                    let snakeMoved = false;
                    while (x < aMax && y < bMax) {
                        const oldUnit = getNextUnit(old, x, current, y);
                        const currentUnit = getNextUnit(current, y, old, x);

                        if (!oldUnit || !currentUnit || !unitsMatch(oldUnit, currentUnit)) {
                            break;
                        }

                        console.log('Matching unit:', oldUnit.elements.map(e => e.hashVal));
                        const startX = x;
                        const startY = y;
                        x += oldUnit.size;
                        y += oldUnit.size;
                        history.push(new Keep(currentUnit));
                        addPathPoints(path, startX, startY, x, y, true);
                        snakeMoved = true;
                    }

                    // Only try edit operations if snake couldn't move
                    if (!snakeMoved) {
                        if (y >= 1 && y <= bMax && goDown) {
                            const unit = getNextUnit(current, y - 1, old, x);
                            if (unit) {
                                console.log('Inserting unit:', unit.elements.map(e => e.hashVal));
                                history.push(new Insert(unit));
                                const startX = x;
                                const startY = y;
                                y += unit.size - 1;
                                addPathPoints(path, startX, startY, x, y);
                            }
                        } else if (x >= 1 && x <= aMax) {
                            const unit = getNextUnit(old, x - 1, current, y);
                            if (unit) {
                                console.log('Removing unit:', unit.elements.map(e => e.hashVal));
                                history.push(new Remove(unit));
                                const startX = x;
                                const startY = y;
                                x += unit.size - 1;
                                addPathPoints(path, startX, startY, x, y);
                            }
                        }
                    }

                    console.log(`Position after moves: x=${x}, y=${y}`);

                    if (x >= aMax && y >= bMax) {
                        console.log('nFound solution!');
                        visualizePath(old, current, path);
                        return { history, path };
                    }

                    newFrontier[k] = new Frontier(x, history, path);
                }
                frontier = newFrontier;
            }

            throw new Error('Could not find edit script');
        }

        // Test
        let text1 = [
            { hashVal: 'P', content: 'P'},
            { hashVal: 'Q', content: 'Q'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'X', content: 'X'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'Y', content: 'Y'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'Z', content: 'Z'},
        ];

        let text2 = [
            { hashVal: 'P', content: 'P'},
            { hashVal: 'Q', content: 'Q'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'A', content: 'A'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'X', content: 'X'},
            { hashVal: 'R', content: 'R'},
            { hashVal: 'S', content: 'S'},
            { hashVal: 'Y', content: 'Y'},
        ];

        const result = myersDiffCompoundUnits(text1, text2);
        const diff = result.history.flatMap(op => {
            return op.unit.elements.map(element => {
                if (op instanceof Keep) {
                    element.status = 'unchanged';
                } else if (op instanceof Insert) {
                    element.status = 'inserted';
                } else if (op instanceof Remove) {
                    element.status = 'removed';
                }
                return element;
            });
        });

        console.log("nFinal diff:");
        console.log(diff.map(item => ({status: item.status, hashVal: item.hashVal})));

where I am detecting these compound units. It comes close enough but still has few issues. Also, I am not sure, if this is even the correct way. What I am having trouble understanding is:

  • How do I define d? Because the modified version must work the same except when it finds the compound units like RSX and RSA where RS match and X and A mismatch. So it treats the complete RSX/RSA for removal/insertion
  • In the modified version, it took more number of iterations (d becomes 6)
  • What is the correct way to modify the K?

How should this be modified / attempted so that the idea of compound units can be used?

How to use If else statements in PDF javascript

I am looking to have a PDF calculate an answer based on the inputs from multiple fields and dropdowns.

I am using input fields for No. of Units, Oil Cost, Oil Changes per Week and a dropdown for Single Fryer which when all inputted will hopefully calculate a per annum cost for oil usage in a fryer.

The code I am trying to us is below;

I would expect with the below units inputted into their associated fields an answer of 62,400,


if ((this.getField("Single Fryer").value) = "MJ150") {
    event.value = (this.getField("No. of Units").value) * (this.getField("Oil Cost").value) * (this.getField("Oil Changes per Week").value) * 25 * 52
} else if ((this.getField("Single Fryer").value) = "MJ140" {
    event.value = (this.getField("No. of Units").value) * (this.getField("Oil Cost").value) * (this.getField("Oil Changes per Week").value) * 20 * 52
} else if ((this.getField("Single Fryer").value) = "SR42" {
    event.value = (this.getField("No. of Units").value) * (this.getField("Oil Cost").value) * (this.getField("Oil Changes per Week").value) * 20 * 52
} else if ((this.getField("Single Fryer").value) = "RE114" {
    event.value = (this.getField("No. of Units").value) * (this.getField("Oil Cost").value) * (this.getField("Oil Changes per Week").value) * 20 * 52
} else if ((this.getField("Single Fryer").value) = "RE117" {
    event.value = (this.getField("No. of Units").value) * (this.getField("Oil Cost").value) * (this.getField("Oil Changes per Week").value) * 20 * 52
} else {
    event.value = "N/A"
}

  1. No. of Units = 4
  2. Oil Cost ($/L) = 4
  3. Oil Changes per Week = 3
  4. Single Fryer = MJ150

However I am getting the below syntax error when trying to input the code;

SyntaxError: Missing ) after condition 3: at line 4

Am using Adobe Acrobat X if that makes a difference.

This is my first time using Javascript so any help/recommendations would be appreciated.

Does from react-router-dom Affect SEO Compared to Tags in React TSX?

I am building a React website using TypeScript and react-router-dom <Link to="/page"> for navigation. Instead of using <a href="/page">, I am using <Link to="/page"> to enable faster client-side navigation.

My questions are:

  1. Does using <Link> instead of <a> impact SEO, considering search
    engines rely on static HTML for crawling?
  2. Do I need to implement server-side rendering (SSR) or pre-rendering to ensure that search engines properly index my site’s pages?

I’d appreciate any insights on best practices for SEO in a React-based Single Page Application (SPA). Thanks!

Why does my JavaScript code for dragging an HTML element distort it at higher dragging speeds?

I built a small prototype for an UI element with a drag feature and it works, but dragging with medium+ speed results in the dragged element getting quite noticeably distorted along the drag axis. When dragging slowly this does not happen. Does anyone know why this happens here and if it’s possible to prevent it? Or does anyone not have this problem maybe? I tried googling but I was completely unable to produce a prompt that would get me an answer.

I’m on Chrome v132.0.6834.160, and here’s the JSBin of the mentioned prototype https://jsbin.com/lovoqulewa/2/edit?html,css,js,console,output

The red div can be dragged inside the blue div by clicking and then dragging the mouse without letting go, like common drag-and-drop elements.

And since stackoverflow won’t let me post it without code, here’s only the portion of the code that moves the element.

function slide(e) {
  movy.style.left = Math.max(0,Math.min((e.clientX-offset[0]), (bounds[2]-cSize[0])))+'px';
  movy.style.top = Math.max(0,Math.min((e.clientY-offset[1]), (bounds[3]-cSize[1])))+'px';
}

slide(e) is called from onmousemove. Is setting left / top every tick of the mouse move event known to be a problem for rendering? I also tried wrapping it in a requestAnimationFrame() like follows, but that didn’t help.

function slide(e) {
  requestAnimationFrame(function() {                        
  movy.style.left = Math.max(0,Math.min((e.clientX-offset[0]), (bounds[2]-cSize[0])))+'px';
  movy.style.top = Math.max(0,Math.min((e.clientY-offset[1]), (bounds[3]-cSize[1])))+'px';
  });
}

I’m not familiar with the inner workings of requestAnimationFrame at all though, I just know it sometimes helps with similar effects when doing canvas animations.

Open universal link from social network in-app browser

I have some universals links to open my app, something like https://website.com/app/user/{id}, it opens the user profile on my app and works great.

At the moment I am working with firebase dynamics links, and it works, but the service will stop soon so I need to find an alternative.

My main issue are the links shared on social networks, for example Instagram, the page is opened in an in-app browser and my add is not opened

First I tried the following solution

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Redirecting...</title>
    <script>
        function openApp() {
            var universalLink = "https://yourdomain.com/path"; // Your actual Universal Link

            // Force the browser to open the Universal Link outside in-app browsers
            window.location.replace(universalLink);

            // Fallback: If Universal Link fails, show a button
            setTimeout(function() {
                document.getElementById("fallback").style.display = "block";
            }, 2000);
        }
    </script>
</head>
<body onload="openApp()">
    <h1>Redirecting...</h1>
    <p>If the app doesn't open, click the button below.</p>
    <a id="fallback" href="https://yourdomain.com/path" style="display: none;">
        <button>Open in Browser</button>
    </a>
</body>
</html>

But none of the two alternatives works, the fallback is never called as the window.location.replace is always called, but the redirect is made into the same in-app browser same if I comment the window.location.replace, clicking on the button, the redirect is made in the same in-app browser.

Then I tried some target attributes in a tags, for example _blank or _system but the link is still opening in the same in-app browser.

Then I tried with browsers deep links as x-safari-https://website.com, or Google Chrome but the links opens in the browser and not opening my app.

I do not know if I have to use a paying service as branch.io for something who could be that simple.

  1. if app installed, open app
  2. Open store if on android or iOS
  3. Open my website

How to omit the index.html in a react app?

I have a next.js react app that is serving several static projects in the public folder:

public/
  ├── project1/
  |   ├── index.html
  |   ├── script.js
  |   └── ...
  ├── project2/
  |   ├── index.html
  |   ├── script.js
  |   └── ...
  └── ...

However, going to localhost:3000/project1 results in a 404 page, and I have to go to localhost:3000/project1/index.html for it to work.

How can I omit the index.html, so going to localhost:3000/project1 works?

I tried doing this in my next.config.ts:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      {
        source: "/project1",
        destination: "/project1/index.html",
      },
    ];
  },
};

export default nextConfig;

Now I can see the page in localhost:3000/project1, but it can no longer read the js and all other assets because it read from localhost:3000/script.js instead of localhost:3000/project1/script.js.

Most of these projects are quite big so I cannot manually change the relative path of everything in there.

WebView in kivy with YouTube Embedded Players and Player Parameters

I have tried to embed a YouTube in my app using kivy but the code wasn’t working. This the code I wrote.

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.webview import WebView
class YouTubePlayer(BoxLayout):
def __init__(self, **kwargs):
    super().__init__(orientation="vertical", **kwargs)

    self.url_input = TextInput(
        hint_text="Enter YouTube Video ID (e.g., dQw4w9WgXcQ)",
        size_hint_y=None,
        height=40,
    )
    self.add_widget(self.url_input)

    self.play_button = Button(text="Play Video", size_hint_y=None, height=50)
    self.play_button.bind(on_press=self.load_video)
    self.add_widget(self.play_button)

    self.video_label = Label(text="YouTube Video Player", size_hint_y=None, height=40)
    self.add_widget(self.video_label)

    self.webview = WebView()
    self.add_widget(self.webview)

def load_video(self, instance):
    video_id = self.url_input.text.strip()
    if video_id:
        youtube_embed_url = f"https://www.youtube.com/embed/{video_id}?autoplay=1"
        self.webview.url = youtube_embed_url
    else:
        self.video_label.text = "Please enter a valid video ID"

class YouTubeApp(App):
  def build(self):
      return YouTubePlayer()

if __name__ == "__main__":
  YouTubeApp().run()

however, this error happens: 
Traceback (most recent call last):
 File "c:python_projectyoutubemain.py", line 6, in <module>
   from kivy.uix.webview import WebView
ModuleNotFoundError: No module named 'kivy.uix.webview'

my question is is there any solution for this problem? or any other way to embed Youtube video in my app while I am using kivy?

How to get correct type hints for vue.esm-browser.js when importing Vue in VS Code?

I am trying to make a web app without any packager, at least for the development variant. I will set my express backend to serve certain paths from node_modules to frontend.

My project structure is:

  • server
    • node_modules
    • web
      • js
        • my_app.js
      • index.html

The server is set to serve vue like this:

const app = express();

// Allowed modules to appear on the frontend
const allowedModules = ['vue'];

// Middleware to serve selected node_modules
allowedModules.forEach(module => {
    // TODO: Maybe not a static() instance per module? There has to be a better way
    app.use(
        `/node_modules/${module}`,
        express.static(path.join(moduledir, '../node_modules', module),
            { 
                immutable: true,
                maxAge: T_DAYS_MS
            }
        )
    );
});

And this is how I import it in my_app.js:

import * as vue_core from "/node_modules/vue/dist/vue.esm-browser.js"

const app = {
  data() {
    return {
      message: 'Hello Vue!'
    };
  },
  template: `<div>{{ message }}</div>`
};

vue_core.createApp(app).mount('#app');

And that works fine. I see this in the console when I load index.html in browser, indicating that vue imported correctly:

[Vue warn]: Failed to mount app: mount target selector "#app" returned null.

But, the problem is type hints. I am new to vue and I want to get detailed type hints and documentation directly in IDE.

What I tried:

  • Installed @types/vue

  • Set my jsconfig.json to know where the file I am importing is located in compilerOptions:

     "paths": {
         "/node_modules/vue/*": ["./node_modules/vue/*"],
     },
    
  • Tried to also do a module declaration in web/module_declarations/vue_helper.d.ts:

     declare module "/node_modules/vue/dist/vue.esm-browser.js" {
         import Vue from 'vue';
         export default Vue;
     }
    

Finally, I tried to also use JSDoc import and type coercion:

import * as vue_core from "/node_modules/vue/dist/vue.esm-browser.js"

/**
 * @typedef {import("vue")} VueLib
 **/
/** @type {VueLib} **/
// @ts-ignore
const vue = vue_core;

However, I still have not one but TWO problems:

  1. The import statement for vue.esm-browser.js shows the following error:

    Could not find a declaration file for module '/node_modules/vue/dist/vue.esm-browser.js'. 'root/node_modules/vue/dist/vue.esm-browser.js' implicitly has an 'any' type.ts(7016)

    enter image description here

  2. The symbols imported from vue do not match what’s in vue.esm-browser.js. The type hints from @types/vue seem to export VueStatic and not any of the vue functions that I actually am importing from the .js file.

JSON objects getting copied in succeeding iteration while pushing into array in JavaScript

I have been trying to create JSON objects inside a JSON array based on the count value I receive during runtime. But I see that first iteration is just fine and second iteration is having keys from previous iterations.need to get this removed. Used arr.push(JSON.parse(JSON.stringify(object))), arr.push(JSON.parse(JSON.stringify(sclog))), dynamic keys not supported in our platform.

var policyCount = 2;

// Initialize an array to hold the details of all policies
const arr = [];

for (var i = 1; i <= policyCount; i++) {
   var policyName = ("policyName" + i);
    var TimeTaken = parseInt(timetaken) / 100000);
    var TargetUrl = ("requestVar" + i)+ ".url");
    var StatusCode = ("responseVar" + i)+ ".status.code");

    // Create an object to store the policy's details
    const log = {
        [policyName + ".url"]: TargetUrl,
        [policyName + ".timeTaken"]: TimeTaken,
        [policyName + ".statusCode"]: StatusCode,

    };
    // Add the policy details to the array
    arr.push(JSON.parse(JSON.stringify(log))); 
}

// Set the array as a context variable
context.setVariable("scDetails", JSON.stringify(arr));

Current output:

[
    {
        "callout.url": "xxx",
        "callout.timeTaken": 3715,
        "callout.statusCode": 401,
    },
    {
        "callout.url": "xxx",
        "callout.timeTaken": 3715,
        "callout.statusCode": 401,
        "callout1.url": "yyy",
        "callout1.timeTaken": 3734415,
        "callout1.statusCode": 403,
    }
]

Expected output :

[
    {
        "callout.url": "xxx",
        "callout.timeTaken": 3715,
        "callout.statusCode": 401,
    },
    {
        "callout1.url": "yyy",
        "callout1.timeTaken": 3734415,
        "callout1.statusCode": 403,
    }
]

How can I prevent my output array from duplicating a certain value?

I am calculating the rolling simple moving average (SMA) from a sample array of stock prices which are then plotted on a chart. The x axis corresponds to the “x” value in the array which holds the dates, and the y axis in the array holds the open, high, low, and closing prices for a given stock for each date. Only the closing prices are used to calculate the SMA.

The average calculates correctly, however the method I’m using to push values to the output array is duplicating a date. If the number of periods (days) for which I’m calculating the average is 5, as shown in the code below, I should see null Y values for the first 4 days. The calculated SMA should appear as the Y value on the 5th day. Instead, I’m showing null values for the first 5 days, and then showing the SMA on the 5th day – duplicating the 5th date in the sequence.

How could I revise my code to avoid this?

    let data=[
    {x:"2021-05-15T00:00:00.000Z",y:[24.8,25.2,24.76,24.91]},  
    {x:"2021-05-16T00:00:00.000Z",y:[25.04,25.04,24.97,25.03]},
    {x:"2021-05-17T00:00:00.000Z",y:[25.05,25.45,25.01,25.31]},
    {x:"2021-05-20T00:00:00.000Z",y:[25.43,25.43,25.2,25.38]},
    {x:"2021-05-21T00:00:00.000Z",y:[25.33,25.4,25.15,25.33]},
    {x:"2021-05-22T00:00:00.000Z",y:[25.39,25.39,25.27,25.3]},
    {x:"2021-05-23T00:00:00.000Z",y:[25.28,25.48,25.21,25.28]},
    {x:"2021-05-24T00:00:00.000Z",y:[25.27,25.59,25.17,25.29]},
    {x:"2021-05-28T00:00:00.000Z",y:[25.29,25.45,25.23,25.43]},
    {x:"2021-05-29T00:00:00.000Z",y:[25.5,25.5,25.37,25.43]}];
    
    let resultingArray = calcAverage(data, 5);
    
    function calcAverage(data, period) {
      let result = [];
    
        period = period || 5;
        for (let i = 0; i < period; i++)
            result.push({
                x: data[i].x,
                y: null
            });
            
      for (let i = period - 1; i < data.length; i++) {
        let window = data.slice(i - period + 1, i + 1); 
        let mean = window.reduce((sum, val) => sum + val.y[3], 0) / period;
    
        if (isNaN(mean))
            result.push({
                x: data[i].x,
                y: null
            });
        else
            result.push({
                x: data[i].x,
                y: mean
            });
      }
      return result;
    }

I logged the output array so you can see the duplication:

0: {x: '2021-05-15T00:00:00.000Z', y: null}
1: {x: '2021-05-16T00:00:00.000Z', y: null}
2: {x: '2021-05-17T00:00:00.000Z', y: null}
3: {x: '2021-05-20T00:00:00.000Z', y: null}
**4: {x: '2021-05-21T00:00:00.000Z', y: null}
5: {x: '2021-05-21T00:00:00.000Z', y: 25.192}**
6: {x: '2021-05-22T00:00:00.000Z', y: 25.27}
7: {x: '2021-05-23T00:00:00.000Z', y: 25.32}
8: {x: '2021-05-24T00:00:00.000Z', y: 25.315999999999995}
9: {x: '2021-05-28T00:00:00.000Z', y: 25.326}

javascript, better way for get indices of numrical array in decrsing order as new array

I want JavaScript function, that receive an array as input and output the indices of the array in a order which if value is max it’s index should be at [0] of new array, second max [1] and so on …

for example: [3, 4, 5, 1, 2] as input should give [2,1,0,4,3] output

I code this function but I want a way with one for loop or one map function

const maxValueIndexes = (array) => {

    if (array.length === 0) return []; 
    
    const indexedArray = array.map((value, index) => ({value, index }));
    indexedArray.sort((a, b) => b.value - a.value);
    return indexedArray.map((item) => item.index);
}

Different row heights and column widths between ExcelJS import and export

I’m developing a component using Vue.js and ExcelJS to open and edit xlsx files. However, I’ve encountered the following issues:

Column width:

  • Array shows: 4.5
  • Source Excel file displays: 3.88
  • Exported file displays: 3.71

Row height:

  • Array shows: undefined
  • Source Excel file displays: 16.5
  • Exported file displays: 15

Current Implementation

<template>
  <el-dialog v-model="visible" title="編輯 Excel" width="80%">
    <input type="file" @change="handleFile" accept=".xlsx, .xls" />
    <el-button @click="saveExcel">儲存 Excel</el-button>
    <vue-office-excel :src="excelUrl" v-if="excelUrl" />
    <template #footer>
      <el-button @click="visible = false">關閉</el-button>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, watch } from "vue";
import ExcelJS from "exceljs";
import VueOfficeExcel from "@vue-office/excel";
import "@vue-office/excel/lib/index.css";

const props = defineProps({ modelValue: Boolean });
const emit = defineEmits(["update:modelValue"]);
const visible = ref(false);
const workbook = ref(null);
const excelUrl = ref("");

// Load Excel File
const handleFile = async (event) => {
  const file = event.target.files[0];
  if (!file) return;
  const reader = new FileReader();
  reader.onload = async (e) => {
    const buffer = e.target.result;
    workbook.value = new ExcelJS.Workbook();
    await workbook.value.xlsx.load(buffer);


    const worksheet = workbook.value.worksheets[0];
    console.log("Readed:", worksheet.getRow(1).values);

    // Check Height and Width
    const columnWidths = worksheet.columns.map((col) => col.width);
    console.log("columnWidths:", columnWidths );
    const rowHeights= worksheet.columns.map((col) => col.height);
    console.log("rowHeights: ", rowHeights);

  };
  reader.readAsArrayBuffer(file);
};


const saveExcel = async () => {
  if (!workbook.value) return;
  const newFileName = "Edited_Quote.xlsx";
  const buffer = await workbook.value.xlsx.writeBuffer();
  const blob = new Blob([buffer], {
    type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  });
  // download Excel file
  const link = document.createElement("a");
  link.href = URL.createObjectURL(blob);
  link.download = newFileName;
  link.click();

  excelUrl.value = URL.createObjectURL(blob);
};


watch(() => props.modelValue, (val) => { visible.value = val; });
watch(visible, (val) => { emit("update:modelValue", val); });
</script>

I’ve been struggling with this issue for quite a while. I would really appreciate if someone could help me resolve this…

Can’t read “csrftoken” from cookies (Django)

I am working on a Django backend. The backend is deployed on Render, and I am testing email validation logic by sending OTP but for that i need to send “csrftoken” for the POST request.

At first the cookie was not getting set in cookies in firefox (but was getting set it chrome), since it was asking for the cookie to have partitioned attribute set, since django does not yet support it, i had to include a custom middleware to do it.

Now currently for some reason i am not able to read the csrftoken.

views.py:

@ensure_csrf_cookie
def register(request):
    if request.method == "POST":
        ......
    elif request.method == "GET":
        return JsonResponse({"message": "GET request handled"}, status=200)

prod.py:

from .common import *

CSRF_TRUSTED_ORIGINS = [
    "http://127.0.0.1:8001",
    "http://localhost:8001",
    "https://forkmemaybe.github.io/temp/",
]
CSRF_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SECURE = True

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
    "http://127.0.0.1:8001",
    "http://localhost:8001",
    "https://forkmemaybe.github.io/temp/",
]

SESSION_COOKIE_SAMESITE = "None"
SESSION_COOKIE_SECURE = True

HTML page i am making requests from URL: [http://127.0.0.1:8001/live.html]

    ......
    <script>
        function getCookie(name) {
            let cookieValue = null;
            console.log(document.cookie);
            if (document.cookie && document.cookie !== '') {
                const cookies = document.cookie.split(';');
                for (let i = 0; i < cookies.length; i++) {
                    const cookie = cookies[i].trim();

                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) === (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));

                        break;
                    }
                }
            }

            return cookieValue;
        }

        fetch("https://App-Name.onrender.com/register/", {  // Initial GET request
            method: "GET",
            credentials: "include",
        })
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            

            // const csrftoken = response.headers.get("X-CSRFToken"); 
            const csrftoken = getCookie("csrftoken");
            console.log(csrftoken);
            
            if (!csrftoken) {
                console.error("CSRF token not found in headers!");
                document.getElementById("errorMessage").textContent = "CSRF token not found. Please refresh the page.";
                return;
            }
            
            console.log("CSRF Token from header:", csrftoken);
           ........

browser chrome

On browser page i am getting CSRF token not found. Please refresh the page.

live.html:169 
live.html:199 null
live.html:202 CSRF token not found in headers!

cookie is getting set in chrome.

it has HttpOnly unchecked, Secure checked, SameSite None, Partition Key Site set to "http://127.0.0.1", Cross Site is checked.

browser firefox

On browser page i am getting “CSRF token not found. Please refresh the page.”

This page is in Quirks Mode. Page layout may be impacted. For Standards Mode use “<!DOCTYPE html>”.
live.html
A meta tag attempting to declare the character encoding declaration was found too late, and the encoding was guessed from content instead. The meta tag needs to be moved to the start of the head part of the document. live.html:147:1
<empty string> live.html:169:21
null live.html:199:21
CSRF token not found in headers! live.html:202:25
    <anonymous> http://127.0.0.1:8001/live.html:202

cookie is not getting set.

I tried providing an HTTPS environment by hosting my HTML page on github pages but the outcome was same as above.

Question:

  • Why is document.cookie returning null when the CSRF token is clearly visible in DevTools?
  • How can I correctly extract the CSRF token and send it in the request so that Django accepts it?
  • Is there any additional security setting that prevents JavaScript from accessing cookies?

Would appreciate any insights.