js v8 function inlining

It mentioned, that v8 can inline function, i get how it works in obvious cases, like this

// object with persistent shape
class Obj {
    prop = 42
}
// function always called with persistent types/shapes of args
function obj_accessor(obj) {
    return obj.prop;
}

function main() {
    const obj = new Obj();
    // this call is highly likely to be inlined
    const val = obj_accessor(obj);
    console.log(val);
}

but if i use higher order functions, in what cases it will perform inlining, if in any?

I have some examples

class Foo {
    bar = 42;
}

class Bar {
    foo = 42;
}

function foo_accessor(obj) {
    return obj.bar;
}
function bar_accessor(obj) {
    return obj.foo;
}

function main(obj, accessor) {
    // will inlining be performed at all ?
    // is it per call site or per function
    // so after passing more then single accessor it
    // can not be optimized any more ?
    const val = accessor(obj);
    console.log(val)
}

// accessor not statically know -> no inlining ? 
main(...(Math.random() < 0.5 ? [new Foo(), foo_accessor] : [new Bar(), bar_accessor])); 

// accessor statically know -> it can be inlined
main(new Foo(), foo_accessor);

// accessor statically know -> it can be inlined
main(new Bar(), bar_accessor);

Laravel Filepond not including file input with Post or Put in Request

I had this filepond instance:

<input type="file" name="files[]" class="filepond" multiple />

<script src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"></script>
<script src="https://unpkg.com/filepond-plugin-image-exif-orientation/dist/filepond-plugin-image-exif-orientation.js"></script>
<script src="https://unpkg.com/filepond-plugin-file-validate-type/dist/filepond-plugin-file-validate-type.js"></script>
<script src="https://unpkg.com/filepond/dist/filepond.js"></script>
<script>
            const inputElement = document.querySelector('input[type="file"].filepond');
            
            FilePond.registerPlugin(FilePondPluginImagePreview,
                                    FilePondPluginImageExifOrientation,
                                    FilePondPluginFileValidateType);
            FilePond.create(inputElement).setOptions({
                allowMultiple: true,
                maxFiles: 3,
                acceptedFileTypes: ['image/*'],
                allowImagePreview: true,
                server: {
                    process: '/upload/process',
                    revert: '/upload/remove',
                    load: '/upload/load/',
                    remove: (source, load, error) => {
                        $.ajax({
                            type:'DELETE',
                            url:'/upload/removelocal',
                            headers: {
                                'X-CSRF-TOKEN': '{{ csrf_token() }}'  
                            },
                            data: { filename: source, userid: {{ auth()->user()->id }} },
                        });
                        load();
                    },
                    headers: {
                        'X-CSRF-TOKEN': '{{ csrf_token() }}',
                    },
                },
                files: [
                    @foreach ($attachments as $attachment)
                        {source: '{{ $attachment->filename }}', options: {type: 'local'}},
                    @endforeach
                ],
            });

I was working on this project and then stopped and started again recently. When I did, I updated everything including going from Laravel 11 to 12. Everything else works, the file initially gets copied to a tmp location (from my process function), the remove function, the prepopulate with existing images, everything, except filepond isn’t including the files[] input in my post back to my Update function. The Request object has all my other fields except file[]. It used to be there. This same code worked perfectly.

My process function (/upload/process): (This is still working, the file makes it to my tmp area)

public function process(Request $request): string
{
     $file = $request['files'][0];
     $ext = pathinfo($file->getClientOriginalName(), PATHINFO_EXTENSION);

     $path = $file->storeAs('tmp', now()->timestamp.'-'.Str::random(20).'.'.$ext, "private");

     return $path;
}

My Controller: No sign of files[], which used to be there. This is where I would copy from tmp to it’s permanent folder.

public function update(Request $request, string $id)
{
     Log::Debug($request->all());
     dd($request);
}

No errors in Chrome debugger console, Looking at the request post from network tab shows just the other fields on the form, not files[]. No errors in Laravel Log:

[2025-06-15 16:52:56] local.DEBUG: array (
  '_method' => 'PUT',
  '_token' => 'DxmXn00LeEZ7Qc1FqV6dFft8m4pkjTSvj7Y8N5un',
  'title' => 'aaa rtfgf',
  'text' => '<p>fgdfgdf</p>',
  'tag_list' => 
  array (
    0 => '11',
  ),
  'contact_list' => 
  array (
    0 => '13',
    1 => '18',
  ),
)  

Any help would be greatly appreciated! Thanks.

How can I integrate an AI to check the validity of posts and answers made in an app(Quora) [closed]

I have a school project to clone an application (Quora) and we’re asked to implement something that isn’t already in the app. I decided on an AI fact checker that will check the validity of posts and answers.It would take the answer or post then use relevant sources like wikipedia or google to check its validity. But I don’t really know how I’ll integrate an ai( I don’t even have an AI in mind). So I wanted to ask for directions and hints on how I could accomplish this task.Also I’m supposed to use react-native and javascript exclusively, so I would appreciate it if answers were relevant to it. THANK YOU

inserted Google map can’t click transport station only

I inserted google map into website using javascript. But I can click everything such as resturant, school, park and it will show me pop up provided from google map place api. However only transport are not click able. Train station, airport, subways I can’t click those on the map.

I haven’t changed anything from google map flatforms, using libraries=places&callback api.
Also style sheet does nothing to map except sizing again.

window.initMap = function() {
        map = new google.maps.Map(document.getElementById('map'), {
            center: { lat: 20, lng: 90 }, // 서울의 위도, 경도
            zoom: 2.3,
            minZoom: 2.3,
            mapTypeId: "roadmap",
            restriction: {
                latLngBounds: {
                    north: 85,
                    south: -85,
                    west: 0,
                    east: 360
                },
                strictBounds: true
            },
            zoomControl: false,
            mapTypeControl: false,
            scaleControl: true,
            streetViewControl: false,
            rotateControl: false,
            fullscreenControl: true,
            clickableIcons: true,
        });

Wix Visual Glitch After Minify

So i have been building a Website on Wix for a while. i work with alot of iFrames and Velo. Today i decided to to minify everything which made the Site way faster, however switching Tabs on my Tabbed Site sometimes (not every time and kinda random) causes a visual Glitch where within a split second the Tabs switch multiple times before the Final Tab is shown.
Basically if i go from tab A to B it might do A->B->A->B super fast, barely visible but still irritating.

First i tried to set a timeout before Pageload which didnt fix the Glitch, then i also tried using the Unminified Code for the Menu + Site Code but it still does the same Glitch now.

Background:
-My Site uses Wix #Tabs to split Content into Sections.
-Each Section is filled with HTML iFrames mostly.
-I Created a Custom TabMenu using an iFrame, which posts a message with the TabID to my Site when a Item is selected
-My Site Code then hides the Menu and triggers a TabChange + updates breadcrumbs.
-also theres a button to open the menu

I know the Approach isnt optimal but it works well, or atleast did until today.
Heres the Frontend Snippet that Handles everything on the Message.


let lastTabId = null;
let debounceTimeout = null;

$w.onReady(function () {
    const Ticker = session.getItem("selectedTicker");
    const companyName = session.getItem("selectedCompanyName");

    const tabMap = {
        "#singleTab4": ["Company Snapshot", "Overview"],
        "#singleTab12": ["Company Snapshot", "Company Profile"],
        "#singleTab8": ["Company Snapshot", "News"],
        "#singleTab6": ["Market & Valuation", "Valuation / Forecast"],
        "#singleTab5": ["Market & Valuation", "Volatility"],
        "#singleTab15": ["Financial Health", "Financials"],
        "#singleTab24": ["Financial Health", "Profitability"],
        "#singleTab9": ["Ownership & Sustainability", "ESG Score"],
        "#singleTab14": ["Ownership & Sustainability", "Holding Structure"],
        "#singleTab16": ["Ownership & Sustainability", "Dividends"],
        "#singleTab21": ["Filings & Insider Data", "Financial Statements"],
        "#singleTab10": ["Filings & Insider Data", "SEC Filings"],
        "#singleTab17": ["Filings & Insider Data", "Insider Activity"],
        "#singleTab13": ["Strategic Analysis", "SWOT Analysis"],
        "#singleTab22": ["Strategic Analysis", "PESTEL Analysis"],
        "#singleTab23": ["Strategic Analysis", "Porters 5 Forces"],
        "#singleTab19": ["Strategic Analysis", "Scenario Analysis"],
        "#singleTab18": ["Advanced Tools", "Monte Carlo"],
        "#singleTab20": ["Advanced Tools", "Custom Ratios"],
        "#singleTab25": ["Advanced Tools", "AI Analysts"],
        "#singleTab26": ["Advanced Tools", "Compare"]
    };

    // Set initial breadcrumb
    $w("#html115").postMessage({
        breadcrumb: `<span style="color:#fd6262;font-weight:500;">${Ticker}</span> > Company Snapshot > Overview`
    });

    // Toggle menu iframe visibility
    $w('#button5').onClick(() => {
        const iframe = $w('#html113');
        iframe.hidden ? iframe.show() : iframe.hide();
    });

    // Handle messages from the menu iframe
    $w('#html113').onMessage(event => {
        const data = event.data;

       if (data?.type === "selectTab") {
            const tabId = data.tabId;
            const hideMenu = data.hideMenu;

            if (tabId !== lastTabId) {
                clearTimeout(debounceTimeout);
                debounceTimeout = setTimeout(() => {
                    lastTabId = tabId;
                    $w("#tabs1").changeTab($w(tabId));

                    const [category, tabName] = tabMap[tabId] || ["Unknown Category", "Unknown Tab"];
                    $w("#html115").postMessage({
                        breadcrumb: `<span style="color:#fd6262;font-weight:500;">${Ticker}</span> > ${category} > ${tabName}`
                    });

                    if (hideMenu) {
                        $w('#html113').hide();
                    }
                }, 60);  // Added this in trying to resolve the issue, does nothing
            }
        }
    });
});

New line in a X post

I wrote a browser extention for social media handling.

You can e.g. reformulate your given text to make it more positive etc.

Now if I insert the response to the X – Reply Textarea back it does not react to “n” chars… makes the text just in one block. I already tried “rn” and pressing the “enter” button.

Does anyone know how to work around this?

Current code:

function copyText(replyText, replyTextarea) {
                replyTextarea.focus();
                document.execCommand('insertText', false, replyText);
                replyTextarea.dispatchEvent(new Event('input', { bubbles: true }));
                clickedButton.innerHTML = originalContent;
                clickedButton.disabled = false;
                clickedButton.style.borderColor = '#666666';
                clickedButton.style.color = '#666666';
            }

Also tried:

function copyText(replyText, replyTextarea) {
                replyTextarea.focus();
                const parts = replyText.split('n');
                parts.forEach((part, index) => {
                    document.execCommand('insertText', false, part);
                    if (index < parts.length - 1) {
                        const enterEvent = new KeyboardEvent('keydown', {
                            key: 'Enter',
                            code: 'Enter',
                            keyCode: 13,
                            which: 13,
                            bubbles: true
                        });
                        replyTextarea.dispatchEvent(enterEvent);
                    }
                });
                
                replyTextarea.dispatchEvent(new Event('input', { bubbles: true }));
                clickedButton.innerHTML = originalContent;
                clickedButton.disabled = false;
                clickedButton.style.borderColor = '#666666';
                clickedButton.style.color = '#666666';
            }

Why is the response empty when handling an XmlHttpRequest load event?

I have an XmlHttpRequest with a listener on the load event:

xhr.upload.addEventListener('load', () => {
  if (xhr.status === 200) {
    resolve(UPLOAD_OK);
  }
  else {
    const message = JSON.parse(xhr.response)?.error;
    reject(new APIError(xhr.status, xhr.statusText, message));
  }
});

When I call JSON.parse(xhr.response), I get an exception:

Uncaught SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data

This is the exception you’d expect from calling JSON.parse on the empty string, but what I see as the response body in the browser console is {"error": "Already exists"}—which is the response I am expecting.

Why does this happen? Shouldn’t the response be set by the time the load event fires? If not, where is this documented and what event is the correct event for getting the response?

Explain difference of two JavaScript generator implementations of Heap’s permutation algorithm

I have two implementations of Heap’s permutation algorithm in JavaScript using generator functions and I would like to understand better how they are related under the hood. Please explain. They both run with Node.js. Since it is often the case that different implementations can be derived or deduced from each other, I would also be interested to get some such code transformation pointed out. Note, that I am not asking, which implementation is ‘better’, since that is an opinion, or which you like most.

Implementation 1:

function permute(N) {
    function swap(x, y) { [ P[x], P[y] ] = [ P[y], P[x] ] }
    function heap(n, i) { return (n % 2) || i }

    var P = []; for (let i = 1; i <= N; i++) P[i] = i;
    var count = 1;
    console.log(count++, ":", P.slice(1));
    
    for (p of permutations(N)) console.log(count++, ":", p.slice(1));

    function* permutations(n) {
        if (n > 1) {
            for (let i = 1; i <= n; i++) {
                yield* permutations(n - 1);
                if (i < n) { swap(n, heap(n, i)); yield P }
            }
        }
    }
}

permute(4);

Implementation 2:

function permute(N) {
    function swap(x, y) { [ P[x], P[y] ] = [ P[y], P[x] ] }
    function heap(n, i) { return (n % 2) || i }

    var P = []; for (let i = 1; i <= N; i++) P[i] = i;
    var count = 1;
    
    for (p of permutations(N)) console.log(count++, ":", p.slice(1));

    function* permutations(n) {
        if (n == 1) yield P;
        else {
            for (let i = 1; i <= n; i++) {
                yield* permutations(n - 1);
                if (i < n) swap(n, heap(n, i));
            }
        }
    }
}

permute(4);

Output:

1 ':' [ 1, 2, 3, 4 ]
2 ':' [ 2, 1, 3, 4 ]
3 ':' [ 3, 1, 2, 4 ]
4 ':' [ 1, 3, 2, 4 ]
5 ':' [ 2, 3, 1, 4 ]
6 ':' [ 3, 2, 1, 4 ]
7 ':' [ 4, 2, 1, 3 ]
8 ':' [ 2, 4, 1, 3 ]
9 ':' [ 1, 4, 2, 3 ]
10 ':' [ 4, 1, 2, 3 ]
11 ':' [ 2, 1, 4, 3 ]
12 ':' [ 1, 2, 4, 3 ]
13 ':' [ 1, 3, 4, 2 ]
14 ':' [ 3, 1, 4, 2 ]
15 ':' [ 4, 1, 3, 2 ]
16 ':' [ 1, 4, 3, 2 ]
17 ':' [ 3, 4, 1, 2 ]
18 ':' [ 4, 3, 1, 2 ]
19 ':' [ 4, 3, 2, 1 ]
20 ':' [ 3, 4, 2, 1 ]
21 ':' [ 2, 4, 3, 1 ]
22 ':' [ 4, 2, 3, 1 ]
23 ':' [ 3, 2, 4, 1 ]
24 ':' [ 2, 3, 4, 1 ]

I have based both generator functions on Robert Sedgewick’s paper, Permutation Generation Methods (1977), where on page 140 the author gives the following basic recursive algorithm:

To generate all permutations of P[1], …, P[N], we repeat N times the step: “first generate all permutations of P[1], …, P[N-1], then exchange P[N] with one of the elements P[1], …, P[N-1].”

In implementation 2 I have added the obvious recursion base case n==1 and a routinely chosen position of the yield statement. Also, to avoid “unnecessary” swaps during the last iteration of the for loop, there is the guard if (i < n). In this way, it seems to be equivalent to Heap’s recursive algorithm given in Wikipedia. In any case, when called with N = 4 it reproduces Heap’s enumeration as given in Sedgewick’s paper.

Also note that in implementation 1 the position of the yield statement is entirely different. I would like to know to what extent that is actually arbitrary or in what way it is determined.

How to block the page from loading in an inline script until async operations finish

I have this embed script that needs to be placed in the head of websites:

<script src="https://www.example.com/track.js?wid=123”></script>

It needs to execute before the page body renders (hence it presumably shouldn’t use async or defer – it’s for A/B testing, where inserting CSS late could cause content shift etc).

However, example.com may not always be reliable (i.e. the server could be down or slow, and I don’t want it blocking the page from loading for long; a small delay is ok – I assume the script with src attribute would delay the page from loading if added that way), hence I built this ‘abort-if-not-fast’ INLINE script (for the head):

<script>
(async () => {
  // If the body is already present, it's too late to inject A/B changes
  if (document.body) return;

  // Setup an AbortController to kill the request after n ms
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 5000);

  function delayedFetchSimulation(url, { signal }) {
    return new Promise((resolve, reject) => {
      // Simulate network latency (e.g., 6000ms)
      const delay = 6000;

      const timeout = setTimeout(() => {
        resolve(new Response("// simulated script content"));
      }, delay);

      // Abort logic
      signal.addEventListener("abort", () => {
        clearTimeout(timeout);
        reject(new DOMException("Aborted", "AbortError"));
      });
    });
  }

  try {
    const response = await delayedFetchSimulation("https://www.example.com/scripts/tracking/track.js?wid=demo", {
      signal: controller.signal
    });

    clearTimeout(timeoutId); // clear the timeout if fetch succeeded

    if (!response.ok) {
      console.warn("A/B test script failed to load (HTTP error).");
      return;
    }

    let scriptText;
    try {
      scriptText = await response.text();
    } catch (readErr) {
      console.warn("Failed to read A/B test script body:", readErr);
      return;
    }

    // Abort if body has now started rendering while we were fetching
    if (document.body) {
      console.log('Body already started rendering - returning early...');
      return;
    }

    // Inject the fetched script into the page
    const script = document.createElement("script");
    script.id = 'ab-testing-script';
    script.textContent = scriptText;
    document.head.appendChild(script);

  } catch (err) {
    console.log('body exists?', Boolean(document.body), document.body) // logs as true, meaning script does not prevent body from rendering
    console.warn("A/B test script not loaded in time or fetch failed:", err);
    // No need to throw — we fail silently on purpose
  }
})();
</script>

The problem is that it doesn’t seem to block the page from rendering the body until async operations finish. How can this inline script be made to block the page from rendering until async operations finish (or abort if the timeout takes too long to not prevent the page from loading)? Or alternatively is there another solution?

Get iframe src from external link [closed]

I have an link where always fresh domain update
https://lernodydenknow.info/current_domain?tid=1151232&enc=0&srv=rt_adblock_backup

I want any script that can pick always new domain from this link and use in my iframe

My iframe example is this

witalfialdt.com this domain name will change when new domain update in “https://lernodydenknow.info/current_domain?tid=1151232&enc=0&srv=rt_adblock_backup” this link

so script always use up to date domain name in iframe

MemberExpression evaluation order of null/undefined base

Why does the following code throw TypeError: Cannot set properties of null instead of Error: property key evaluated?

var base = null;
var prop = {
  toString: function() {
    throw new Error("property key evaluated");
  }
};

base[prop] = 2;

This behavior suggests the base is converted to object before the index is converted to string. But the ECMAScript specification…

https://tc39.es/ecma262/#sec-assignment-operators-runtime-semantics-evaluation
https://tc39.es/ecma262/#sec-property-accessors-runtime-semantics-evaluation
https://tc39.es/ecma262/#sec-evaluate-property-access-with-expression-key
https://tc39.es/ecma262/#sec-topropertykey
https://tc39.es/ecma262/#sec-putvalue

…seems to indicate clearly that ? ToPropertyKey(propertyNameValue) (calling ToString(prop), which should throw new Error()) happens before ? ToObject(V.[[Base]]) (calling ToObject(base), throwing a TypeError). What’s going on here?

(If this is nonconformance by major engines, why is Test262 expecting this behavior?)

Contentful multiple content types filtering

I’ve an Intros content type in Contentful, and this is some portion of the JSON representation of the content type

{
  "name": "Intros",
  "description": "Inserted into existing pages",
  "displayField": "title",
  "fields": [
    {
      "id": "title",
      "name": "Title",
      "type": "Symbol",
      "localized": false,
      "required": true,
      "validations": [],
      "disabled": false,
      "omitted": false
    },
    {
      "id": "reviewStatus",
      "name": "Review Status",
      "type": "Symbol",
      "localized": false,
      "required": false,
      "validations": [
        {
          "in": [
            "Internal",
            "Accepted"
          ]
        }
      ],
      "disabled": false,
      "omitted": false
    },
    {
      "id": "referencePages",
      "name": "Reference Pages",
      "type": "Array",
      "localized": false,
      "required": false,
      "validations": [],
      "disabled": false,
      "omitted": false,
      "items": {
        "type": "Link",
        "validations": [
          {
            "linkContentType": [
              "application",
              "activity",
              "assessments",
              "book",
              "client",
              "landing",
              "list",
              "onlineProgram",
              "organization",
              "person",
              "podcast",
              "topics",
              "video"
            ]
          }
        ],
        "linkType": "Entry"
      }
    },
    {
      "id": "excludeClient",
      "name": "Exclude Client",
      "type": "Array",
      "localized": false,
      "required": false,
      "validations": [],
      "disabled": false,
      "omitted": false,
      "items": {
        "type": "Link",
        "validations": [
          {
            "linkContentType": [
              "client"
            ]
          }
        ],
        "linkType": "Entry"
      }
    }
  ]
}

And I’ve a contentful query to get all intros where a reference page is used, like I want to get intros where 7HizEUDWMMWA4EMkm8kcKS application type resource is used and I’ve this query and it does return 1 item where the reference id is used which is correct.

    const ctfPayload = {
      content_type: 'intros',
      select:
        'fields.referencePages,fields.client,fields.excludeClient,fields.commonPages,fields.header,fields.description,fields.url,fields.linkName,fields.style,fields.image,fields.multipleImages,fields.tags,fields.context',
      'fields.reviewStatus': 'Accepted',
      limit: 1000,
      'fields.referencePages.sys.id': '7HizEUDWMMWA4EMkm8kcKS',
    };

    const response = await ctf.getEntries(ctfPayload);

So the first questions is: If I use GraphQl to do same thing, I got 0 items which is confusing as they’re exactly same query.

query {
  introsCollection(
    where: {
      reviewStatus: "Accepted",
      referencePages: {
        sys: {
          id: "7HizEUDWMMWA4EMkm8kcKS"
        }
      }
    }
    limit: 1000
  ) {
    items {
      title
    }
    total
  }
}

The second question is, all reference page content types have a slug properly with same id and type, and according to the documentation I can provide slug to filter intros by referencePages.slug
https://www.contentful.com/developers/docs/references/graphql/#/reference/collection-filters/nested-collection-filters

And as you can see the GraphQL playground it understood and we can apply a slug filter for a reference pages, but it does return 0 items, while the first intro does have a reference page called Calm with slug calm, I confirmed this as if I remove referencePages filter I got 141 intros and the first intro which contains calm reference page is also returned.

enter image description here

If I try same with the contentful JS api I got an error

    const ctfPayload = {
      content_type: 'intros',
      select:
        'fields.referencePages,fields.client,fields.excludeClient,fields.commonPages,fields.header,fields.description,fields.url,fields.linkName,fields.style,fields.image,fields.multipleImages,fields.tags,fields.context',
      'fields.reviewStatus': 'Accepted',
      limit: 1000,
      'fields.referencePages.slug': 'calm',
    };

    const response = await ctf.getEntries(ctfPayload);

This is the error I’m getting

{
    "sys": {
        "type": "Error",
        "id": "InvalidQuery"
    },
    "message": "The query you sent was invalid. Probably a filter or ordering specification is not applicable to the type of a field.",
    "details": {
        "errors": [
            {
                "name": "unknown",
                "path": [
                    "fields",
                    "referencePages",
                    "en-US",
                    "slug"
                ],
                "details": "The path "fields.referencePages.en-US.slug" is not recognized"
            }
        ]
    },
    "requestId": "8c86a826-0025-463c-8bcc-2a1bbb4955cd"
}

So I want to filter intros by referencePages.slug as all the reference page content types have a slug property with same id and type.

I tried to add referencePages.slug in contentful js api, and in GraphQL I tried to add both sys.id and slug filters for referencePages

Inferring the drawing direction of a rotation+reflection matrix applied to a canvas

I have a function that needs to know whether the canvas will draw up and/or left from the origin (pointsLeft and pointsUp are booleans). I am trying to determine, whether the axes are flipped visually from drawing down and right. The DOMMatrix applied to the canvas has reflection done with rotate/scale and then a further rotation applied.

    const radians = getAngleFromOrigin(line);
    const angle = getDegreesFromRadians(radians);
    matrix.rotateSelf(angle);
    matrix.scaleSelf(1, -1);
    matrix.rotateSelf(-angle);
    const longestEdgeAngle = getLongestEdgeAngle(originalPoints);
    const degrees = getDegreesFromRadians(longestEdgeAngle);
    matrix.rotateSelf(degrees);

I feel like I should be able to look at the signs of a and d and know, but they don’t seem to correspond to the actual drawing direction? I got better results by comparing the absolute values of a/b and c/d and then checking whether the value with the greater magnitude was < 0 (implemented in my snippet), but it still failed in one case. I also tried various things involving the net rotation (or taking atan2 of b/a) and trying to see if that changed which one I should check the sign of for each of a/b and c/d, but I couldn’t work it out.

Technically speaking, I don’t need to determine it from the matrix directly, as I have access to the transformations applied to the matrix. It just seemed safest to me. If context on my function is necessary, see my answer to my previous question here.

I tried looking at the values of the matrixes of my different test cases, but I cannot work out why matrixes with the same signs on a/b/c/d orient differently (in my example, the first test’s green piece – the long horizontal one – and the last test’s orange piece – the short one – have identical signs, but different drawing orientations).

Code below with some test cases embedded, the goal is to have the image perfectly overlap the outline when clipped (which it will do if I can get this working):

"use strict";
const canvasWidth = 600;
const canvasHeight = 830;
const pieces = [
  [{
    points: [new DOMPoint(140, 50), new DOMPoint(140.00000000000003, -90), new DOMPoint(90.00000000000001, -90), new DOMPoint(90, 0), ],
    line: {
      start: new DOMPoint(283.6663192636163, 193.66631926361632),
      end: new DOMPoint(-52.666319263616316, -142.66631926361632)
    },
    original: [new DOMPoint(140, 50), new DOMPoint(0, 50), new DOMPoint(0, 0), new DOMPoint(90, 0), ],
    intersects: [new DOMPoint(90, 0), new DOMPoint(140, 50), ],
    origTopLeft: new DOMPoint(0, 0),
    width: 50.00000000000003,
    height: 50.00000000000003
  }, {
    points: [new DOMPoint(158.36517719568567, 44.67326250912334), new DOMPoint(163.97896049896048, -53.783451143451146), new DOMPoint(213.82095634095634, -49.58802494802492), new DOMPoint(211.1386748844376, -2.5451301597599514), ],
    line: {
      start: new DOMPoint(252.24682141160773, -39.3261033682806),
      end: new DOMPoint(101.75317858839227, 95.3261033682806)
    },
    original: [new DOMPoint(158.36517719568567, 44.67326250912335), new DOMPoint(256.8378378378378, 50), new DOMPoint(258.18918918918916, -1.6317320576126618e-15), new DOMPoint(211.1386748844376, -2.5451301597599563), ],
    intersects: [new DOMPoint(211.1386748844376, -2.5451301597599514), new DOMPoint(158.36517719568567, 44.67326250912334), ],
    origTopLeft: new DOMPoint(158.36517719568567, -2.5451301597599563),
    width: 55.45577914527067,
    height: 55.45577914527067
  }, {
    points: [new DOMPoint(198.38255973344914, 8.868236027966603), new DOMPoint(-153.64897521683866, 5.578032470538176), new DOMPoint(-154.11627140114496, 55.57584876561373), new DOMPoint(143.07549812764987, 58.3535016752606), ],
    line: {
      start: new DOMPoint(436.3443301443184, -204.04492697123226),
      end: new DOMPoint(-82.3443301443184, 260.04492697123226)
    },
    original: [new DOMPoint(198.3825597334491, 8.868236027966553), new DOMPoint(162.65825355141538, 359.09787638799855), new DOMPoint(112.9163540869709, 354.0240772523315), new DOMPoint(143.0754981276499, 58.353501675260645), ],
    intersects: [new DOMPoint(143.07549812764987, 58.3535016752606), new DOMPoint(198.38255973344914, 8.868236027966603), ],
    origTopLeft: new DOMPoint(112.9163540869709, 8.868236027966553),
    width: 352.49883113459407,
    height: 352.49883113459407
  }, ],
  [{
    points: [new DOMPoint(183, 0), new DOMPoint(-115.80000000000018, -398.4000000000001), new DOMPoint(-155.80000000000018, -368.4000000000001), new DOMPoint(158, 50), ],
    line: {
      start: new DOMPoint(466.81944546997806, -567.6388909399561),
      end: new DOMPoint(-126.81944546997806, 619.6388909399561)
    },
    original: [new DOMPoint(183.00000000000003, 0), new DOMPoint(681, 0), new DOMPoint(681, 50), new DOMPoint(158, 50), ],
    intersects: [new DOMPoint(158, 50), new DOMPoint(183, 0), ],
    originalTopLeft: new DOMPoint(158, 0),
    width: 338.8000000000002,
    height: 338.8000000000002
  }, ],
  [{
    points: [new DOMPoint(157.50666666666666, 24.98461538461538), new DOMPoint(232.01174895512395, 458.84515237596656), new DOMPoint(182.7330781575854, 467.307575458501), new DOMPoint(121.1733333333333, 108.830769230769), ],
    line: {
      start: new DOMPoint(358.8607804360353, -439.6787240831585),
      end: new DOMPoint(-43.86078043603533, 489.6787240831585)
    },
    original: [new DOMPoint(157.50666666666666, 24.9846153846154), new DOMPoint(-210.00917431192647, 267.30275229357795), new DOMPoint(-182.48623853211006, 309.045871559633), new DOMPoint(121.17333333333352, 108.83076923076914), ],
    intersects: [new DOMPoint(121.1733333333333, 108.830769230769), new DOMPoint(157.50666666666666, 24.98461538461538), ],
    originalTopLeft: new DOMPoint(-210.00917431192647, 24.9846153846154),
    width: 110.83841562179065,
    height: 110.83841562179065
  }, {
    points: [new DOMPoint(118.49999999999997, 49.99999999999999), new DOMPoint(207.78082191780817, 127.91780821917807), new DOMPoint(240.6575342465753, 90.24657534246575), new DOMPoint(137.25, -4.9897642155143516e-15), ],
    line: {
      start: new DOMPoint(199.2848941516392, -165.42638440437122),
      end: new DOMPoint(55.71510584836079, 217.42638440437122)
    },
    original: [new DOMPoint(118.5, 50), new DOMPoint(0, 50), new DOMPoint(0, 0), new DOMPoint(137.25, 0), ],
    intersects: [new DOMPoint(137.25, -4.9897642155143516e-15), new DOMPoint(118.49999999999997, 49.99999999999999), ],
    originalTopLeft: new DOMPoint(0, 0),
    width: 122.15753424657532,
    height: 122.15753424657532
  }]
];

// reflect, rotate by angle of the longest edge of the pre-reflected shape so that the image renders at the right angle on the page

function getReflectionMatrix(piece, ctx) {
  const {
    line,
    original,
    points,
    intersects
  } = piece;
  const anchor = intersects[0]; // point where the line and the other edges meet, used as an origin for reflection

  const display = new DOMMatrix();
  reflectMatrix(display, line, anchor);
  rotateMatrix(display, original, anchor); // i do this so the image shows up at the right angle on the canvas
  translateMatrix(display, points, ctx);
  return display;
}

function reflectMatrix(matrix, line, anchor) {
  const radians = getAngleFromOrigin(line);
  const angle = getDegreesFromRadians(radians);

  matrix.translateSelf(anchor.x, anchor.y);
  matrix.rotateSelf(angle);
  matrix.scaleSelf(1, -1);
  matrix.rotateSelf(-angle);
}

function rotateMatrix(matrix, originalPoints, anchor) {
  const longestEdgeAngle = getLongestEdgeAngle(originalPoints);
  const degrees = getDegreesFromRadians(longestEdgeAngle);

  matrix.rotateSelf(degrees);
  matrix.translateSelf(-anchor.x, -anchor.y);
}

// snap the image to the appropriate point on the axis-aligned bounding box

function translateMatrix(matrix, newPoints, ctx) {
  const pt0T = new DOMPoint(0, 0).matrixTransform(matrix);
  const { pointsUp, pointsLeft } = getMatrixDirection(matrix);
  const corners = getRotatedBoundingBox(newPoints);

  let d = "topLeft";
  if (pointsUp && pointsLeft) d = "bottomRight";
  if (pointsUp && !pointsLeft) d = "bottomLeft";
  if (pointsLeft && !pointsUp) d = "topRight";
  const target = corners[d];

  const dx = target.x - pt0T.x;
  const dy = target.y - pt0T.y;
  const translated = new DOMMatrix().translateSelf(dx, dy);
  matrix.preMultiplySelf(translated);

  drawDebugMarker(target.x, target.y, "purple", ctx); // visualises the origin
}

// extracts the up/down and left/right orientation of the matrix as applied to a canvas

function getMatrixDirection(matrix) {
  const { a, b, c, d } = matrix;
  let pointsLeft = Math.abs(a) >= Math.abs(b) ? a < 0 : b < 0;
  let pointsUp = Math.abs(c) >= Math.abs(d) ? c < 0 : d < 0;
  return { pointsLeft, pointsUp };
}

// rotated bounding box helpers - everything below is pretty irrelevant to the question afaict

function getRotatedBoundingBox(points) {
    const { angle, corners } = getBestBox(points);
    const cos = Math.cos(-angle);
    const sin = Math.sin(-angle);
    const unrotated = corners.map(point => rotatePoint(point, sin, cos));
    return sortCorners(unrotated);
}

function sortCorners(points) {
  const sorted = points.toSorted((a, b) => a.y == b.y ? a.x - b.x : a.y - b.y);
  const [pt1, pt2, pt3, pt4] = sorted;
  const [topLeft, topRight] = pt1.x < pt2.x ? [pt1, pt2] : [pt2, pt1];
  const [bottomLeft, bottomRight] = pt3.x < pt4.x ? [pt3, pt4] : [pt4, pt3];
  return { topLeft, topRight, bottomRight, bottomLeft };
}

function getBestBox(points) {
    let bestArea = Infinity;
    let bestBox;
    for (let i = 0; i < points.length; i++) {
        const a = points[i];
        const b = points[(i + 1) % points.length];
        const angle = -Math.atan2(b.y - a.y, b.x - a.x);
        const cos = Math.cos(angle);
        const sin = Math.sin(angle);

        const rotated = points.map(point => rotatePoint(point, sin, cos));
        const { width, height } = getDimensions(rotated);
        const area = width * height;
        if (area < bestArea) {
            bestArea = area;
            bestBox = makeBoundingBox(rotated, angle);
        }
    }
    return bestBox;
}

function rotatePoint(point, sin, cos) {
    const { x, y } = point;
    return new DOMPoint(rotateX(x, y, sin, cos), rotateY(x, y, sin, cos));
}

function rotateX(x, y, sin, cos) {
    return x * cos - y * sin;
}

function rotateY(x, y, sin, cos) {
    return x * sin + y * cos;
}

function makeBoundingBox(points, angle) {
    const { minX, maxX, minY, maxY } = getBoundingBox(points);
    return {
        corners: [
            new DOMPoint(minX, minY),
            new DOMPoint(maxX, minY),
            new DOMPoint(maxX, maxY),
            new DOMPoint(minX, maxY),
        ],
        angle
    };
}

// helpers for getting shape dimensions etc.

function getAngleFromOrigin(line) {
  const { start, end } = line;
  const dx = end.x - start.x;
  const dy = end.y - start.y;
  return Math.atan2(dy, dx);
}

function getLongestEdgeAngle(points) {
  let maxLength = 0;
  let bestAngle = 0;
  for (let i = 0; i < points.length; i++) {
    const a = points[i];
    const b = points[(i + 1) % points.length];
    const dx = b.x - a.x;
    const dy = b.y - a.y;
    const length = Math.hypot(dx, dy);
    if (length > maxLength) {
      maxLength = length;
      bestAngle = Math.atan2(dy, dx);
    }
  }
  return bestAngle;
}

function getDegreesFromRadians(angle) {
  const degrees = angle * 180 / Math.PI;
  return ((degrees % 360) + 360) % 360;
}

function getTopLeft(points) {
  const { minX, maxX, minY, maxY } = getBoundingBox(points);
  return new DOMPoint(minX, minY);
}

function getBoundingBox(points) {
  const coordsX = points.map(point => point.x);
  const minX = Math.min(...coordsX);
  const maxX = Math.max(...coordsX);
  const coordsY = points.map(point => point.y);
  const minY = Math.min(...coordsY);
  const maxY = Math.max(...coordsY);
  return {
    minX,
    maxX,
    minY,
    maxY
  };
}

function getDimensions(points) {
  const { minX, maxX, minY, maxY } = getBoundingBox(points);
  const width = maxX - minX;
  const height = maxY - minY;
  return {
    width,
    height
  };
}

// drawing

function loopThroughPieces(test, ctx, testNum) {
  for (let i = 0; i < test.length; i++) {
    ctx.setTransform(canvasTransform);
    const piece = test[i];
    const colour = getColour(i);
    const display = getReflectionMatrix(piece, ctx);
    drawPiece(piece, colour, display, ctx);
  }
}

function getColour(i) {
  // red comes first
  const hue = (i * 45) % 360;
  const lightness = 100 - (40 + 10);
  const alpha = 0.5;
  return `hsla(${hue}, 90%, ${lightness}%, ${alpha})`;
}

function drawPiece(piece, colour, display, ctx) {
  ctx.save();
  tracePiecePath(piece.points, ctx);
  ctx.globalAlpha = 0.65;
  //ctx.clip(); // it's supposed to be clipped, but i unclipped for visualisation, since sometimes the image floats outside of the outline

  ctx.setTransform(canvasTransform.multiply(display));
  ctx.drawImage(image, 0, 0, image.width, image.height);

  ctx.strokeStyle = colour;
  ctx.lineWidth = 3;
  ctx.globalAlpha = 1;
  ctx.stroke();

  ctx.restore();
  ctx.save();
}

function tracePiecePath(points, ctx) {
  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 drawDebugMarker(x, y, colour, ctx) {
  ctx.beginPath();
  ctx.arc(x, y, 5, 0, 2 * Math.PI);
  ctx.fillStyle = colour;
  ctx.fill();
}

// everything below is just assembling test cases etc. and rendering them

function makeCanvasTransform() {
  canvasTransform.scaleSelf(0.6, 0.6);
  canvasTransform.translateSelf(canvasWidth / 2, canvasHeight / 2);
}

function drawDebugImage() {
  const imgCtx = image.getContext("2d");
  imgCtx.fillStyle = "white";
  imgCtx.fillRect(0, 0, image.width, image.height);
  imgCtx.font = "20px arial";
  imgCtx.textAlign = "center";
  imgCtx.fillStyle = "black";
  const segmentWidth = image.width / 12;
  let offsetX = 0;
  for (let i = 0; i < Math.ceil(image.width / segmentWidth); i++) {
    imgCtx.strokeRect(offsetX, 0, segmentWidth, image.height);
    imgCtx.fillText(i + 1, offsetX + segmentWidth / 2, image.height / 2);
    offsetX += segmentWidth;
  }
}

function gatherCtxs() {
  const ctxs = [];
  for (let i = 0; i < pieces.length; i++) {
    const canvas = document.createElement("canvas");
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
    canvases.appendChild(canvas);
    if (i % 2 == 1) {
      const br = document.createElement("br");
      canvases.appendChild(br);
    }
    ctxs.push(canvas.getContext("2d"));
  }
  return ctxs;
}

const image = document.getElementById("image");
const canvases = document.getElementById("canvases");
const canvasTransform = new DOMMatrix();

drawDebugImage();
makeCanvasTransform();
const ctxs = gatherCtxs();
for (let i = 0; i < pieces.length; i++) {
  loopThroughPieces(pieces[i], ctxs[i], i);
}
canvas {
    border: 1px solid grey;
    margin: 2px;
}
<p><canvas id="image" width="680" height="50"></canvas></p>
<p id="canvases"></p>