function add(hookId, callback) {
    let [id, label] = hookId.split(".");
    if (VALID_HOOKS.indexOf(id) === -1) {
        throw new Error(`Invalid hook '${id}'`);
    }
    if (label && labelledHooks[label] && labelledHooks[label][id]) {
        throw new Error("Hook "+hookId+" already registered")
    }
    // Get location of calling code
    let callModule;
    const stack = new Error().stack;
    const stackEntries = stack.split("\n").slice(1);//drop 1st line (error message)
    const stackEntry2 = stackEntries[1];//get 2nd stack entry
    if (stackEntry2) {
        try {
            if (stackEntry2.indexOf(" (") >= 0) {
                callModule = stackEntry2.split("(")[1].slice(0, -1);
            } else {
                callModule = stackEntry2.split(" ").slice(-1)[0];
            }
        } catch (error) {
            Log.debug(`Unable to determined module when adding hook '${hookId}'. Stack:\n${stackEntries.join("\n")}`);
            callModule = "unknown:0:0";
        }
    } else {
        Log.debug(`Unable to determined module when adding hook '${hookId}'. Stack:\n${stackEntries.join("\n")}`);
        callModule = "unknown:0:0";
    }
    Log.debug(`Adding hook '${hookId}' from ${callModule}`);

    const hookItem = {cb:callback, location: callModule, previousHook: null, nextHook: null }

    let tailItem = hooks[id];
    if (tailItem === undefined) {
        hooks[id] = hookItem;
    } else {
        while(tailItem.nextHook !== null) {
            tailItem = tailItem.nextHook
        }
        tailItem.nextHook = hookItem;
        hookItem.previousHook = tailItem;
    }

    if (label) {
        labelledHooks[label] = labelledHooks[label]||{};
        labelledHooks[label][id] = hookItem;
    }

    // TODO: get rid of this;
    states[id] = true;
}
Register a handler to a named hook
@memberof @node-red/util_hooks
@param {String} hookId - the name of the hook to attach to
@param {Function} callback - the callback function for the hook