Bug in function node? pdf-lib

I have a function node with pdf-lib to extract the first page of a PDF file. The same code works great in VS Code / cmd prompt with Node.js v22.12 but produces this strange error in the Node-Red function node.

"TypeError: `indices` must be of type `Array`, but was actually of type `Array`"

I cannot see what I am doing wrong, and the code works fine Node.js so I can only conclude there is something wrong with the function node. Line 9 produces the error.

I hope someone can find a minute to show me what I am doing wrong. Cheers.

[{"id":"458e16e66760310f","type":"inject","z":"c996c166521b8b9e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":300,"y":700,"wires":[["c66ff2ee12d45ab7"]]},{"id":"c66ff2ee12d45ab7","type":"file in","z":"c996c166521b8b9e","name":"","filename":"C:\\node-red\\A.pdf","filenameType":"str","format":"","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":490,"y":700,"wires":[["962912b07d8f265f"]]},{"id":"962912b07d8f265f","type":"function","z":"c996c166521b8b9e","name":"PDF extract page","func":"//import * as fs from 'fs';\n//import * as pdfLib from 'pdf-lib';\n\nasync function exportPdfPage(pdfBuffer) {\n  const pdfDoc = await pdfLib.PDFDocument.load(pdfBuffer);\n  node.warn(`Found ${pdfDoc.getPageCount() } pages in PDF file`);\n  //console.log(`Found ${pdfDoc.getPageCount() } pages in PDF file`);\n  const newPdfDoc = await pdfLib.PDFDocument.create();\n  const [copiedPage] = await newPdfDoc.copyPages(pdfDoc, [1]);\n  newPdfDoc.addPage(copiedPage);\n  const pdfBytes = await newPdfDoc.save();\n  return pdfBytes;\n}\n\nconst myPdfExportedPage = await exportPdfPage(msg.payload);\n//const myPdfExportedPage = await exportPdfPage(fs.readFileSync(\".\\\\A.pdf\"))\n//console.log(`Extracted PDF file is ${myPdfExportedPage.length} bytes`)\nmsg.payload = myPdfExportedPage;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"pdfLib","module":"pdf-lib"},{"var":"fs","module":"fs"}],"x":770,"y":540,"wires":[["849d6c06cf308c95"]]},{"id":"849d6c06cf308c95","type":"file","z":"c996c166521b8b9e","name":"","filename":"C:\\node-red\\pdfExport.pdf","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":1110,"y":700,"wires":[["d8fe6ee56689069d"]]},{"id":"d8fe6ee56689069d","type":"debug","z":"c996c166521b8b9e","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1340,"y":700,"wires":[]}]

Here is the same code as mjs for node.js

import * as fs from 'fs';
import * as pdfLib from 'pdf-lib';

async function exportPdfPage(pdfBuffer) {
  const pdfDoc = await pdfLib.PDFDocument.load(pdfBuffer);
  //node.warn(`Found ${pdfDoc.getPageCount()} pages in PDF file`);
  console.log(`Found ${pdfDoc.getPageCount()} pages in PDF file`);
  const newPdfDoc = await pdfLib.PDFDocument.create();
  const [copiedPage] = await newPdfDoc.copyPages(pdfDoc, [1]);
  newPdfDoc.addPage(copiedPage);
  const pdfBytes = await newPdfDoc.save();
  return pdfBytes;
}

//const myPdfExportedPage = await exportPdfPage(msg.payload);
const myPdfExportedPage = await exportPdfPage(fs.readFileSync(".\\A.pdf"))
console.log(`Extracted PDF file is ${myPdfExportedPage.length} bytes`)
//msg.payload = myPdfExportedPage;
//return msg;

Try

in node-red settings.js

functionGlobalContext: {
  // os:require('os'),
  pdfLib: require('pdf-lib'),
},

restart node-red instance

in flow node const pdfLib = global.get('pdfLib'); and your other code

I am not sure what difference using the old style loading of npm modules will have as I am already using the functionExternalModules option and the module is loading fine and executing.

As you see this line executes fine so loading the module is not the problem.

node.warn(`Found ${pdfDoc.getPageCount() } pages in PDF file`);

Sorry, it looks like an array validation error in node-red

It is not directly a node-red problem, it is something to do with pdf-lib. Another has seen the problem, but the solution was not posted. How to use pdf-lib with nw.js if I want to build a desktop application? · Issue #787 · Hopding/pdf-lib · GitHub

You might be better asking for help there.

I think the issue is related to the sandbox that the Function node uses, and the ‘unconventional’ way the pdf-lib is checking if something is an array.

The Function node sandbox gets its own Array class. If an Array object is created outside of the Function node and passed in, then code that is checking the type of the object might fail if it is comparing the class object itself - rather than using other utilities like Array.isArray.

Don’t have an immediate fix/workaround for you. We have solved this for other object types by passing them into the sandbox so the class objectss are the same. It somewhat undermines the purpose of the sandbox, but there you go.

You can always build a package. I don't know if this is a good idea.
I've prepared a simple demo - creates new file with page 7. Page can defined as pdfPage, link to GitHub on npm.

npm i @aaqu/node-red-aaqu-pdf

[
    {
        "id": "089d17553fb029f3",
        "type": "pdf-get-page",
        "z": "19cb94d5b096ef08",
        "name": "",
        "pdfPage": "7",
        "x": 730,
        "y": 760,
        "wires": [
            [
                "6e5b98ce359af5b2"
            ]
        ]
    },
    {
        "id": "c53e1fd40bbfd421",
        "type": "inject",
        "z": "19cb94d5b096ef08",
        "name": "",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 190,
        "y": 760,
        "wires": [
            [
                "db7788eb72014467"
            ]
        ]
    },
    {
        "id": "db7788eb72014467",
        "type": "file in",
        "z": "19cb94d5b096ef08",
        "name": "",
        "filename": "/Users/xyz/Downloads/test.pdf",
        "filenameType": "str",
        "format": "",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 430,
        "y": 760,
        "wires": [
            [
                "089d17553fb029f3"
            ]
        ]
    },
    {
        "id": "6acc37ea7a6237e3",
        "type": "debug",
        "z": "19cb94d5b096ef08",
        "name": "debug 3",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 1280,
        "y": 760,
        "wires": []
    },
    {
        "id": "6e5b98ce359af5b2",
        "type": "file",
        "z": "19cb94d5b096ef08",
        "name": "",
        "filename": "/Users/xyz/Downloads/test-export.pdf",
        "filenameType": "str",
        "appendNewline": true,
        "createDir": false,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 990,
        "y": 760,
        "wires": [
            [
                "6acc37ea7a6237e3"
            ]
        ]
    },
    {
        "id": "d4a842447f088e68",
        "type": "global-config",
        "env": [],
        "modules": {
            "@aaqu/node-red-aaqu-pdf": "0.1.0"
        }
    }
]