@webpod/ps 0.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Webpod
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # @webpod/ps
2
+
3
+ > A Node.js module for looking up running processes. Originated from [neekey/ps](https://github.com/neekey/ps), [UmbraEngineering/ps](https://github.com/UmbraEngineering/ps) and completely reforged.
4
+
5
+ ## Differences
6
+ * [x] Rewritten in TypeScript
7
+ * [x] CJS and ESM package entry points
8
+ * [x] `table-parser` replaced with `@webpod/ingrid` to handle some issues: [neekey/ps#76](https://github.com/neekey/ps/issues/76), [neekey/ps#62](https://github.com/neekey/ps/issues/62), [neekey/table-parser#11](https://github.com/neekey/table-parser/issues/11), [neekey/table-parser#18](https://github.com/neekey/table-parser/issues/18)
9
+ * [x] Provides promisified responses
10
+ * [ ] Brings sync API
11
+ * [x] Builds a process tree
12
+
13
+ ## Install
14
+ ```bash
15
+ $ npm install @webpod/ps
16
+ ```
17
+
18
+ ## Internals
19
+ This module invokes different tools to get process list:
20
+
21
+ * `ps` for unix/mac: `ps -lx`
22
+ * [`wmic` for win runtimes](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmic): `wmic process get ProcessId,CommandLine`.
23
+
24
+ ## Usage
25
+
26
+ ### lookup()
27
+ Searches for the process by the specified `pid`.
28
+ ```ts
29
+ import {lookup} from '@webpod/ps'
30
+
31
+ // Both callback and promise styles are supported
32
+ const list = await lookup({pid: 12345})
33
+
34
+ // or
35
+ lookup({pid: 12345}, (err, list) => {
36
+ if (err) {
37
+ throw new Error(err)
38
+ }
39
+
40
+ const [found] = list
41
+ if (found) {
42
+ console.log('PID: %s, COMMAND: %s, ARGUMENTS: %s', found.pid, found.command, found.arguments)
43
+ } else {
44
+ console.log('No such process found!')
45
+ }
46
+ })
47
+ ```
48
+
49
+ Define a query opts to filter the results by `command` and/or `arguments` predicates:
50
+ ```ts
51
+ const list = await lookup({
52
+ command: 'node', // it will be used to build a regex
53
+ arguments: '--debug',
54
+ })
55
+
56
+ list.forEach(entry => {
57
+ console.log('PID: %s, COMMAND: %s, ARGUMENTS: %s', entry.pid, entry.command, entry.arguments);
58
+ })
59
+ ```
60
+
61
+ Unix users can override the default `ps` arguments:
62
+ ```ts
63
+ lookup({
64
+ command: 'node',
65
+ psargs: 'ux'
66
+ }, (err, resultList) => {
67
+ // ...
68
+ })
69
+ ```
70
+
71
+ Specify the `ppid` option to filter the results by the parent process id (make sure that your custom `psargs` provides this output: `-l` or `-j` for instance)
72
+ ```ts
73
+ lookup({
74
+ command: 'mongod',
75
+ psargs: '-l',
76
+ ppid: 82292
77
+ }, (err, resultList) => {
78
+ // ...
79
+ })
80
+ ```
81
+
82
+ ### tree()
83
+ Returns a child processes list by the specified parent `pid`. Some kind of shortcut for `lookup({ppid: pid})`.
84
+ ```ts
85
+ import { tree } from '@webpod/ps'
86
+
87
+ const children = await tree(123)
88
+ /**
89
+ [
90
+ {pid: 124, ppid: 123},
91
+ {pid: 125, ppid: 123}
92
+ ]
93
+ */
94
+ ```
95
+
96
+ To obtain all nested children, set `recursive` option to `true`:
97
+ ```ts
98
+ const children = await tree({pid: 123, recursive: true})
99
+ /**
100
+ [
101
+ {pid: 124, ppid: 123},
102
+ {pid: 125, ppid: 123},
103
+
104
+ {pid: 126, ppid: 124},
105
+ {pid: 127, ppid: 124},
106
+ {pid: 128, ppid: 124},
107
+
108
+ {pid: 129, ppid: 125},
109
+ {pid: 130, ppid: 125},
110
+ ]
111
+ */
112
+ ```
113
+
114
+ ### kill()
115
+ Eliminates the process by its `pid`.
116
+
117
+ ```ts
118
+ import { kill } from '@webpod/ps'
119
+
120
+ kill('12345', (err, pid) => {
121
+ if (err) {
122
+ throw new Error(err)
123
+ } else {
124
+ console.log('Process %s has been killed!', pid)
125
+ }
126
+ })
127
+ ```
128
+
129
+ Method `kill` also supports a `signal` option to be passed. It's only a wrapper of `process.kill()` with checking of that killing is finished after the method is called.
130
+
131
+ ```ts
132
+ import { kill } from '@webpod/ps'
133
+
134
+ // Pass signal SIGKILL for killing the process without allowing it to clean up
135
+ kill('12345', 'SIGKILL', (err, pid) => {
136
+ if (err) {
137
+ throw new Error(err)
138
+ } else {
139
+ console.log('Process %s has been killed without a clean-up!', pid)
140
+ }
141
+ })
142
+ ```
143
+
144
+ You can also use object notation to specify more opts:
145
+ ```ts
146
+ kill( '12345', {
147
+ signal: 'SIGKILL',
148
+ timeout: 10, // will set up a ten seconds timeout if the killing is not successful
149
+ }, () => {})
150
+ ```
151
+
152
+ Notice that the nodejs build-in `process.kill()` does not accept number as a signal, you will have to use string format.
153
+
154
+ ## License
155
+ [MIT](./LICENSE)
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@webpod/ps",
3
+ "version": "0.0.0-beta.0",
4
+ "description": "A process lookup utility",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "type": "module",
9
+ "main": "target/cjs/index.cjs",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./target/dts/index.d.ts",
13
+ "require": "./target/cjs/index.cjs",
14
+ "import": "./target/esm/index.mjs",
15
+ "default": "./target/esm/index.mjs"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "concurrently 'npm:build:*'",
20
+ "build:js": "node ./src/scripts/build.mjs",
21
+ "build:dts": "tsc --emitDeclarationOnly --outDir target/dts",
22
+ "build:docs": "typedoc --options src/main/typedoc",
23
+ "build:stamp": "npx buildstamp",
24
+ "test": "concurrently 'npm:test:*'",
25
+ "test:lint": "eslint -c src/test/lint/.eslintrc.json src",
26
+ "test:unit": "c8 -r lcov -r text -o target/coverage -x src/scripts -x src/test -x target node --loader ts-node/esm --experimental-specifier-resolution=node src/scripts/test.mjs",
27
+ "test:legacy": "node ./node_modules/mocha/bin/mocha -t 0 -R spec src/test/legacy/test.cjs"
28
+ },
29
+ "files": [
30
+ "target/cjs",
31
+ "target/esm",
32
+ "target/dts"
33
+ ],
34
+ "keywords": [
35
+ "ps",
36
+ "process",
37
+ "lookup",
38
+ "pid"
39
+ ],
40
+ "dependencies": {
41
+ "@webpod/ingrid": "^0.0.0-beta.1",
42
+ "zurk": "^0.0.32"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20.11.30",
46
+ "c8": "^9.1.0",
47
+ "concurrently": "^8.2.2",
48
+ "esbuild": "^0.20.2",
49
+ "esbuild-node-externals": "^1.13.0",
50
+ "esbuild-plugin-entry-chunks": "^0.1.11",
51
+ "eslint": "^8.57.0",
52
+ "eslint-config-qiwi": "^2.1.3",
53
+ "fast-glob": "^3.3.2",
54
+ "minimist": "^1.2.8",
55
+ "mocha": "^10.3.0",
56
+ "sinon": "^17.0.1",
57
+ "ts-node": "^10.9.2",
58
+ "typedoc": "^0.25.12",
59
+ "typescript": "^5.4.3"
60
+ },
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "git://github.com/webpod/ps.git"
64
+ },
65
+ "license": "MIT"
66
+ }
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var __async = (__this, __arguments, generator) => {
30
+ return new Promise((resolve, reject) => {
31
+ var fulfilled = (value) => {
32
+ try {
33
+ step(generator.next(value));
34
+ } catch (e) {
35
+ reject(e);
36
+ }
37
+ };
38
+ var rejected = (value) => {
39
+ try {
40
+ step(generator.throw(value));
41
+ } catch (e) {
42
+ reject(e);
43
+ }
44
+ };
45
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
46
+ step((generator = generator.apply(__this, __arguments)).next());
47
+ });
48
+ };
49
+
50
+ // src/main/ts/index.ts
51
+ var ts_exports = {};
52
+ __export(ts_exports, {
53
+ default: () => ts_default,
54
+ kill: () => kill,
55
+ lookup: () => lookup,
56
+ tree: () => tree
57
+ });
58
+ module.exports = __toCommonJS(ts_exports);
59
+
60
+ // src/main/ts/ps.ts
61
+ var import_node_process = __toESM(require("node:process"), 1);
62
+ var import_node_fs = __toESM(require("node:fs"), 1);
63
+ var import_ingrid = require("@webpod/ingrid");
64
+ var import_spawn = require("zurk/spawn");
65
+ var import_node_os = require("node:os");
66
+ var EOL = /(\r\n)|(\n\r)|\n|\r/;
67
+ var IS_WIN = import_node_process.default.platform === "win32";
68
+ var isBin = (f) => {
69
+ if (f === "")
70
+ return false;
71
+ if (!f.includes("/"))
72
+ return true;
73
+ if (!import_node_fs.default.existsSync(f))
74
+ return false;
75
+ const stat = import_node_fs.default.lstatSync(f);
76
+ return stat.isFile() || stat.isSymbolicLink();
77
+ };
78
+ var lookup = (query = {}, cb = noop) => {
79
+ const { promise, resolve, reject } = makeDeferred();
80
+ const { psargs = ["-lx"] } = query;
81
+ const args = typeof psargs === "string" ? psargs.split(/\s+/) : psargs;
82
+ const extract = IS_WIN ? extractWmic : identity;
83
+ const callback = (err, { stdout }) => {
84
+ if (err) {
85
+ reject(err);
86
+ cb(err);
87
+ return;
88
+ }
89
+ const list = parseProcessList(extract(stdout), query);
90
+ resolve(list);
91
+ cb(null, list);
92
+ };
93
+ const ctx = IS_WIN ? {
94
+ cmd: "cmd",
95
+ input: "wmic process get ProcessId,ParentProcessId,CommandLine \n",
96
+ callback,
97
+ run(cb2) {
98
+ cb2();
99
+ }
100
+ } : {
101
+ cmd: "ps",
102
+ args,
103
+ run(cb2) {
104
+ cb2();
105
+ },
106
+ callback
107
+ };
108
+ (0, import_spawn.exec)(ctx);
109
+ return promise;
110
+ };
111
+ var parseProcessList = (output, query = {}) => {
112
+ const processList = parseGrid(output.trim());
113
+ const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
114
+ const filters = [
115
+ (p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
116
+ (p) => query.arguments ? new RegExp(query.arguments, "i").test(p.arguments.join(" ")) : true,
117
+ (p) => query.ppid ? query.ppid + "" === p.ppid : true
118
+ ];
119
+ return processList.filter(
120
+ (p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
121
+ );
122
+ };
123
+ var extractWmic = (stdout) => {
124
+ const _stdout = stdout.split(EOL);
125
+ const beginRow = _stdout.findIndex((out) => (out == null ? void 0 : out.indexOf("CommandLine")) === 0);
126
+ _stdout.splice(_stdout.length - 1, 1);
127
+ _stdout.splice(0, beginRow);
128
+ return _stdout.join(import_node_os.EOL);
129
+ };
130
+ var pickTree = (list, pid, recursive = false) => {
131
+ const children = list.filter((p) => p.ppid === pid + "");
132
+ return [
133
+ ...children,
134
+ ...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
135
+ ];
136
+ };
137
+ var tree = (_0, ..._1) => __async(void 0, [_0, ..._1], function* (opts, cb = noop) {
138
+ if (typeof opts === "string" || typeof opts === "number") {
139
+ return tree({ pid: opts }, cb);
140
+ }
141
+ try {
142
+ const { pid, recursive = false } = opts;
143
+ const list = pickTree(yield lookup(), pid, recursive);
144
+ cb(null, list);
145
+ return list;
146
+ } catch (err) {
147
+ cb(err);
148
+ throw err;
149
+ }
150
+ });
151
+ var kill = (pid, opts, next) => {
152
+ if (typeof opts == "function") {
153
+ return kill(pid, void 0, opts);
154
+ }
155
+ if (typeof opts == "string" || typeof opts == "number") {
156
+ return kill(pid, { signal: opts }, next);
157
+ }
158
+ const { promise, resolve, reject } = makeDeferred();
159
+ const {
160
+ timeout = 30,
161
+ signal = "SIGTERM"
162
+ } = opts || {};
163
+ try {
164
+ import_node_process.default.kill(+pid, signal);
165
+ } catch (e) {
166
+ reject(e);
167
+ next == null ? void 0 : next(e);
168
+ return promise;
169
+ }
170
+ let checkConfident = 0;
171
+ let checkTimeoutTimer;
172
+ let checkIsTimeout = false;
173
+ const checkKilled = (finishCallback) => lookup({ pid }, (err, list = []) => {
174
+ if (checkIsTimeout)
175
+ return;
176
+ if (err) {
177
+ clearTimeout(checkTimeoutTimer);
178
+ reject(err);
179
+ finishCallback == null ? void 0 : finishCallback(err, pid);
180
+ } else if (list.length > 0) {
181
+ checkConfident = checkConfident - 1 || 0;
182
+ checkKilled(finishCallback);
183
+ } else {
184
+ checkConfident++;
185
+ if (checkConfident === 5) {
186
+ clearTimeout(checkTimeoutTimer);
187
+ resolve(pid);
188
+ finishCallback == null ? void 0 : finishCallback(null, pid);
189
+ } else {
190
+ checkKilled(finishCallback);
191
+ }
192
+ }
193
+ });
194
+ if (next) {
195
+ checkKilled(next);
196
+ checkTimeoutTimer = setTimeout(() => {
197
+ checkIsTimeout = true;
198
+ next(new Error("Kill process timeout"));
199
+ }, timeout * 1e3);
200
+ } else {
201
+ resolve(pid);
202
+ }
203
+ return promise;
204
+ };
205
+ var parseGrid = (output) => output ? formatOutput((0, import_ingrid.parse)(output, { format: IS_WIN ? "win" : "unix" })) : [];
206
+ var formatOutput = (data) => data.reduce((m, d) => {
207
+ var _a, _b, _c, _d;
208
+ const pid = ((_a = d.PID) == null ? void 0 : _a[0]) || ((_b = d.ProcessId) == null ? void 0 : _b[0]);
209
+ const ppid = ((_c = d.PPID) == null ? void 0 : _c[0]) || ((_d = d.ParentProcessId) == null ? void 0 : _d[0]);
210
+ const cmd = d.CMD || d.CommandLine || d.COMMAND || [];
211
+ if (pid && cmd.length > 0) {
212
+ const c = cmd.findIndex((_v, i) => isBin(cmd.slice(0, i).join(" ")));
213
+ const command = cmd.slice(0, c).join(" ");
214
+ const args = cmd.length > 1 ? cmd.slice(c) : [];
215
+ m.push({
216
+ pid,
217
+ ppid,
218
+ command,
219
+ arguments: args
220
+ });
221
+ }
222
+ return m;
223
+ }, []);
224
+ var makeDeferred = () => {
225
+ let resolve;
226
+ let reject;
227
+ const promise = new Promise((res, rej) => {
228
+ resolve = res;
229
+ reject = rej;
230
+ });
231
+ return { resolve, reject, promise };
232
+ };
233
+ var noop = () => {
234
+ };
235
+ var identity = (v) => v;
236
+
237
+ // src/main/ts/index.ts
238
+ var ts_default = { lookup, kill, tree };
239
+ // Annotate the CommonJS export names for ESM import in node:
240
+ 0 && (module.exports = {
241
+ kill,
242
+ lookup,
243
+ tree
244
+ });
@@ -0,0 +1 @@
1
+ export declare const foo: () => undefined;
@@ -0,0 +1,8 @@
1
+ export type * from './ps.ts';
2
+ export { kill, lookup, tree } from './ps.ts';
3
+ declare const _default: {
4
+ lookup: (query?: import("./ps.ts").TPsLookupQuery, cb?: import("./ps.ts").TPsLookupCallback) => Promise<import("./ps.ts").TPsLookupEntry[]>;
5
+ kill: (pid: string | number, opts?: string | number | import("./ps.ts").TPsKillOptions | import("./ps.ts").TPsNext | undefined, next?: import("./ps.ts").TPsNext | undefined) => Promise<void>;
6
+ tree: (opts: string | number | import("./ps.ts").TPsTreeOpts, cb?: import("./ps.ts").TPsLookupCallback) => Promise<import("./ps.ts").TPsLookupEntry[]>;
7
+ };
8
+ export default _default;
@@ -0,0 +1,61 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { TIngridResponse } from '@webpod/ingrid';
3
+ export type TPsLookupCallback = (err: any, processList?: TPsLookupEntry[]) => void;
4
+ export type TPsLookupEntry = {
5
+ pid: string;
6
+ ppid?: string;
7
+ command: string;
8
+ arguments: string[];
9
+ };
10
+ export type TPsLookupQuery = {
11
+ pid?: number | string | (string | number)[];
12
+ command?: string;
13
+ arguments?: string;
14
+ ppid?: number | string;
15
+ psargs?: string | string[];
16
+ };
17
+ export type TPsKillOptions = {
18
+ timeout?: number;
19
+ signal?: string | number | NodeJS.Signals;
20
+ };
21
+ export type TPsNext = (err?: any, data?: any) => void;
22
+ /**
23
+ * Query Process: Focus on pid & cmd
24
+ * @param query
25
+ * @param {String|String[]} query.pid
26
+ * @param {String} query.command RegExp String
27
+ * @param {String} query.arguments RegExp String
28
+ * @param {String|String[]} query.psargs
29
+ * @param {Function} cb
30
+ * @param {Object=null} cb.err
31
+ * @param {Object[]} cb.processList
32
+ * @return {Object}
33
+ */
34
+ export declare const lookup: (query?: TPsLookupQuery, cb?: TPsLookupCallback) => Promise<TPsLookupEntry[]>;
35
+ export declare const parseProcessList: (output: string, query?: TPsLookupQuery) => TPsLookupEntry[];
36
+ export declare const extractWmic: (stdout: string) => string;
37
+ export type TPsTreeOpts = {
38
+ pid: string | number;
39
+ recursive?: boolean;
40
+ };
41
+ export declare const pickTree: (list: TPsLookupEntry[], pid: string | number, recursive?: boolean) => TPsLookupEntry[];
42
+ export declare const tree: (opts: string | number | TPsTreeOpts, cb?: TPsLookupCallback) => Promise<TPsLookupEntry[]>;
43
+ /**
44
+ * Kill process
45
+ * @param pid
46
+ * @param {Object|String} opts
47
+ * @param {String} opts.signal
48
+ * @param {number} opts.timeout
49
+ * @param next
50
+ */
51
+ export declare const kill: (pid: string | number, opts?: TPsNext | TPsKillOptions | TPsKillOptions['signal'], next?: TPsNext) => Promise<void>;
52
+ export declare const parseGrid: (output: string) => TPsLookupEntry[];
53
+ export declare const formatOutput: (data: TIngridResponse) => TPsLookupEntry[];
54
+ export type PromiseResolve<T = any> = (value?: T | PromiseLike<T>) => void;
55
+ export declare const makeDeferred: <T = any, E = any>() => {
56
+ promise: Promise<T>;
57
+ resolve: PromiseResolve<T>;
58
+ reject: PromiseResolve<E>;
59
+ };
60
+ export declare const noop: () => void;
61
+ export declare const identity: <T>(v: T) => T;
@@ -0,0 +1,184 @@
1
+ // src/main/ts/ps.ts
2
+ import process from "node:process";
3
+ import fs from "node:fs";
4
+ import { parse } from "@webpod/ingrid";
5
+ import { exec } from "zurk/spawn";
6
+ import { EOL as SystemEOL } from "node:os";
7
+ var EOL = /(\r\n)|(\n\r)|\n|\r/;
8
+ var IS_WIN = process.platform === "win32";
9
+ var isBin = (f) => {
10
+ if (f === "")
11
+ return false;
12
+ if (!f.includes("/"))
13
+ return true;
14
+ if (!fs.existsSync(f))
15
+ return false;
16
+ const stat = fs.lstatSync(f);
17
+ return stat.isFile() || stat.isSymbolicLink();
18
+ };
19
+ var lookup = (query = {}, cb = noop) => {
20
+ const { promise, resolve, reject } = makeDeferred();
21
+ const { psargs = ["-lx"] } = query;
22
+ const args = typeof psargs === "string" ? psargs.split(/\s+/) : psargs;
23
+ const extract = IS_WIN ? extractWmic : identity;
24
+ const callback = (err, { stdout }) => {
25
+ if (err) {
26
+ reject(err);
27
+ cb(err);
28
+ return;
29
+ }
30
+ const list = parseProcessList(extract(stdout), query);
31
+ resolve(list);
32
+ cb(null, list);
33
+ };
34
+ const ctx = IS_WIN ? {
35
+ cmd: "cmd",
36
+ input: "wmic process get ProcessId,ParentProcessId,CommandLine \n",
37
+ callback,
38
+ run(cb2) {
39
+ cb2();
40
+ }
41
+ } : {
42
+ cmd: "ps",
43
+ args,
44
+ run(cb2) {
45
+ cb2();
46
+ },
47
+ callback
48
+ };
49
+ exec(ctx);
50
+ return promise;
51
+ };
52
+ var parseProcessList = (output, query = {}) => {
53
+ const processList = parseGrid(output.trim());
54
+ const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
55
+ const filters = [
56
+ (p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
57
+ (p) => query.arguments ? new RegExp(query.arguments, "i").test(p.arguments.join(" ")) : true,
58
+ (p) => query.ppid ? query.ppid + "" === p.ppid : true
59
+ ];
60
+ return processList.filter(
61
+ (p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
62
+ );
63
+ };
64
+ var extractWmic = (stdout) => {
65
+ const _stdout = stdout.split(EOL);
66
+ const beginRow = _stdout.findIndex((out) => out?.indexOf("CommandLine") === 0);
67
+ _stdout.splice(_stdout.length - 1, 1);
68
+ _stdout.splice(0, beginRow);
69
+ return _stdout.join(SystemEOL);
70
+ };
71
+ var pickTree = (list, pid, recursive = false) => {
72
+ const children = list.filter((p) => p.ppid === pid + "");
73
+ return [
74
+ ...children,
75
+ ...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
76
+ ];
77
+ };
78
+ var tree = async (opts, cb = noop) => {
79
+ if (typeof opts === "string" || typeof opts === "number") {
80
+ return tree({ pid: opts }, cb);
81
+ }
82
+ try {
83
+ const { pid, recursive = false } = opts;
84
+ const list = pickTree(await lookup(), pid, recursive);
85
+ cb(null, list);
86
+ return list;
87
+ } catch (err) {
88
+ cb(err);
89
+ throw err;
90
+ }
91
+ };
92
+ var kill = (pid, opts, next) => {
93
+ if (typeof opts == "function") {
94
+ return kill(pid, void 0, opts);
95
+ }
96
+ if (typeof opts == "string" || typeof opts == "number") {
97
+ return kill(pid, { signal: opts }, next);
98
+ }
99
+ const { promise, resolve, reject } = makeDeferred();
100
+ const {
101
+ timeout = 30,
102
+ signal = "SIGTERM"
103
+ } = opts || {};
104
+ try {
105
+ process.kill(+pid, signal);
106
+ } catch (e) {
107
+ reject(e);
108
+ next?.(e);
109
+ return promise;
110
+ }
111
+ let checkConfident = 0;
112
+ let checkTimeoutTimer;
113
+ let checkIsTimeout = false;
114
+ const checkKilled = (finishCallback) => lookup({ pid }, (err, list = []) => {
115
+ if (checkIsTimeout)
116
+ return;
117
+ if (err) {
118
+ clearTimeout(checkTimeoutTimer);
119
+ reject(err);
120
+ finishCallback?.(err, pid);
121
+ } else if (list.length > 0) {
122
+ checkConfident = checkConfident - 1 || 0;
123
+ checkKilled(finishCallback);
124
+ } else {
125
+ checkConfident++;
126
+ if (checkConfident === 5) {
127
+ clearTimeout(checkTimeoutTimer);
128
+ resolve(pid);
129
+ finishCallback?.(null, pid);
130
+ } else {
131
+ checkKilled(finishCallback);
132
+ }
133
+ }
134
+ });
135
+ if (next) {
136
+ checkKilled(next);
137
+ checkTimeoutTimer = setTimeout(() => {
138
+ checkIsTimeout = true;
139
+ next(new Error("Kill process timeout"));
140
+ }, timeout * 1e3);
141
+ } else {
142
+ resolve(pid);
143
+ }
144
+ return promise;
145
+ };
146
+ var parseGrid = (output) => output ? formatOutput(parse(output, { format: IS_WIN ? "win" : "unix" })) : [];
147
+ var formatOutput = (data) => data.reduce((m, d) => {
148
+ const pid = d.PID?.[0] || d.ProcessId?.[0];
149
+ const ppid = d.PPID?.[0] || d.ParentProcessId?.[0];
150
+ const cmd = d.CMD || d.CommandLine || d.COMMAND || [];
151
+ if (pid && cmd.length > 0) {
152
+ const c = cmd.findIndex((_v, i) => isBin(cmd.slice(0, i).join(" ")));
153
+ const command = cmd.slice(0, c).join(" ");
154
+ const args = cmd.length > 1 ? cmd.slice(c) : [];
155
+ m.push({
156
+ pid,
157
+ ppid,
158
+ command,
159
+ arguments: args
160
+ });
161
+ }
162
+ return m;
163
+ }, []);
164
+ var makeDeferred = () => {
165
+ let resolve;
166
+ let reject;
167
+ const promise = new Promise((res, rej) => {
168
+ resolve = res;
169
+ reject = rej;
170
+ });
171
+ return { resolve, reject, promise };
172
+ };
173
+ var noop = () => {
174
+ };
175
+ var identity = (v) => v;
176
+
177
+ // src/main/ts/index.ts
178
+ var ts_default = { lookup, kill, tree };
179
+ export {
180
+ ts_default as default,
181
+ kill,
182
+ lookup,
183
+ tree
184
+ };