Debugging "Missing" nodes

I've been rewriting a large set of custom JS nodes into typescript to improve maintainability with some dependencies. In doing so I'm faced with a situation where the nodes load up OK on a blank flow and can be dropped and configured as expected; however on deploy they are reported as "missing" and the flow is stopped.

there is nothing useful in the log beyond the below.

on redeploying I can see in the js console that some debugging from the html file is still active and outputs expected data (the node object). No other useful debug data is being passed to the js console.

It looks like the js node constructor function is never being called. which is odd as I can see that the file itself is loaded. I've included a minimal snip of the server.js that is being transpiled from typescript and confirm that the package.json file points to the right place.

can anyone help with debugging steps please?

21 Sep 12:34:58 - [info] Waiting for missing types to be registered:

21 Sep 12:34:58 - [info] - hub

21 Sep 12:34:58 - [info] - sensors

21 Sep 12:34:58 - [trace] runtime event: {"id":"runtime-state","payload":{"state":"stop","error":"missing-types","type":"warning","text":"notification.warnings.missing-types","types":["hub","sensors"]},"retain":true}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nodeInit = exports.matterHub = void 0;

console.log("loading server file");  //this is in the console

const nodeInit = (RED) => {
    function MatterServer(config) {
        console.log("inside the MatterServer function");  //NOT in the console
        RED.nodes.createNode(this, config); 
    }
    /*start the engine*/
    console.log("registering matterserver function");   // NOT in the console
    RED.nodes.registerType("hub", MatterServer);
};
exports.nodeInit = nodeInit;

One of my larger Node RED modules is being refactored to typescript, and I currently have no issue.

Here is a link to one of the simpler nodes in it's collection.

You haven't provided the ts source, so I'm pasting mine here as reference, just in case your missing something.

You will see reference to an earlier suggestion I provided on node call back 'Registration' as well :nerd_face:

const callback = (Data: UserPayloadPackage) => {
			switch (Data.Type) {
				case MessageType.STATUS:
					self.status(Data.Status);
					if (Data.Status.clearTime) {
						setTimeout(() => {
							self.status({});
						}, Data.Status.clearTime);
					}
					break;

				case MessageType.EVENT:
					self.send({ payload: Data.Event });
					break;
			}
		};

self.runtime.registerDeviceNode(self.id, undefined, callback);

What I think might be happening.

you're exporting your init function attached to a property named nodeInit
but the whole module needs exporting. Node-RED won't know to execute the function named nodeInit on the exported object

exports.nodeInit = nodeInit;

maybe try

exports = nodeInit;

thank you Marcus.

for fullness (for future readers) I have pasted the whole ts file below. it contains much that is unlikely to be germane to this issue.

I have given your initialiser format a try (an anonymous function) and it no longer bleats. Thank you. I am unsure why though! (that an anonymous function would work but a named function would not)!


require("@project-chip/matter-node.js");
import { VendorId } from "@project-chip/matter.js/datatype";
import { ServerNode } from "@project-chip/matter.js/node";
import { QrCode } from "@project-chip/matter.js/schema";
import { logEndpoint } from "@project-chip/matter.js/device";
import { EndpointServer } from "@project-chip/matter.js/endpoint";
import { AggregatorEndpoint } from "@project-chip/matter.js/endpoints/AggregatorEndpoint";
import { Endpoint } from "@project-chip/matter.js/endpoint";
import type { Node, NodeAPI, NodeDef, NodeInitializer } from 'node-red';


interface ServerConfig extends NodeDef {
    passcode: number | string;
    discriminator: number | string;
}


let matterHub: Endpoint<AggregatorEndpoint> = new Endpoint(AggregatorEndpoint, { id: "matterHub" });
export { matterHub };
export { nodeInit };

console.log("loading server file");
let nodeInit: NodeInitializer = (RED: NodeAPI): void => {
    console.log("outside matterserver function");
    function MatterServer(this: Node, config: ServerConfig) {
        console.log("inside the MatterServer function");
        RED.nodes.createNode(this, config);
        let matterServer: any;
        let commissioned: boolean;
        let qrcode: string;
        let manualPairingCode: string;
        let qrCodeURL: string;

        console.log("about to load httpadmin endpoint");

        RED.httpAdmin.post("/matter-server/:id",
            function (request, response) {
                if (!request.hasOwnProperty("params")) {
                    console.log("no params provided");
                    response.json(JSON.stringify({ message: "no params provided" }));
                    return null;
                }
                if (!request.params.hasOwnProperty("id")) {
                    response.json(JSON.stringify({ message: "no id provided" }));
                    return null;
                }
                let node = RED.nodes.getNode(request.params.id);
                console.log("node", node);
                if (!node) {
                    response.json(JSON.stringify({
                        message: "node is null"
                    }));
                } else {
                    response.json(
                        JSON.stringify(
                            {
                                qrcode: qrcode || null,
                                qrcodeURL: qrCodeURL || null,
                                manualPairingCode: manualPairingCode || null,
                                commissioned: commissioned || false,
                                message: "OK"
                            }
                        )
                    );
                }
            }
        );
        console.log("loaded httpadmin endpoint");
        // console.log("server data", config);

        if (typeof this.id != "undefined") {
            console.log("starting matter server");
            const name = "Node-Red Matter.js Hub";
            let serverOpts = {
                id: this.id,
                network: {
                    port: 5540,
                },
                commissioning: {
                    passcode: Number(config.passcode),
                    discriminator: Number(config.discriminator),
                },
                productDescription: {
                    name: name,
                    deviceType: AggregatorEndpoint.deviceType,
                },
                basicInformation: {
                    vendorName: "jpadie",
                    vendorId: VendorId(65521),
                    nodeLabel: name,
                    productName: name,
                    productLabel: name,
                    productId: 1,
                    serialNumber: `NR_Mtr.js-${this.id}`.substring(0, 30),
                    uniqueId: this.id,
                }
            };

            console.log("server options", serverOpts);
            ServerNode
                .create(serverOpts)
                .then((resolve) => {
                    matterServer = resolve;
                    matterServer.add(matterHub)
                        .then(() => {
                            matterServer.bringOnline().then(() => {
                                //do something
                            }
                            ).catch((error) => {
                                console.log("problem bringing matter server online", error);
                            })
                        });
                    //console.log("server node", this.matterServer);

                    if (!matterServer.lifecycle.isCommissioned) {
                        const qrPairingCode = matterServer.state.commissioning.pairingCodes.qrPairingCode;
                        manualPairingCode = matterServer.state.commissioning.pairingCodes.manualPairingCode;
                        qrcode = QrCode.get(qrPairingCode);
                        qrCodeURL = `https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`;
                        commissioned = false;
                    } else {
                        commissioned = true;
                    }

                    setTimeout(() => {
                        logEndpoint(EndpointServer.forEndpoint(matterServer));
                    }, 5000);

                }).catch((error) => {
                    console.error("Issue with matter server deployment", error);
                });
        } else {
            console.log("id is null");
        }
    }

    /*start the engine*/
    console.log("registering matterserver function");
    RED.nodes.registerType("hub", MatterServer);
}
1 Like

In Node RED, I think the exported object needs to be a function its self,
not an object with a property (in your case nodeInit)

This is relative to the default export - it's not named

1 Like

thank you both.

using Marcus's formulation of module.exports solved the error.
equally using the nodeInit route and changing the export at the end of the file to

export default nodeInit
export {matterHub}

also removes the error.

as an aside: I'm not yet convinced that the effort of refactoring to typescript and the learning curve will pay long term dividends! my hair is getting thinner by the day...

1 Like

This has always been my thought about Typescript. I can get nearly all the benefits by using VSCode and good JSDoc descriptors. And then I don't have to learn another language AND write twice as many files! AND do a build step on every change. :slight_smile:

1 Like

I won't get too spammy (I have been silently told off lately :innocent: )

But I think typescript does have a place when the collection of nodes are massively intertwined with each other, and complex relationships exists between them all, where defined objects are expected to contain this or that.

In pure JS - you face error only at runtime (despite IntelliSense - we are only human right?)... in typescript, these errors are highlighted before build time.

A comment/IntelliSense won't stop a contributor causing an error - typescript address's that, at evaluation level during design time.

But I agree - typescript is not always suitable/or needed, I think it depends how complex the code base is, and how strict types need to be in the project

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.