node-notifier
Version:
A Node.js module for sending notifications on native Mac, Windows (post and pre 8) and Linux (or Growl as fallback)
135 lines (134 loc) • 4.26 kB
JavaScript
import callableClass from "../lib/callableClass.js";
import utils from "../lib/utils.js";
import os from "node:os";
import fs from "node:fs";
import path from "node:path";
import { EventEmitter } from "node:events";
// src/notifiers/notifysend.ts
/**
* Node.js wrapper for "notify-send".
*/
const notifier = "notify-send";
let hasNotifier;
var NotifySend = class extends EventEmitter {
options;
#notify;
constructor(options) {
super();
this.options = utils.clone(options || {});
}
get notify() {
this.#notify ??= notifyRaw.bind(this);
return this.#notify;
}
};
const Notifier = callableClass(NotifySend);
function noop() {}
function notifyRaw(notification, userCallback) {
const callback = userCallback || noop;
if (typeof callback !== "function") throw new TypeError(`The second argument must be a function callback. You have passed ${typeof callback}`);
const options = typeof notification === "string" ? {
title: "node-notifier",
message: notification
} : utils.clone(notification || {});
if (!options.message) {
callback(/* @__PURE__ */ new Error("Message is required."));
return this;
}
if (os.type() !== "Linux" && !os.type().match(/BSD$/)) {
callback(/* @__PURE__ */ new Error("Only supported on Linux and *BSD systems"));
return this;
}
if (hasNotifier === false) {
callback(/* @__PURE__ */ new Error("notify-send must be installed on the system."));
return this;
}
if (hasNotifier || this.options.suppressOsdCheck) {
doNotification.call(this, options, callback);
return this;
}
try {
hasNotifier = !!findOnPath(notifier);
doNotification.call(this, options, callback);
} catch (err) {
hasNotifier = false;
callback(err);
}
return this;
}
function findOnPath(cmd) {
const dirs = (process.env.PATH || "").split(path.delimiter);
for (const dir of dirs) {
const file = path.join(/^".*"$/.test(dir) ? dir.slice(1, -1) : dir, cmd);
try {
if (fs.statSync(file).isFile()) {
fs.accessSync(file, fs.constants.X_OK);
return file;
}
} catch {}
}
throw Object.assign(/* @__PURE__ */ new Error(`not found: ${cmd}`), { code: "ENOENT" });
}
const allowedArguments = [
"urgency",
"expire-time",
"icon",
"category",
"hint",
"app-name"
];
function doNotification(options, callback) {
const originalOptions = { ...options };
const actions = toActionList(options.actions);
const transient = options.transient === true;
delete options.actions;
delete options.transient;
options = utils.mapToNotifySend(options);
options.title = options.title || "Node Notification:";
const initial = [options.title, options.message];
delete options.title;
delete options.message;
const argsList = [
...utils.constructArgumentList(options, {
keyExtra: "-",
allowedArguments,
noEscape: true,
wrapper: ""
}),
...actions.map((action, i) => `--action=${i}=${action}`),
...transient ? ["--hint=int:transient:1"] : [],
"--",
...utils.constructArgumentList({}, {
initial,
noEscape: true,
wrapper: ""
})
];
if (!actions.length) {
utils.commandWithoutShell(notifier, argsList, callback);
return;
}
const expireTime = options["expire-time"];
const start = Date.now();
const actionJackedCallback = utils.actionJackerDecorator(this, originalOptions, callback, (data) => {
if (data === "activate") return "click";
if (data === "timeout") return "timeout";
return false;
});
utils.commandWithoutShell(notifier, argsList, (err, stdout) => {
if (err instanceof Error) return actionJackedCallback(err, stdout);
const output = (stdout || "").trim();
if (/^\d+$/.test(output) && Number(output) < actions.length) return actionJackedCallback(err || null, {
activationType: "actionClicked",
activationValue: actions[Number(output)]
});
if (String(err || "").includes("Wait timeout expired") || expireTime > 0 && Date.now() - start >= expireTime) return actionJackedCallback(null, { activationType: "timeout" });
actionJackedCallback(err || null, { activationType: "closed" });
});
}
function toActionList(actions) {
if (actions === void 0 || actions === null) return [];
return (Array.isArray(actions) ? actions : [actions]).map(String);
}
export { Notifier as default, Notifier as "module.exports" };
//# sourceMappingURL=notifysend.js.map