{"id":15537320,"url":"https://github.com/nullvoxpopuli/salvatore","last_synced_at":"2025-04-23T13:42:13.159Z","repository":{"id":246289897,"uuid":"820544654","full_name":"NullVoxPopuli/salvatore","owner":"NullVoxPopuli","description":"A modern alternative to daemon-pid, with bugfixes, ESM, etc","archived":false,"fork":false,"pushed_at":"2024-07-25T18:29:48.000Z","size":295,"stargazers_count":11,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-20T19:41:41.729Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NullVoxPopuli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-26T17:24:04.000Z","updated_at":"2025-01-04T10:30:23.000Z","dependencies_parsed_at":"2024-11-08T21:01:23.049Z","dependency_job_id":"267a2bc8-e460-4848-9f7c-b116924b93e0","html_url":"https://github.com/NullVoxPopuli/salvatore","commit_stats":null,"previous_names":["nullvoxpopuli/daemon-pid-esm","nullvoxpopuli/salvatore"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fsalvatore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fsalvatore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fsalvatore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NullVoxPopuli%2Fsalvatore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NullVoxPopuli","download_url":"https://codeload.github.com/NullVoxPopuli/salvatore/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250440892,"owners_count":21431078,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T11:56:05.701Z","updated_at":"2025-04-23T13:42:13.144Z","avatar_url":"https://github.com/NullVoxPopuli.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Damon Salvatore, from _The Vampire Diaries_ looking like \"wat\"](./assets/damon-wat.jpg)\n\n# Salvatore, Daemon\n\n_0 dependency daemon management for Node_.\n\nand\n\n_A replacement for [`pm2`](https://github.com/Unitech/pm2/issues/5143) that isn't copyleft_.\n\n-------------\n\nDaemonPID is a Node utility module which provides straight-forward and robust PID file management; perfect for writing and reading PID files for daemonized services. It provides the ability to check or monitor the status of previously launched child processes, store additional data along with the process id, and provides process start-time verification to ensure the recorded process-id was not recycled by the OS.\n\n- [Basic Usage](#basic-usage)\n- [Advance Usage](#advanced-usage)\n- [API](#api)\n\n**Warning: this module is for POSIX systems and will not function on Windows. If you're a Windows veteran and would like to make it work, please feel free to contribute!**\n\n\n## Features\n\n✨ Maintained  \n🦹🏽 ESM  \n☁️ High-level API  \n⏱️ No callbacks - uses Promises  \n😎 TypeScript-friendly  \n🙂‍↔️ Compatible with MacOS and Linux\n\nDaemon features:\n- can \"start\" multiple times and retain the same process\n- process keeps running after main program exits\n- configurable logging to file\n\n\n## What is a PID file?\n\nA PID file is a text file in a well-defined location in the \nfile-system which contains at-least the process ID of a running application. \nWriting PID files is a convention used in many software systems as a simple \nway to check the status of services or daemonized processes without requiring \nseparate supervisory or monitoring processes.\n\n\n## PID File Pitfalls \n\nProcess IDs are not required to be unique and can potentially be recycled. To solve this issue, `salvatore` also records and checks the `command` and start time of the process for future comparison and status checks.\n\n## What is a Daemon? \n\nA daemon is a program that runs in the background, rather than under direct control of an interactive user. It typically performs routine tasks or provides services such as managing printers, handling emails, or serving web pages. Daemons are common in Unix-like operating systems and are crucial for system functionality by handling system processes independently of user input. They often start when the system boots and run continuously until the system shuts down. \n\n[more info on wikipedia](https://en.wikipedia.org/wiki/Daemon_(computing)).\n\n### `import { PidFile } from 'salvatore'`\n\nThe `PidFile` provides a number of methods and getters for working with PID files and helping you build tools that could use process-based tooling.\n\n\n_**For an example, with logging, check out the `examples/pidfile` folder.**_\n\n\n_For the child process_:\n\n```js\n// child.js\nimport { PidFile } from 'salvatore';\n\nconst pidFile = new PidFile('.some.file.pid');\n\n// writes-out the pid file\n// this will include \n// - pid\n// - startedAt (Date)\n// - command (initiator and script)\n// - any custom data you wish to pass to your parent process\npidFile.write(/* custom JSON-serializable data here */);\n\n// cleanup isn't strictly necesary, but it's a helpful debugging tool\n// as you could assume that no pid file could mean that no process is running.\nprocess.on('exit', function() {\n    pidFile.delete();\n});\n```\n\n_For the parent process_: \n\n```js\nimport { PidFile } from 'salvatore';\nimport { spawn } from 'node:child_process';\n\n// file path should match the above\nconst pidFile = new PidFile('.some.file.pid');\n\nconst process = spawn(process.argv0, []'child.js'], {\n    detached: true,\n    stdio: 'ignore',\n});\n\n// some time later:\n//   ask questions of the pidFile\npidFile.data  // JSON.parse result of what was passed to pidFile.write in child\npidFile.uptime // number\npidFile.startedAt // Date\npidFile.isRunning // boolean\npidFile.pid // number - the PID\npidFile.fileContents // object - raw pidFile contents\npidFile.exists // boolean - has the pid file been written?\npidFile.command // string - the initiator and script that started the child process\n\n//   perform actions on the child process via the pidFile\npidFile.kill(signal); // -- stops the process, must pass a number / SIGINT / SIGTERM type string\n```\n\n### `import { Daemon } from 'salvatore'`\n\nIn the file-to-be-daemonized:\n```js\n// waiter.js\nimport { PidFile } from 'salvatore';\n\nconst pidFile = new PidFile('.example.pid');\n\npidFile.write();\nprocess.on('exit', () =\u003e pidFile.delete());\n\nasync function run() {\n  await new Promise((resolve) =\u003e {\n    // force the process to stay running for 20s\n    setTimeout(() =\u003e resolve(null), 20_000);\n  });\n}\n\nawait run();\n```\n\na small CLI for managing the daemon:\n```js \n#!/usr/bin/env node\n\nimport { Daemon } from 'salvatore';\n\nconst daemon = new Daemon('./waiter.js', { pidFilePath: '.example.pid' });\nconst [,, ...args] = process.argv;\nconst [command] = args;\n\nasync function main() {\n  switch (command) {\n    case 'start': return daemon.ensureStarted();\n    case 'stop': return daemon.stop();\n    case 'status': return console.log(JSON.stringify(daemon.info));\n    default: throw new Error(`Command not recognized`);\n  }\n}\n\nawait main();\n```\n    \n### Storing Data\n\nAdditional information can be stored in the PID file for use later. Any data convertible to JSON can be stored. This could be useful for starting a server and then reading out what PORT that server had started on:\n\nIn the daemonized file:\n```js\n// server.js\nimport { PidFile } from 'salvatore';\nimport express from 'express';\n\nconst pidfile = new PidFile('.express.pid');\n\nconst app = express();\n\nconst listener = app.listen(8888, function () {\n    let { port, host } = listener.address();\n\n    pidFile.write({\n        port,\n        host,\n    })\n});\n```\n\nand then in the bootstrap / cli / start script\n```js \n#!/usr/bin/env node\nimport { Daemon } from 'salvatore';\n\nconst daemon = new Daemon('./server.js', { pidFilePath: '.express.pid' });\nconst [,, ...args] = process.argv;\nconst [command] = args;\n\nasync function main() {\n  switch (command) {\n    case 'start':\n      return daemon.ensureStarted();\n    // ...\n    case 'address': {\n      const { host, port } = daemon.info.data\n\n      console.log({ host, port })\n    }\n  }\n}\n\nawait main();\n```\n\n## Examples\n\nSee the `examples` directory for examples.\n\nEach example has a CLI with these commands:\n```bash\n./cli.js start # starts the example daemon\n./cli.js stop # stops the example daemon\n./cli.js status # prints status about the daemon\n```\n\nExample:\n```bash \nsalvatore/examples/daemon/\n❯ ./cli.js status\n\n    Running: true\n    PID:     49443\n    Started: Fri Jun 28 2024 12:35:32 GMT-0400 (Eastern Daylight Time)\n    Uptime:  4783086 \n    Data:    \"custom-data-from-the-daemon\"\n  \n\n```\n\n\n### Contributing\n\n1. fork it\n2. change it\n3. pr it\n4. 🎉 collab time 🎉\n\n\n#### Notes\n\nEach test that operates on the examples folder runs in its own tmp directory. This is because we need a unique pid file for each test, and in order to run the tests in parallel, this is the only way to not run in to issues with one test reading another's pid file.\n\n\n### Why \"Salvatore\"\n\nI've pronounced \"Daemon\" as \"Damon\" my whole life, and I can't stop.\nI know that literature out there says that \"Daemon\" is supposed to be prounced like \"Demon\", but 1. I don't like that, and 2. I appreciate the disambiguation. Though, after having working through this project now, debugging mac vs linux issues, I understand why folks would call these things demons. oofta.\n\nDamon Salvatore is the name of a character in _[The Vampire Diaries](https://www.imdb.com/title/tt1405406/)_, and since I prounce \"Daemon\" as \"Damon\", it was a natural fit.\n\n\n\n\n\n-----------------\n\nOriginal project: [daemon-pid](https://github.com/JoshuaToenyes/daemon-pid) - [published code](https://www.npmjs.com/package/daemon-pid?activeTab=code)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullvoxpopuli%2Fsalvatore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnullvoxpopuli%2Fsalvatore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnullvoxpopuli%2Fsalvatore/lists"}