setTimeout + setInterval behave inconsistently in inative tab on Chrome

I’m trying to implement a simple behavior where a function starts running repeatedly after a delay once the user leaves the browser tab (i.e., switches to another tab). For example:

After 3 seconds of the tab being inactive,

Start toggling tab title every 2 seconds.

This works perfectly in Firefox, but in Chrome, the timing is inconsistent:

The delayed setTimeout seems to trigger,

The setInterval starts toggling,

Then it stalls unpredictably — sometimes after a few iterations (e.g., 18 toggles), then resumes later, or stops entirely for a while.

window.addEventListener("blur", () => {
    const delayBeforeStart = 10000;
    const blinkInterval = 2000;

    const originalTitle = document.title;
    const alternateTitle = "Come back!";

    let intervalId = null;

    const timeoutId = setTimeout(() => {
        if (document.hidden) {
            intervalId = setInterval(() => {
                document.title = document.title === originalTitle ? alternateTitle : originalTitle;
            }, blinkInterval);
        }
    }, delayBeforeStart);

    window.addEventListener("focus", () => {
        clearTimeout(timeoutId);
        if (intervalId) {
            clearInterval(intervalId);
            intervalId = null;
            document.title = originalTitle;
        }
    });
});


Works smoothly in Firefox.

In Chrome, the interval often gets throttled or even stops entirely after some time when the tab is in the background.

This appears related to Chrome’s background timer throttling

Flexdatalist show code as text when search for text in HTML

I’m using flexdatalist. In this datalist, I’m putting html at end of visible property to show ID.

When each for some letter like “a” or “smal” you will see the same search line but with the html code showed as text instead of being interpreted.

example of issue

(here, the red part isn’t wanted and should not appear)

Here is an example of code:

const users = [
  {
    UserId: 1,
    Identity: "User A",
    Title: "M."
  },
  {
    UserId: 2,
    Identity: "User B",
    Title: "M."
  },
  {
    UserId: 3,
    Identity: "User C",
    Title: "Mlle."
  },
  {
    UserId: 4,
    Identity: "User D",
    Title: "Ms."
  }
];

for(var user of users) {
  user.VisualName = user.Title + " " + user.Identity + "<small>#" + user.UserId + "</small>";
}

$("#User").flexdatalist({
    minLength: 0,
    visibleProperties: ["VisualName"],
    searchIn: "VisualName",
    valueProperty: "UserId",
    textProperty: "Identity",
    searchContain: true,
    selectionRequired: true,
    noResultsText: "no entries",
    data: users
});
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-flexdatalist/2.3.0/jquery.flexdatalist.min.js" integrity="sha512-JEX6Es4Dhu4vQWWA+vVBNJzwejdpqeGeii0sfiWJbBlAfFzkeAy6WOxPYA4HEVeCHwAPa+8pDZQt8rLKDDGHgw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-flexdatalist/2.3.0/jquery.flexdatalist.min.css" integrity="sha512-feX1WY95JHQ//uffCG42xy0PofA6DAZSEgIQMMOmRBbSmmOoz/J52azXXLM3kYa9EkQ5PlOHFFCgqpnPJ1oYpg==" crossorigin="anonymous" referrerpolicy="no-referrer" />

<div class="row offset-2 col-8">
    <label for="User" class="form-control-label">Users</label>
    <div class="input-group">
        <input class="form-select flexdatalist" id="User" name="User" placeholder="Search a user ...">
        <button class="btn btn-secondary" type="button" name="UnselectUser"><i class="bi bi-trash"></i></button>                        
    </div>
</div>

How can I fix it?

How to correlate requests and responses using Axios interceptors?

I’m using Axios to make HTTP requests, often concurrently, e.g. using Promise.all. In this scenario, I can’t rely on the order of requests and responses to correlate them.

I want to reliably match each response (including errors) to its original request, even when they happen concurrently.

So far, I’ve identified two possible solutions:

  • Use a request interceptor to attach a unique ID to the headers. Later, read this ID
    in the response interceptor to correlate the pair.
  • Store the Axios request config from the request interceptor. Compare
    it with the response.config when the response comes back.

Are there any better or more efficient ways to correlate requests and responses. Is there a best practice for handling this, especially when dealing with concurrent requests?

Math ain’t Mathing — Why is my MET-based calorie calculator returning massive numbers at low speeds?

Video link of Bug :

// Dropdown handling
const exerciseItems = document.querySelectorAll("#exercise-dropdown li");

exerciseItems.forEach(item => {
    item.addEventListener('click', () => {
        document.getElementById("exercise-type").textContent = item.textContent;
    }); 
});

const weightInput = document.getElementById('weight');
const distanceInput = document.getElementById("distance");
const speedInput = document.getElementById("speed");
const caloriesInput = document.getElementById('Calories');
const submitButton = document.getElementById('submit');
const hiddenElements = document.querySelectorAll('.hidden');


function getMet(type) {
    const speed = speedValue(speedInput);
    const t = type.toLowerCase();
    if (t === 'walking') return speed < 3 ? 2.0 : speed < 5 ? 2.8 : 3.5;
    if (t === 'running') return speed < 8 ? 6.0 : speed < 10 ? 8.0 : 10.0;
    if (t === 'biking') return speed < 16 ? 4.0 : speed < 20 ? 6.8 : 8.0;
    if (t === 'swimming') return speed < 2 ? 4.8 : speed < 4 ? 5.8 : 7.0;
    return 4.0;
}

function computeCal(w, m, d, s) {
    return (m * 3.5 * w / 200) * ((d / s) * 60);
}

const clicking = e => {
    e.preventDefault();
    const distance = distanceValue(distanceInput);
    const speed = speedValue(speedInput);
    const weight = weightValue(weightInput);
    const exercise = document.getElementById("exercise-type").textContent;
    const met = getMet(exercise);
    const calories = computeCal(weight, met, distance, speed);
    caloriesInput.textContent = `Calories Burned : ${calories.toFixed(2)}`;
};
<section id="section1" class="hidden">
        <div id="calculator-box">
            <h3 id="exercise-type">
                Walking
            </h3>
            <form>
                <label for="weight" class="dis2">Weight (kg)</label>
                <input id="weight" class="input" type="number" inputmode="numeric" min="0" max="180" step="any">
                <br>
                <label for="distance" class="dis2">Distance (km)</label>
                <input id="distance" class="input" type="number" inputmode="numeric" min="0" max="1000" step="any">
                <br>
                
                <label for="speed" class="dis2">Speed (km/h)</label>
                <input id="speed" class="input" type="number" min="0" max="100" inputmode="numeric" step="any">
                <br>
                <p id="Calories">Calories Burned : 0</p>
                <button id="submit" type="button" inputmode="numeric">Calculate</button>
            </form>
        </div>
    </section>

I’m pretty sure the main issue is related to the speed input, but I’ve noticed that the overall calculations feel off too. I’ve included a video in the link above that shows the bug in action.

Here’s what’s happening:
• When I increase the speed (for example, going from 11 to 12 km/h), the calories burned goes down.
• On the flip side, when I enter a very low speed, the calorie estimate becomes really high.

From what I understand, this might be because the duration is being calculated as distance ÷ speed, so a lower speed = longer duration = more calories burned. But it doesn’t feel accurate — especially when super low speeds cause calorie numbers that seem way too high.

I’m not sure if the formula is correct, or if I should be capping values or adjusting METs differently.

Please take a look if you can — I’d really appreciate the help. More details are explained in the video above. Thank you!

Retrieve all selected(not necessarily active) tabs in chrome extension

In each chrome window, there can be multiple selected tabs(by shift click), but there is one and only one active tab, which is generally the mostly recently selected one.

In chrome extension you can use chrome.tabs.onActivated.addListener((activeInfo) => {} or chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {} to get active tab(s).

The question is how to retrieve tabs that are selected on each window.

Trigger callback when N tab stops away from a target element (for smarter prefetching) in JavaScript

I’m building a JavaScript library called ForesightJS for smart prefetching. It currently predicts mouse movements and prefetches content accordingly. I now want to support keyboard users.

Goal:

Given a focusable (tabbable) element and a number N, I want to trigger a callback when the user tabs to an element that is N steps before the subscribed element.

Example:

Say there are 8 focusable elements. If the 6th element is the target and N = 2, then when the user focuses (tabs to) the 4th element, the callback for the 6th element should fire.

How can I detect and track tab navigation like this in the most performant way possible?

How to override a “shadow-root” style for a element that doesn’t have an ID or CLASS

Dose someone know how to hide or remove this “shadow-root” HTML div or toolbar element below? I tried somne JavaScript’s but they all were for div’s that have an ID or CLASS. If JavaScript is required for this then something in ES5 would be nice, if it’s possible.

I tried it also with CSS but it didn’t work. -_-

[style*="display: initial !important;forced-color-adjust: initial !important;"] {
  display: none !important;
}

Here is the HTML code that is getting inserted by an external JavaScript file that I try to hide or remove.

<div style="... display: initial !important;forced-color-adjust: initial !important;mask: initial !important;math-depth: initial !important;position: initial !important;position-anchor: initial !important; ...>
#shadow-root (open)
    <link rel="stylesheet" href="https://SomeCSS.url">
    <style>... .tg-container {position: fixed;}.tg-menu {position: absolute; bottom: 0; display: flex; flex-direction: column;}.tg-menu:not(.ft-multiple-buttons *) ...</style>
    <toolbar id="ft-floating-toolbar" class="tg-container tg-left" style="z-index: 2147483647; bottom: 50px; left: 10px;"><div class="tg-menu"><div class="tg-button-holder"></toolbar>
</div>

EDIT: This div has no child and it’s always added at the end of the “/body” tag and it no always added.

Sometimes no response is displayed, even though HTTP 200 is returned

I’m building a lightweight personal chatbot that uses the OpenAI Chat API. The chatbot is embedded into a custom HTML/JS frontend and runs on a PHP backend that sends the prompt and gives back the result using curl_setopt($ch, CURLOPT_WRITEFUNCTION, ...).

It works most of the time — but occasionally (especially on the second or third message), the frontend just hangs. In Chrome DevTools I can see the following:

Status: 200 OK
Response time: <300ms (very fast — suspiciously fast)

But: No text is ever rendered in the chat window

The request body is sent correctly and the request completes — but no chunks are received or displayed.

Here’s the php code, I’m using:

<?php

session_start();
error_reporting(E_ALL);
ini_set('display_errors', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
ob_implicit_flush(true);

header('Content-Type: text/plain');
header('Cache-Control: no-cache');
header('Access-Control-Allow-Origin: *');

require_once 'config.php';

$systemPrompt = file_get_contents('prompt.txt');

if (!isset($_SESSION['chat_history'])) {
    $_SESSION['chat_history'] = [
        ["role" => "system", "content" => $systemPrompt]
    ];
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    $userMessage = trim($input['message'] ?? '');

    if ($userMessage === '') {
        http_response_code(400);
        echo "Leere Eingabe";
        exit;
    }

    $_SESSION['chat_history'][] = ["role" => "user", "content" => $userMessage];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://api.openai.com/v1/chat/completions");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Content-Type: application/json",
        "Authorization: Bearer " . OPENAI_API_KEY
    ]);

    $chatPayload = [
        "model" => MODEL,
        "messages" => $_SESSION['chat_history'],
        "temperature" => TEMPERATURE,
        "max_tokens" => MAX_TOKENS,
        "stream" => false
    ];

    $fullReply = '';

    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($chatPayload));

    curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use (&$fullReply) {
        $lines = explode("n", $data);
        foreach ($lines as $line) {
            if (strpos($line, 'data: ') === 0) {
                $jsonStr = substr($line, 6);
                if (trim($jsonStr) === '[DONE]') break;

                $json = json_decode($jsonStr, true);
                if (isset($json['choices'][0]['delta']['content'])) {
                    $chunk = $json['choices'][0]['delta']['content'];
                    echo $chunk;
                    flush();
                    $fullReply .= $chunk;
                }
            }
        }
        return strlen($data);
    });

    curl_exec($ch);
    curl_close($ch);

    $_SESSION['chat_history'][] = ["role" => "assistant", "content" => $fullReply];

} else {
    http_response_code(405);
    echo "Method not allowed";
}

// Fehlerausgabe, falls nötig
if (isset($ch) && curl_errno($ch)) {
    echo "cURL-Fehler: " . curl_error($ch);
    exit;
}

Does anyone have an idea, why this is happening?

What I’ve ruled out:

  • It’s not an invalid API key — most requests work just fine
  • It’s not a missing prompt.txt — it’s read and logged successfully
  • It’s not a quota issue — no errors from OpenAI, no “insufficient quota”
  • It’s not a general connection problem — the server has internet and cURL works
  • It’s not a JS bug on the first request — only later requests (2nd or 3rd) hang

ERR_SSL_PROTOCOL_ERROR on Subdomains for Laravel Multi-tenant App (stancl/tenancy, Single DB) on Hostinger

I’ve deployed a multi-tenant Laravel application using the stancl/tenancy package. It’s configured with a single database and hosted on Hostinger’s hPanel business plan.

The application works perfectly fine on the central domain (e.g., maindomain.com). However, when I try to access any of the tenant subdomains (e.g., tenant1.maindomain.com, tenant2.maindomain.com), I encounter the ERR_SSL_PROTOCOL_ERROR in my browser.

Here’s a summary of my setup:

Laravel Version: (Specify your Laravel version here)
stancl/tenancy Version: (Specify your stancl/tenancy version here)
Database: Single database for all tenants.
Hosting: Hostinger hPanel business.
SSL: I have an SSL certificate installed for my main domain (and I believe it should cover subdomains).
Things I’ve already checked/tried:

Ensuring the central domain has a valid SSL certificate.
Checking the Hostinger SSL settings for the domain and subdomains.
Reviewing my Laravel and tenancy configuration files (though I’m not sure what to look for regarding SSL on subdomains).
Clearing browser cache and trying different browsers.
The fact that the central domain works correctly suggests the base SSL setup might be okay, but there seems to be an issue specifically with the subdomains.

Has anyone encountered this issue before with stancl/tenancy and Hostinger (or similar setups)? Any insights or troubleshooting steps would be greatly appreciated. I’m not sure where to look next to resolve this SSL error on my tenant subdomains.

Thank you in advance for your help!

Please remember to replace the placeholders for Laravel and stancl/tenancy versions with your actual versions. You might also want to include relevant snippets from your config/tenancy.php file if you think they might be related (be careful not to expose any sensitive information). Good luck!

How to turn a PHP generated page dark? [duplicate]

I’m serving a dead simple page on the raspberry:

<?php
 
 
require 'Parsedown.php';  # A markdown parser.
 
 
$h = date('H');
if( $h < 5 )
{
    $pic = 'res/cyber_rat.png';
}
elseif( $h < 11 )
{
    $pic = 'res/day_rat.png';
}
elseif( $h < 20 )
{
    $pic = 'res/saint_rat.png';
}
else
{
    $pic = 'res/asm_rat.png';
}
 
$readme = file_get_contents('/opt/rat/README.md');
 
# As a text file
#$readme_html = nl2br(htmlspecialchars($readme));  # Convert line breaks and escape HTML
 
# Or as parsed markdown.
$Parsedown = new Parsedown();
$readme_html = $Parsedown->text($readme);  # Convert Markdown to HTML
 
 
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>rat</title>
    <link rel="icon" type="image/png" href="res/icon.png">
</head>
<body>
    <img src="<?php echo $pic; ?>" alt="Saint Rat" style="max-width: 100%; height: auto;" />
    <pre><span class="inner-pre" style="font-size: 14px"><?php echo $readme_html; ?></span></pre>
 
</body>
</html>

Any idea how to turn this into something a visitor would experience if they were using a “dark theme” browser plugin?

PHP class not found when using name spacing

I am trying to load my class through namespacing but Im struggling to find the right configuration and I keep getting a class not found error.

Here is the psr-4 property in my composer.json

"psr-4": {
    "Namespaceone\": [
        "src/core/Modules/Moduleone",
        "src/core/Modules/Moduletwo",
    ],
    "Volvo\": [
        "src/programme/volvo/",
        "src/programme/volvo/volvoxc"
    ]
}

Here is the file structure of src/programme/volvo:

- init.php  //Contains new Volvo()
- Volvo.php //Class Volvo
- volvoxc/
  - Volvoxc.php //Class Volvoxc extends Volvo

The files and classes:

init.php

$volvo = new Volvo(); //This returns all the properties from Volvo()

Volvo.php

namespace Volvo;
use Volvoxc;
class Volvo {
  $volvoxc = new Volvoxc();
}

Volvoxc.php //This class can not be found

namespace Volvo;
class Volvoxc extends Volvo {}

Unable to run Laravel Apps on latest Ubuntu 25.04 [closed]

Yesterday, I upgraded my Ubuntu 24 to 25.04, which I should not have. Now I am not able to run Laravel Applications. Apache2 is having troubles, I guess. Says could not find driver, but I have mysql and pdo_mysql installed. Everything is installed as required for Laravel to run, but I am still getting this error. Help appreciated.

Middleware Validates Invalid ObjectId Locally (Returns 400), but Deployed Backend Throws 500 During Database Query [closed]

when i am trying to enter invalid objectId in frontend in a deployed env it is giving 500 internal server error with displaying not a valid Object ID which comes when querying through database but when i kept localhost as backendUrl and try to hit it without any changes it is working fine as expected as it is getting failed at middleware what might be the issue over here to show 500 instead of 4xx error. We have a middleware validation for all the fields and it is working fine when I am using localhost and working without any issues??

I have entered an invalid String which actually expects an ObjectId. In this case it should show invalid object Id and should give an 400 error but it is displaying 500 error.

How to make eslint catch ReferenceErrors

I use eslint from NodeJS to check my code and it seems not to catch ReferenceErrors. Here is a minimal example, mre.js:

console.log(obj);
let obj = 2;

When I run eslint, I get no warnings:

$ ./node_modules/.bin/eslint mre.js

I updated eslint and confirm it is the latest version (9.2.6):

$ npm update eslint
$ npm info eslint version           
9.26.0

How can I make eslint catch ReferenceErrors, or what is a linter that catches them?

setTransform visually incorrect for a reflected image (translated/rotated/scaled), but matrixTransform applied to the original coordinates works

My vanilla JS program has two canvases. The user draws arbitrary straight lines on an image on the first canvas, and pieces taken from splitting the canvas by the line are drawn on the second canvas, some of them reflected around the line (using reflectPiece). I draw the reflected points of the piece directly on the second canvas, then apply a reflection matrix transform (derived with getReflectionMatrix) so that I can rotate/scale the image appropriately, then draw the image clipped to the outline.

This works perfectly for most combinations that I have tried, but I have found a line that it will not work for and cannot work out why. It is as if the reflection is not applying (or doubling) or something, so the image is rotated to the wrong angle and its angle doesn’t align with the unmodified outline of the reflected points.

  1. In my example code, the unmodified outline after reflection is drawn
    in black and blue using two approaches – point for point
    from reflectPiece, and using DOMPoint.matrixTransform to
    transform the original points with the reflection matrix in order to show the
    matrix doesn’t work the same both times – and filled in with both
    yellow and blue (green). These are the intended points.
  2. Then I draw
    the reflected points with the reflection matrix applied as a
    transform in red, to show the result of doubling the reflection.
  3. Then I draw the image with the transform applied like I do in my
    main code, and the angle aligns with the double-rotated red piece, when it should
    align with the one that’s coloured in green.

I’m sure it’s something really basic and I feel silly asking, but I’ve been stuck for days somehow 🙁 Geometry is not my forte.

Broken:

Screenshot of broken shape

Working:

Screenshot of working shape

Here is my debug code:

<canvas id="canvas" width="800" height="800"></canvas>
<img src="debug.png" id="image" style="display: none">
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const img = document.getElementById("image");

const intersects = [new DOMPoint(143.07549812764975, 58.35350167526056),
new DOMPoint(198.3825597334491, 8.86823602796649)];
const points = [
    new DOMPoint(198.38255973344914, 8.868236027966528),
    new DOMPoint(162.65825355141538, 359.09787638799855),
    new DOMPoint(112.9163540869709, 354.0240772523315),
    new DOMPoint(143.0754981276499, 58.3535016752607),
];
const line = { startX: 250.4616879421679, startY: -37.7288786850976, endX: 103.53831205783209, endY: 93.7288786850976 };

const reflected = reflectPiece(points, line);
const transform = getReflectionMatrix(line);
const mapped = mapTransform(points, transform);

const canvasTransform = new DOMMatrix().translateSelf(canvas.width/2, canvas.height/2);
const drawTransform = canvasTransform.multiply(transform);
ctx.setTransform(canvasTransform);

img.onload = () => {
    debug();
}

function debug() {
    drawMatrixTransformedPoints();
    drawReflectedWithTransform();
    drawReflectedPoints();
    drawImage();
}

function drawMatrixTransformedPoints() {
    ctx.strokeStyle = "black";
    tracePiecePath(mapped);
    ctx.stroke();
    ctx.fillStyle = "yellow";
    ctx.fill();
}

function drawReflectedPoints() {
    ctx.strokeStyle = "blue";
    tracePiecePath(reflected);
    ctx.stroke();
    ctx.globalAlpha = 0.25;
    ctx.fillStyle = "cyan";
    ctx.fill();
    ctx.globalAlpha = 1;
}

function drawReflectedWithTransform() {
    ctx.save();
    ctx.strokeStyle = "red";
    ctx.setTransform(drawTransform);
    tracePiecePath(reflected);
    ctx.stroke();
    ctx.restore();
}

function drawImage() {
    ctx.globalAlpha = 0.5;
    ctx.save();
    ctx.setTransform(drawTransform);
    ctx.drawImage(img, 0, 0);
    ctx.restore();
    ctx.globalAlpha = 1;
}

function tracePiecePath(points) {
    ctx.beginPath();
    const firstPoint = points[0];
    ctx.moveTo(firstPoint.x, firstPoint.y);
    points.slice(1).forEach(point => {
        ctx.lineTo(point.x, point.y);
    });
    ctx.closePath();
}

function getReflectionMatrix(line) {
    const matrix = new DOMMatrix();
    const origin = getMidlinePoint(...intersects);
    const angle = getAngleFromOrigin(line);
    const angleInDegrees = angle * 180 / Math.PI;
    matrix.translateSelf(origin.x, origin.y);
    matrix.rotateSelf(angleInDegrees);
    matrix.scaleSelf(1, -1);    
    matrix.rotateSelf(-angleInDegrees);
    matrix.translateSelf(-origin.x, -origin.y);
    return matrix;
}

function getMidlinePoint(pt1, pt2) {
    const x = (pt1.x + pt2.x)/2;
    const y = (pt1.y + pt2.y)/2;
    return new DOMPoint(x, y);
}

function getAngleFromOrigin(line) {
    const { startX, endX, startY, endY } = line;
    const dx = endX - startX;
    const dy = endY - startY;
    return Math.atan2(dy, dx);
}

function reflectPiece(points, line) {
    const normal = this.getNormalVector(line);
    const newPoints = [];
    for (let i = 0; i < points.length; i++) {
        newPoints.push(this.reflectPoint(line, points[i], normal));
    };
    return newPoints;
}

function getNormalVector(line) {
    const { startX, endX, startY, endY } = line;
    const dx = endX - startX;
    const dy = endY - startY;
    const len = Math.hypot(dx, dy);

    const nx = -dy / len;
    const ny = dx / len;
    return { nx, ny };
}
function reflectPoint(line, point, normal) {
    const { x, y } = point;
    const { startX, startY } = line;
    const { nx, ny } = normal;

    const vx = x - startX;
    const vy = y - startY;
    const dot = vx * nx + vy * ny;
    const rx = startX + vx - 2 * dot * nx;
    const ry = startY + vy - 2 * dot * ny;
    return new DOMPoint(rx, ry);
}

function mapTransform(points, m) {
    return points.map(point => point.matrixTransform(m));
}
</script>

Here is an example of a working shape (aligns with the green shape, not the red one):

const intersects = [new DOMPoint(256.8378378378378, 50),
new DOMPoint(258.18918918918916)];
const points = [new DOMPoint(369.29268292682923, 50),
new DOMPoint(256.83783783783787, 50),
new DOMPoint(258.1891891891892, 0),
new DOMPoint(316.8536585365853, 0)];
const line = { startX: 267.0952908723996, startY: -329.5257622787839, endX: 247.90470912760043, endY: 380.5257622787839 };

debug.png: debug.png

Thanks!