@webpod/ps 1.1.0 → 1.2.1

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/README.md CHANGED
@@ -2,163 +2,104 @@
2
2
 
3
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
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
- * [x] Brings sync API
11
- * [x] Builds a process subtree by parent
5
+ ## Features
6
+ - Written in TypeScript, ships with types
7
+ - CJS and ESM entry points
8
+ - Promise and callback API, sync variants
9
+ - Process tree traversal by parent pid
10
+ - Uses `@webpod/ingrid` instead of `table-parser` ([neekey/ps#76](https://github.com/neekey/ps/issues/76), [neekey/ps#62](https://github.com/neekey/ps/issues/62))
12
11
 
13
12
  ## Install
14
13
  ```bash
15
- $ npm install @webpod/ps
14
+ npm install @webpod/ps
16
15
  ```
17
16
 
18
17
  ## Internals
19
- This module uses different approaches for getting process list:
20
18
 
21
- | Platform | Method |
22
- |--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
23
- | Unix/Mac | `ps -lx` |
24
- | Windows (kernel >= 26000)| `pwsh -NoProfile -Command "Get-CimInstance Win32_Process \| Select-Object ProcessId,ParentProcessId,CommandLine \| ConvertTo-Json -Compress"` |
25
- | Windows (kernel < 26000) | [`wmic`](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmic) `process get ProcessId,CommandLine` |
19
+ | Platform | Command |
20
+ |---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
21
+ | Unix / macOS | `ps -eo pid,ppid,args` |
22
+ | Windows (kernel >= 26000) | `pwsh -NoProfile -Command "Get-CimInstance Win32_Process \| Select-Object ProcessId,ParentProcessId,CommandLine \| ConvertTo-Json -Compress"` |
23
+ | Windows (kernel < 26000) | [`wmic`](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmic) `process get ProcessId,CommandLine` |
26
24
 
27
- ## Usage
25
+ ## API
26
+
27
+ ### lookup(query?, callback?)
28
+ Returns a list of processes matching the query.
28
29
 
29
- ### lookup()
30
- Searches for the process by the specified `pid`.
31
30
  ```ts
32
- import {lookup} from '@webpod/ps'
33
-
34
- // Both callback and promise styles are supported
35
- const list = await lookup({pid: 12345})
36
-
37
- // or
38
- lookup({pid: 12345}, (err, list) => {
39
- if (err) {
40
- throw new Error(err)
41
- }
42
-
43
- const [found] = list
44
- if (found) {
45
- console.log('PID: %s, COMMAND: %s, ARGUMENTS: %s', found.pid, found.command, found.arguments)
46
- } else {
47
- console.log('No such process found!')
48
- }
49
- })
31
+ import { lookup } from '@webpod/ps'
50
32
 
51
- // or syncronously
52
- const _list = lookup.sync({pid: 12345})
53
- ```
33
+ // Find by pid
34
+ const list = await lookup({ pid: 12345 })
35
+ // [{ pid: '12345', ppid: '123', command: '/usr/bin/node', arguments: ['server.js', '--port=3000'] }]
54
36
 
55
- Define a query opts to filter the results by `command` and/or `arguments` predicates:
56
- ```ts
57
- const list = await lookup({
58
- command: 'node', // it will be used to build a regex
59
- arguments: '--debug',
60
- })
37
+ // Filter by command and/or arguments (treated as RegExp)
38
+ const nodes = await lookup({ command: 'node', arguments: '--debug' })
61
39
 
62
- list.forEach(entry => {
63
- console.log('PID: %s, COMMAND: %s, ARGUMENTS: %s', entry.pid, entry.command, entry.arguments);
64
- })
40
+ // Filter by parent pid
41
+ const children = await lookup({ ppid: 82292 })
42
+
43
+ // Synchronous
44
+ const all = lookup.sync()
65
45
  ```
66
46
 
67
- Unix users can override the default `ps` arguments:
47
+ On Unix, you can override the default `ps` arguments via `psargs`:
68
48
  ```ts
69
- lookup({
70
- command: 'node',
71
- psargs: 'ux'
72
- }, (err, resultList) => {
73
- // ...
74
- })
49
+ const list = await lookup({ command: 'node', psargs: '-eo pid,ppid,comm' })
75
50
  ```
76
51
 
77
- 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)
52
+ Callback style is also supported:
78
53
  ```ts
79
- lookup({
80
- command: 'mongod',
81
- psargs: '-l',
82
- ppid: 82292
83
- }, (err, resultList) => {
84
- // ...
85
- })
54
+ lookup({ pid: 12345 }, (err, list) => { /* ... */ })
86
55
  ```
87
56
 
88
- ### tree()
89
- Returns a child processes list by the specified parent `pid`. Some kind of shortcut for `lookup({ppid: pid})`.
57
+ ### tree(opts?, callback?)
58
+ Returns child processes of a given parent pid.
59
+
90
60
  ```ts
91
61
  import { tree } from '@webpod/ps'
92
62
 
93
- const children = await tree(123)
94
- /**
95
- [
96
- {pid: 124, ppid: 123},
97
- {pid: 125, ppid: 123}
98
- ]
99
- */
63
+ // Direct children
64
+ const children = await tree(123)
65
+ // [
66
+ // { pid: '124', ppid: '123', command: 'node', arguments: ['worker.js'] },
67
+ // { pid: '125', ppid: '123', command: 'node', arguments: ['worker.js'] }
68
+ // ]
69
+
70
+ // All descendants
71
+ const all = await tree({ pid: 123, recursive: true })
72
+ // [
73
+ // { pid: '124', ppid: '123', ... },
74
+ // { pid: '125', ppid: '123', ... },
75
+ // { pid: '126', ppid: '124', ... },
76
+ // { pid: '127', ppid: '125', ... }
77
+ // ]
78
+
79
+ // Synchronous
80
+ const list = tree.sync({ pid: 123, recursive: true })
100
81
  ```
101
82
 
102
- To obtain all nested children, set `recursive` option to `true`:
103
- ```ts
104
- const children = await tree({pid: 123, recursive: true})
105
- /**
106
- [
107
- {pid: 124, ppid: 123},
108
- {pid: 125, ppid: 123},
109
-
110
- {pid: 126, ppid: 124},
111
- {pid: 127, ppid: 124},
112
- {pid: 128, ppid: 124},
113
-
114
- {pid: 129, ppid: 125},
115
- {pid: 130, ppid: 125},
116
- ]
117
- */
118
-
119
- // or syncronously
120
- const list = tree.sync({pid: 123, recursive: true})
121
- ```
122
-
123
- ### kill()
124
- Eliminates the process by its `pid`.
83
+ ### kill(pid, opts?, callback?)
84
+ Kills a process and waits for it to exit. The returned promise resolves once the process is confirmed dead, or rejects on timeout.
125
85
 
126
86
  ```ts
127
87
  import { kill } from '@webpod/ps'
128
88
 
129
- kill('12345', (err, pid) => {
130
- if (err) {
131
- throw new Error(err)
132
- } else {
133
- console.log('Process %s has been killed!', pid)
134
- }
135
- })
136
- ```
89
+ // Sends SIGTERM, polls until the process is gone (default timeout 30s)
90
+ await kill(12345)
137
91
 
138
- 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.
92
+ // With signal
93
+ await kill(12345, 'SIGKILL')
139
94
 
140
- ```ts
141
- import { kill } from '@webpod/ps'
95
+ // With custom timeout (seconds) and polling interval (ms)
96
+ await kill(12345, { signal: 'SIGKILL', timeout: 10, interval: 250 })
142
97
 
143
- // Pass signal SIGKILL for killing the process without allowing it to clean up
144
- kill('12345', 'SIGKILL', (err, pid) => {
145
- if (err) {
146
- throw new Error(err)
147
- } else {
148
- console.log('Process %s has been killed without a clean-up!', pid)
149
- }
98
+ // With callback
99
+ await kill(12345, (err, pid) => {
100
+ // called when the process is confirmed dead or timeout is reached
150
101
  })
151
102
  ```
152
103
 
153
- You can also use object notation to specify more opts:
154
- ```ts
155
- kill( '12345', {
156
- signal: 'SIGKILL',
157
- timeout: 10, // will set up a ten seconds timeout if the killing is not successful
158
- }, () => {})
159
- ```
160
-
161
- Notice that the nodejs build-in `process.kill()` does not accept number as a signal, you will have to use string format.
162
-
163
104
  ## License
164
105
  [MIT](./LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpod/ps",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "A process lookup utility",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -23,12 +23,23 @@
23
23
  "build:dts": "tsc --emitDeclarationOnly --outDir target/dts",
24
24
  "build:docs": "typedoc --options src/main/typedoc",
25
25
  "build:stamp": "npx buildstamp",
26
- "test": "concurrently 'npm:test:lint' 'npm:test:unit' && npm run test:legacy",
26
+ "test": "concurrently 'npm:test:lint' 'npm:test:unit' 'npm:test:size' && npm run test:legacy",
27
27
  "test:lint": "oxlint -c oxlintrc.json src/main/ts src/test/ts",
28
28
  "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",
29
- "test:legacy": "node ./node_modules/mocha/bin/mocha -t 0 -R spec src/test/legacy/test.cjs",
29
+ "test:legacy": "mocha -t 0 -R spec src/test/legacy/test.cjs",
30
+ "test:size": "size-limit",
30
31
  "publish:draft": "npm run build && npm publish --no-git-tag-version"
31
32
  },
33
+ "size-limit": [
34
+ {
35
+ "path": "target/esm/index.mjs",
36
+ "limit": "4 kB"
37
+ },
38
+ {
39
+ "path": "target/cjs/index.cjs",
40
+ "limit": "5 kB"
41
+ }
42
+ ],
32
43
  "files": [
33
44
  "target/cjs",
34
45
  "target/esm",
@@ -45,24 +56,28 @@
45
56
  "zurk": "^0.11.10"
46
57
  },
47
58
  "devDependencies": {
59
+ "@size-limit/file": "^12.0.1",
48
60
  "@types/node": "^25.5.2",
49
61
  "c8": "^11.0.0",
50
62
  "concurrently": "^9.2.1",
51
63
  "esbuild": "^0.28.0",
52
64
  "esbuild-node-externals": "^1.21.0",
53
65
  "esbuild-plugin-entry-chunks": "^0.1.18",
54
- "oxlint": "^1.58.0",
55
66
  "fast-glob": "^3.3.3",
56
67
  "minimist": "^1.2.8",
57
- "mocha": "^10.8.2",
58
- "sinon": "^18.0.1",
68
+ "mocha": "^12.0.0-beta-9.2",
69
+ "oxlint": "^1.59.0",
70
+ "size-limit": "^12.0.1",
59
71
  "ts-node": "^10.9.2",
60
72
  "typedoc": "^0.28.18",
61
- "typescript": "^5.9.2"
73
+ "typescript": "^6.0.2"
62
74
  },
63
75
  "repository": {
64
76
  "type": "git",
65
77
  "url": "git://github.com/webpod/ps.git"
66
78
  },
67
- "license": "MIT"
79
+ "license": "MIT",
80
+ "overrides": {
81
+ "chokidar": "^4.0.3"
82
+ }
68
83
  }
@@ -65,234 +65,210 @@ var import_node_fs = __toESM(require("node:fs"), 1);
65
65
  var import_node_os = __toESM(require("node:os"), 1);
66
66
  var import_ingrid = require("@webpod/ingrid");
67
67
  var import_spawn = require("zurk/spawn");
68
+ var noop = () => {
69
+ };
68
70
  var IS_WIN = import_node_process.default.platform === "win32";
69
71
  var IS_WIN2025_PLUS = IS_WIN && Number.parseInt(import_node_os.default.release().split(".")[2], 10) >= 26e3;
72
+ var LOOKUP_FLOW = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
70
73
  var LOOKUPS = {
71
74
  wmic: {
72
75
  cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
73
76
  args: [],
74
- parse(stdout) {
75
- return (0, import_ingrid.parse)(removeWmicPrefix(stdout), { format: "win" });
76
- }
77
+ parse: (stdout) => (0, import_ingrid.parse)(removeWmicPrefix(stdout), { format: "win" })
77
78
  },
78
79
  ps: {
79
80
  cmd: "ps",
80
81
  args: ["-eo", "pid,ppid,args"],
81
- parse(stdout) {
82
- return (0, import_ingrid.parse)(stdout, { format: "unix" });
83
- }
82
+ parse: (stdout) => (0, import_ingrid.parse)(stdout, { format: "unix" })
84
83
  },
85
84
  pwsh: {
86
85
  cmd: "pwsh",
87
86
  args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
88
87
  parse(stdout) {
89
- let arr = [];
90
88
  try {
91
- arr = JSON.parse(stdout);
89
+ const arr = JSON.parse(stdout);
90
+ return arr.map((p) => ({
91
+ ProcessId: [String(p.ProcessId)],
92
+ ParentProcessId: [String(p.ParentProcessId)],
93
+ CommandLine: p.CommandLine ? [p.CommandLine] : []
94
+ }));
92
95
  } catch (e) {
93
96
  return [];
94
97
  }
95
- return arr.map((p) => ({
96
- ProcessId: [p.ProcessId + ""],
97
- ParentProcessId: [p.ParentProcessId + ""],
98
- CommandLine: p.CommandLine ? [p.CommandLine] : []
99
- }));
100
98
  }
101
99
  }
102
100
  };
103
- var isBin = (f) => {
104
- if (f === "") return false;
105
- if (!f.includes("/") && !f.includes("\\")) return true;
106
- if (f.length > 3 && f[0] === '"')
107
- return f[f.length - 1] === '"' ? isBin(f.slice(1, -1)) : false;
108
- try {
109
- if (!import_node_fs.default.existsSync(f)) return false;
110
- const stat = import_node_fs.default.lstatSync(f);
111
- return stat.isFile() || stat.isSymbolicLink();
112
- } catch (e) {
113
- return false;
114
- }
115
- };
116
- var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
117
- var lookupSync = (query = {}, cb = noop) => _lookup({ query, cb, sync: true });
101
+ var lookup = (query = {}, cb = noop) => runLookup(query, cb, false);
102
+ var lookupSync = (query = {}, cb = noop) => runLookup(query, cb, true);
118
103
  lookup.sync = lookupSync;
119
- var _lookup = ({
120
- query = {},
121
- cb = noop,
122
- sync = false
123
- }) => {
124
- const pFactory = sync ? makePseudoDeferred.bind(null, []) : makeDeferred;
125
- const { promise, resolve, reject } = pFactory();
126
- const result = [];
127
- const lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
128
- const { parse: parse2, cmd, args } = LOOKUPS[lookupFlow];
129
- const callback = (err, { stdout }) => {
104
+ function runLookup(query, cb, sync) {
105
+ const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[LOOKUP_FLOW];
106
+ const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
107
+ let result = [];
108
+ let error;
109
+ const handle = (err, { stdout }) => {
130
110
  if (err) {
131
- reject(err);
132
- cb(err);
111
+ error = err;
133
112
  return;
134
113
  }
135
- result.push(...filterProcessList(normalizeOutput(parse2(stdout)), query));
136
- resolve(result);
137
- cb(null, result);
114
+ result = filterProcessList(normalizeOutput(parseOutput(stdout)), query);
138
115
  };
139
- (0, import_spawn.exec)({
140
- cmd,
141
- args,
142
- callback,
143
- sync,
144
- run(cb2) {
145
- cb2();
146
- }
147
- });
148
- return Object.assign(promise, result);
149
- };
150
- var filterProcessList = (processList, query = {}) => {
151
- const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
152
- const filters = [
153
- (p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
154
- (p) => query.arguments ? new RegExp(query.arguments, "i").test(p.arguments.join(" ")) : true,
155
- (p) => query.ppid ? query.ppid + "" === p.ppid : true
156
- ];
157
- return processList.filter(
158
- (p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
159
- );
160
- };
161
- var removeWmicPrefix = (stdout) => {
162
- const s = stdout.indexOf(LOOKUPS.wmic.cmd + import_node_os.default.EOL);
163
- const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(import_node_os.default.EOL) : stdout.length;
164
- return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
165
- };
166
- var pickTree = (list, pid, recursive = false) => {
167
- const children = list.filter((p) => p.ppid === pid + "");
168
- return [
169
- ...children,
170
- ...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
171
- ];
172
- };
173
- var _tree = ({
174
- cb = noop,
175
- opts,
176
- sync = false
177
- }) => {
178
- if (typeof opts === "string" || typeof opts === "number") {
179
- return _tree({ opts: { pid: opts }, cb, sync });
116
+ if (sync) {
117
+ (0, import_spawn.exec)({ cmd, args, sync: true, callback: handle, run(c) {
118
+ c();
119
+ } });
120
+ cb(error != null ? error : null, error ? void 0 : result);
121
+ if (error) throw error;
122
+ return result;
180
123
  }
181
- const onError = (err) => cb(err);
182
- const onData = (all) => {
183
- if (opts === void 0) return all;
184
- const { pid, recursive = false } = opts;
185
- const list = pickTree(all, pid, recursive);
124
+ return new Promise((resolve, reject) => {
125
+ (0, import_spawn.exec)({
126
+ cmd,
127
+ args,
128
+ sync: false,
129
+ run(c) {
130
+ c();
131
+ },
132
+ callback(err, ctx) {
133
+ handle(err, ctx);
134
+ if (error) {
135
+ cb(error);
136
+ reject(error);
137
+ } else {
138
+ cb(null, result);
139
+ resolve(result);
140
+ }
141
+ }
142
+ });
143
+ });
144
+ }
145
+ var tree = (_0, ..._1) => __async(null, [_0, ..._1], function* (opts, cb = noop) {
146
+ try {
147
+ const list = pickFromTree(yield lookup(), opts);
186
148
  cb(null, list);
187
149
  return list;
188
- };
150
+ } catch (err) {
151
+ cb(err);
152
+ throw err;
153
+ }
154
+ });
155
+ var treeSync = (opts, cb = noop) => {
189
156
  try {
190
- const all = _lookup({ sync });
191
- return sync ? onData(all) : all.then(onData, (err) => {
192
- onError(err);
193
- throw err;
194
- });
157
+ const list = pickFromTree(lookupSync(), opts);
158
+ cb(null, list);
159
+ return list;
195
160
  } catch (err) {
196
- onError(err);
197
- return Promise.reject(err);
161
+ cb(err);
162
+ throw err;
198
163
  }
199
164
  };
200
- var tree = (opts, cb) => __async(null, null, function* () {
201
- return _tree({ opts, cb });
202
- });
203
- var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
204
165
  tree.sync = treeSync;
166
+ var pickFromTree = (all, opts) => {
167
+ if (opts === void 0) return all;
168
+ const { pid, recursive = false } = typeof opts === "object" ? opts : { pid: opts };
169
+ return pickTree(all, pid, recursive);
170
+ };
171
+ var pickTree = (list, pid, recursive = false) => {
172
+ const children = list.filter((p) => p.ppid === String(pid));
173
+ return recursive ? children.flatMap((p) => [p, ...pickTree(list, p.pid, true)]) : children;
174
+ };
205
175
  var kill = (pid, opts, next) => {
206
- if (typeof opts == "function") {
207
- return kill(pid, void 0, opts);
208
- }
209
- if (typeof opts == "string" || typeof opts == "number") {
210
- return kill(pid, { signal: opts }, next);
211
- }
212
- const { promise, resolve, reject } = makeDeferred();
213
- const {
214
- timeout = 30,
215
- signal = "SIGTERM"
216
- } = opts || {};
217
- try {
218
- import_node_process.default.kill(+pid, signal);
219
- } catch (e) {
220
- reject(e);
221
- next == null ? void 0 : next(e);
222
- return promise;
223
- }
224
- let checkConfident = 0;
225
- let checkTimeoutTimer;
226
- let checkIsTimeout = false;
227
- const checkKilled = (finishCallback) => lookup({ pid }, (err, list = []) => {
228
- if (checkIsTimeout) return;
229
- if (err) {
230
- clearTimeout(checkTimeoutTimer);
231
- reject(err);
232
- finishCallback == null ? void 0 : finishCallback(err, pid);
233
- } else if (list.length > 0) {
234
- checkConfident = checkConfident - 1 || 0;
235
- checkKilled(finishCallback);
236
- } else {
237
- checkConfident++;
238
- if (checkConfident === 5) {
239
- clearTimeout(checkTimeoutTimer);
240
- resolve(pid);
241
- finishCallback == null ? void 0 : finishCallback(null, pid);
242
- } else {
243
- checkKilled(finishCallback);
244
- }
176
+ if (typeof opts === "function") return kill(pid, void 0, opts);
177
+ if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
178
+ const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
179
+ const sPid = String(pid);
180
+ return new Promise((resolve, reject) => {
181
+ let done = false;
182
+ const entry = { pid: sPid, registered: 0, interval, settle: noop };
183
+ const settle = (err) => {
184
+ if (done) return;
185
+ done = true;
186
+ clearTimeout(timer);
187
+ killPending.delete(entry);
188
+ if (err) reject(err);
189
+ else resolve(pid);
190
+ next == null ? void 0 : next(err != null ? err : null, pid);
191
+ };
192
+ entry.settle = settle;
193
+ const timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
194
+ try {
195
+ import_node_process.default.kill(+pid, signal);
196
+ } catch (e) {
197
+ settle(e);
198
+ return;
245
199
  }
200
+ entry.registered = Date.now();
201
+ killPending.add(entry);
202
+ scheduleKillTick();
246
203
  });
247
- if (next) {
248
- checkKilled(next);
249
- checkTimeoutTimer = setTimeout(() => {
250
- checkIsTimeout = true;
251
- next(new Error("Kill process timeout"));
252
- }, timeout * 1e3);
253
- } else {
254
- resolve(pid);
255
- }
256
- return promise;
257
204
  };
258
- var normalizeOutput = (data) => data.reduce((m, d) => {
205
+ var killPending = /* @__PURE__ */ new Set();
206
+ var killTickTimer = null;
207
+ var killTickRunning = false;
208
+ var scheduleKillTick = (lastStart = 0) => {
209
+ if (killTickTimer || killTickRunning || killPending.size === 0) return;
210
+ let minInterval = Infinity;
211
+ for (const k of killPending) if (k.interval < minInterval) minInterval = k.interval;
212
+ const delay = lastStart === 0 ? 0 : Math.max(0, lastStart + minInterval - Date.now());
213
+ killTickTimer = setTimeout(runKillTick, delay);
214
+ };
215
+ var runKillTick = () => {
216
+ killTickTimer = null;
217
+ if (killPending.size === 0) return;
218
+ killTickRunning = true;
219
+ const startedAt = Date.now();
220
+ lookup().then((list) => {
221
+ const alive = new Set(list.map((p) => p.pid));
222
+ for (const k of killPending) {
223
+ if (k.registered >= startedAt) continue;
224
+ if (!alive.has(k.pid)) k.settle();
225
+ }
226
+ killTickRunning = false;
227
+ scheduleKillTick(startedAt);
228
+ }, (err) => {
229
+ for (const k of killPending) k.settle(err);
230
+ killTickRunning = false;
231
+ });
232
+ };
233
+ var normalizeOutput = (data) => data.flatMap((d) => {
259
234
  var _a, _b;
260
235
  const pid = (_a = d.PID || d.ProcessId) == null ? void 0 : _a[0];
261
236
  const ppid = (_b = d.PPID || d.ParentProcessId) == null ? void 0 : _b[0];
262
- const _cmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
263
- const cmd = _cmd.length === 1 ? _cmd[0].split(/\s+/) : _cmd;
264
- if (pid && cmd.length > 0) {
265
- const c = cmd.findIndex((_v, i) => isBin(cmd.slice(0, i).join(" ")));
266
- const command = (c === -1 ? cmd : cmd.slice(0, c)).join(" ");
267
- const args = c === -1 ? [] : cmd.slice(c);
268
- m.push({
269
- pid,
270
- ppid,
271
- command,
272
- arguments: args
273
- });
274
- }
275
- return m;
276
- }, []);
277
- var makeDeferred = () => {
278
- let resolve;
279
- let reject;
280
- const promise = new Promise((res, rej) => {
281
- resolve = res;
282
- reject = rej;
283
- });
284
- return { resolve, reject, promise };
237
+ const rawCmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
238
+ const parts = rawCmd.length === 1 ? rawCmd[0].split(/\s+/) : rawCmd;
239
+ if (!pid || parts.length === 0) return [];
240
+ const binIdx = parts.findIndex((_v, i) => isBin(parts.slice(0, i).join(" ")));
241
+ const command = (binIdx === -1 ? parts : parts.slice(0, binIdx)).join(" ");
242
+ const args = binIdx === -1 ? [] : parts.slice(binIdx);
243
+ return [{ pid, ppid, command, arguments: args }];
244
+ });
245
+ var filterProcessList = (processList, query = {}) => {
246
+ const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map(String);
247
+ const commandRe = query.command ? new RegExp(query.command, "i") : null;
248
+ const argumentsRe = query.arguments ? new RegExp(query.arguments, "i") : null;
249
+ const ppid = query.ppid === void 0 ? null : String(query.ppid);
250
+ return processList.filter(
251
+ (p) => (pidList.length === 0 || pidList.includes(p.pid)) && (!commandRe || commandRe.test(p.command)) && (!argumentsRe || argumentsRe.test(p.arguments.join(" "))) && (!ppid || ppid === p.ppid)
252
+ );
285
253
  };
286
- var makePseudoDeferred = (r = {}) => ({
287
- promise: r,
288
- resolve: identity,
289
- reject(e) {
290
- throw e;
254
+ var removeWmicPrefix = (stdout) => {
255
+ const s = stdout.indexOf(LOOKUPS.wmic.cmd + import_node_os.default.EOL);
256
+ const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(import_node_os.default.EOL) : stdout.length;
257
+ return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
258
+ };
259
+ var isBin = (f) => {
260
+ if (f === "") return false;
261
+ if (!f.includes("/") && !f.includes("\\")) return true;
262
+ if (f.length > 3 && f[0] === '"')
263
+ return f.at(-1) === '"' ? isBin(f.slice(1, -1)) : false;
264
+ try {
265
+ if (!import_node_fs.default.existsSync(f)) return false;
266
+ const stat = import_node_fs.default.lstatSync(f);
267
+ return stat.isFile() || stat.isSymbolicLink();
268
+ } catch (e) {
269
+ return false;
291
270
  }
292
- });
293
- var noop = () => {
294
271
  };
295
- var identity = (v) => v;
296
272
 
297
273
  // src/main/ts/index.ts
298
274
  var index_default = { kill, lookup, lookupSync, tree, treeSync };
@@ -8,9 +8,9 @@ declare const _default: {
8
8
  };
9
9
  lookupSync: (query?: import("./ps.js").TPsLookupQuery, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
10
10
  tree: {
11
- (opts?: string | number | import("./ps.js").TPsTreeOpts | undefined, cb?: import("./ps.js").TPsLookupCallback): Promise<import("./ps.js").TPsLookupEntry[]>;
12
- sync: (opts?: string | number | import("./ps.js").TPsTreeOpts | undefined, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
11
+ (opts?: string | number | import("./ps.js").TPsTreeOpts, cb?: import("./ps.js").TPsLookupCallback): Promise<import("./ps.js").TPsLookupEntry[]>;
12
+ sync: (opts?: string | number | import("./ps.js").TPsTreeOpts, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
13
13
  };
14
- treeSync: (opts?: string | number | import("./ps.js").TPsTreeOpts | undefined, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
14
+ treeSync: (opts?: string | number | import("./ps.js").TPsTreeOpts, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
15
15
  };
16
16
  export default _default;
@@ -1,5 +1,4 @@
1
1
  import { type TIngridResponse } from '@webpod/ingrid';
2
- export type TPsLookupCallback = (err: any, processList?: TPsLookupEntry[]) => void;
3
2
  export type TPsLookupEntry = {
4
3
  pid: string;
5
4
  ppid?: string;
@@ -11,57 +10,45 @@ export type TPsLookupQuery = {
11
10
  command?: string;
12
11
  arguments?: string;
13
12
  ppid?: number | string;
13
+ psargs?: string;
14
14
  };
15
+ export type TPsLookupCallback = (err: any, processList?: TPsLookupEntry[]) => void;
15
16
  export type TPsKillOptions = {
16
17
  timeout?: number;
17
18
  signal?: string | number | NodeJS.Signals;
19
+ /** Polling interval in ms between exit checks (default 200). */
20
+ interval?: number;
21
+ };
22
+ export type TPsTreeOpts = {
23
+ pid: string | number;
24
+ recursive?: boolean;
18
25
  };
19
26
  export type TPsNext = (err?: any, data?: any) => void;
20
27
  /**
21
- * Query Process: Focus on pid & cmd
22
- * @param query
23
- * @param {String|String[]} query.pid
24
- * @param {String} query.command RegExp String
25
- * @param {String} query.arguments RegExp String
26
- * @param {String|String[]} query.psargs
27
- * @param {TPsLookupCallback} cb
28
- * @return {Promise<TPsLookupEntry[]>}
28
+ * Query running processes by pid, command, arguments or ppid.
29
+ * Supports both promise and callback styles.
29
30
  */
30
31
  export declare const lookup: {
31
32
  (query?: TPsLookupQuery, cb?: TPsLookupCallback): Promise<TPsLookupEntry[]>;
32
33
  sync: (query?: TPsLookupQuery, cb?: TPsLookupCallback) => TPsLookupEntry[];
33
34
  };
34
- /**
35
- * Looks up the process list synchronously
36
- * @param query
37
- * @param {String|String[]} query.pid
38
- * @param {String} query.command RegExp String
39
- * @param {String} query.arguments RegExp String
40
- * @param {String|String[]} query.psargs
41
- * @param {TPsLookupCallback} cb
42
- * @return {TPsLookupEntry[]}
43
- */
35
+ /** Synchronous version of {@link lookup}. */
44
36
  export declare const lookupSync: (query?: TPsLookupQuery, cb?: TPsLookupCallback) => TPsLookupEntry[];
45
- export declare const filterProcessList: (processList: TPsLookupEntry[], query?: TPsLookupQuery) => TPsLookupEntry[];
46
- export declare const removeWmicPrefix: (stdout: string) => string;
47
- export type TPsTreeOpts = {
48
- pid: string | number;
49
- recursive?: boolean;
50
- };
51
- export declare const pickTree: (list: TPsLookupEntry[], pid: string | number, recursive?: boolean) => TPsLookupEntry[];
37
+ /** Returns child processes of the given parent pid. */
52
38
  export declare const tree: {
53
- (opts?: string | number | TPsTreeOpts | undefined, cb?: TPsLookupCallback): Promise<TPsLookupEntry[]>;
54
- sync: (opts?: string | number | TPsTreeOpts | undefined, cb?: TPsLookupCallback) => TPsLookupEntry[];
39
+ (opts?: string | number | TPsTreeOpts, cb?: TPsLookupCallback): Promise<TPsLookupEntry[]>;
40
+ sync: (opts?: string | number | TPsTreeOpts, cb?: TPsLookupCallback) => TPsLookupEntry[];
55
41
  };
56
- export declare const treeSync: (opts?: string | number | TPsTreeOpts | undefined, cb?: TPsLookupCallback) => TPsLookupEntry[];
42
+ /** Synchronous version of {@link tree}. */
43
+ export declare const treeSync: (opts?: string | number | TPsTreeOpts, cb?: TPsLookupCallback) => TPsLookupEntry[];
44
+ export declare const pickTree: (list: TPsLookupEntry[], pid: string | number, recursive?: boolean) => TPsLookupEntry[];
57
45
  /**
58
- * Kill process
59
- * @param pid
60
- * @param {Object|String} opts
61
- * @param {String} opts.signal
62
- * @param {number} opts.timeout
63
- * @param next
46
+ * Kills a process by pid.
47
+ * @param pid - Process ID to kill
48
+ * @param opts - Signal, options object, or callback
49
+ * @param next - Callback invoked when kill is confirmed or timed out
64
50
  */
65
51
  export declare const kill: (pid: string | number, opts?: TPsNext | TPsKillOptions | TPsKillOptions["signal"], next?: TPsNext) => Promise<void>;
66
52
  export declare const normalizeOutput: (data: TIngridResponse) => TPsLookupEntry[];
67
- export type PromiseResolve<T = any> = (value?: T | PromiseLike<T>) => void;
53
+ export declare const filterProcessList: (processList: TPsLookupEntry[], query?: TPsLookupQuery) => TPsLookupEntry[];
54
+ export declare const removeWmicPrefix: (stdout: string) => string;
@@ -4,231 +4,209 @@ import fs from "node:fs";
4
4
  import os from "node:os";
5
5
  import { parse } from "@webpod/ingrid";
6
6
  import { exec } from "zurk/spawn";
7
+ var noop = () => {
8
+ };
7
9
  var IS_WIN = process.platform === "win32";
8
10
  var IS_WIN2025_PLUS = IS_WIN && Number.parseInt(os.release().split(".")[2], 10) >= 26e3;
11
+ var LOOKUP_FLOW = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
9
12
  var LOOKUPS = {
10
13
  wmic: {
11
14
  cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
12
15
  args: [],
13
- parse(stdout) {
14
- return parse(removeWmicPrefix(stdout), { format: "win" });
15
- }
16
+ parse: (stdout) => parse(removeWmicPrefix(stdout), { format: "win" })
16
17
  },
17
18
  ps: {
18
19
  cmd: "ps",
19
20
  args: ["-eo", "pid,ppid,args"],
20
- parse(stdout) {
21
- return parse(stdout, { format: "unix" });
22
- }
21
+ parse: (stdout) => parse(stdout, { format: "unix" })
23
22
  },
24
23
  pwsh: {
25
24
  cmd: "pwsh",
26
25
  args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
27
26
  parse(stdout) {
28
- let arr = [];
29
27
  try {
30
- arr = JSON.parse(stdout);
28
+ const arr = JSON.parse(stdout);
29
+ return arr.map((p) => ({
30
+ ProcessId: [String(p.ProcessId)],
31
+ ParentProcessId: [String(p.ParentProcessId)],
32
+ CommandLine: p.CommandLine ? [p.CommandLine] : []
33
+ }));
31
34
  } catch {
32
35
  return [];
33
36
  }
34
- return arr.map((p) => ({
35
- ProcessId: [p.ProcessId + ""],
36
- ParentProcessId: [p.ParentProcessId + ""],
37
- CommandLine: p.CommandLine ? [p.CommandLine] : []
38
- }));
39
37
  }
40
38
  }
41
39
  };
42
- var isBin = (f) => {
43
- if (f === "") return false;
44
- if (!f.includes("/") && !f.includes("\\")) return true;
45
- if (f.length > 3 && f[0] === '"')
46
- return f[f.length - 1] === '"' ? isBin(f.slice(1, -1)) : false;
47
- try {
48
- if (!fs.existsSync(f)) return false;
49
- const stat = fs.lstatSync(f);
50
- return stat.isFile() || stat.isSymbolicLink();
51
- } catch {
52
- return false;
53
- }
54
- };
55
- var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
56
- var lookupSync = (query = {}, cb = noop) => _lookup({ query, cb, sync: true });
40
+ var lookup = (query = {}, cb = noop) => runLookup(query, cb, false);
41
+ var lookupSync = (query = {}, cb = noop) => runLookup(query, cb, true);
57
42
  lookup.sync = lookupSync;
58
- var _lookup = ({
59
- query = {},
60
- cb = noop,
61
- sync = false
62
- }) => {
63
- const pFactory = sync ? makePseudoDeferred.bind(null, []) : makeDeferred;
64
- const { promise, resolve, reject } = pFactory();
65
- const result = [];
66
- const lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
67
- const { parse: parse2, cmd, args } = LOOKUPS[lookupFlow];
68
- const callback = (err, { stdout }) => {
43
+ function runLookup(query, cb, sync) {
44
+ const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[LOOKUP_FLOW];
45
+ const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
46
+ let result = [];
47
+ let error;
48
+ const handle = (err, { stdout }) => {
69
49
  if (err) {
70
- reject(err);
71
- cb(err);
50
+ error = err;
72
51
  return;
73
52
  }
74
- result.push(...filterProcessList(normalizeOutput(parse2(stdout)), query));
75
- resolve(result);
76
- cb(null, result);
53
+ result = filterProcessList(normalizeOutput(parseOutput(stdout)), query);
77
54
  };
78
- exec({
79
- cmd,
80
- args,
81
- callback,
82
- sync,
83
- run(cb2) {
84
- cb2();
85
- }
86
- });
87
- return Object.assign(promise, result);
88
- };
89
- var filterProcessList = (processList, query = {}) => {
90
- const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
91
- const filters = [
92
- (p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
93
- (p) => query.arguments ? new RegExp(query.arguments, "i").test(p.arguments.join(" ")) : true,
94
- (p) => query.ppid ? query.ppid + "" === p.ppid : true
95
- ];
96
- return processList.filter(
97
- (p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
98
- );
99
- };
100
- var removeWmicPrefix = (stdout) => {
101
- const s = stdout.indexOf(LOOKUPS.wmic.cmd + os.EOL);
102
- const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(os.EOL) : stdout.length;
103
- return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
104
- };
105
- var pickTree = (list, pid, recursive = false) => {
106
- const children = list.filter((p) => p.ppid === pid + "");
107
- return [
108
- ...children,
109
- ...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
110
- ];
111
- };
112
- var _tree = ({
113
- cb = noop,
114
- opts,
115
- sync = false
116
- }) => {
117
- if (typeof opts === "string" || typeof opts === "number") {
118
- return _tree({ opts: { pid: opts }, cb, sync });
55
+ if (sync) {
56
+ exec({ cmd, args, sync: true, callback: handle, run(c) {
57
+ c();
58
+ } });
59
+ cb(error ?? null, error ? void 0 : result);
60
+ if (error) throw error;
61
+ return result;
119
62
  }
120
- const onError = (err) => cb(err);
121
- const onData = (all) => {
122
- if (opts === void 0) return all;
123
- const { pid, recursive = false } = opts;
124
- const list = pickTree(all, pid, recursive);
63
+ return new Promise((resolve, reject) => {
64
+ exec({
65
+ cmd,
66
+ args,
67
+ sync: false,
68
+ run(c) {
69
+ c();
70
+ },
71
+ callback(err, ctx) {
72
+ handle(err, ctx);
73
+ if (error) {
74
+ cb(error);
75
+ reject(error);
76
+ } else {
77
+ cb(null, result);
78
+ resolve(result);
79
+ }
80
+ }
81
+ });
82
+ });
83
+ }
84
+ var tree = async (opts, cb = noop) => {
85
+ try {
86
+ const list = pickFromTree(await lookup(), opts);
125
87
  cb(null, list);
126
88
  return list;
127
- };
89
+ } catch (err) {
90
+ cb(err);
91
+ throw err;
92
+ }
93
+ };
94
+ var treeSync = (opts, cb = noop) => {
128
95
  try {
129
- const all = _lookup({ sync });
130
- return sync ? onData(all) : all.then(onData, (err) => {
131
- onError(err);
132
- throw err;
133
- });
96
+ const list = pickFromTree(lookupSync(), opts);
97
+ cb(null, list);
98
+ return list;
134
99
  } catch (err) {
135
- onError(err);
136
- return Promise.reject(err);
100
+ cb(err);
101
+ throw err;
137
102
  }
138
103
  };
139
- var tree = async (opts, cb) => _tree({ opts, cb });
140
- var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
141
104
  tree.sync = treeSync;
105
+ var pickFromTree = (all, opts) => {
106
+ if (opts === void 0) return all;
107
+ const { pid, recursive = false } = typeof opts === "object" ? opts : { pid: opts };
108
+ return pickTree(all, pid, recursive);
109
+ };
110
+ var pickTree = (list, pid, recursive = false) => {
111
+ const children = list.filter((p) => p.ppid === String(pid));
112
+ return recursive ? children.flatMap((p) => [p, ...pickTree(list, p.pid, true)]) : children;
113
+ };
142
114
  var kill = (pid, opts, next) => {
143
- if (typeof opts == "function") {
144
- return kill(pid, void 0, opts);
145
- }
146
- if (typeof opts == "string" || typeof opts == "number") {
147
- return kill(pid, { signal: opts }, next);
148
- }
149
- const { promise, resolve, reject } = makeDeferred();
150
- const {
151
- timeout = 30,
152
- signal = "SIGTERM"
153
- } = opts || {};
154
- try {
155
- process.kill(+pid, signal);
156
- } catch (e) {
157
- reject(e);
158
- next?.(e);
159
- return promise;
160
- }
161
- let checkConfident = 0;
162
- let checkTimeoutTimer;
163
- let checkIsTimeout = false;
164
- const checkKilled = (finishCallback) => lookup({ pid }, (err, list = []) => {
165
- if (checkIsTimeout) return;
166
- if (err) {
167
- clearTimeout(checkTimeoutTimer);
168
- reject(err);
169
- finishCallback?.(err, pid);
170
- } else if (list.length > 0) {
171
- checkConfident = checkConfident - 1 || 0;
172
- checkKilled(finishCallback);
173
- } else {
174
- checkConfident++;
175
- if (checkConfident === 5) {
176
- clearTimeout(checkTimeoutTimer);
177
- resolve(pid);
178
- finishCallback?.(null, pid);
179
- } else {
180
- checkKilled(finishCallback);
181
- }
115
+ if (typeof opts === "function") return kill(pid, void 0, opts);
116
+ if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
117
+ const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
118
+ const sPid = String(pid);
119
+ return new Promise((resolve, reject) => {
120
+ let done = false;
121
+ const entry = { pid: sPid, registered: 0, interval, settle: noop };
122
+ const settle = (err) => {
123
+ if (done) return;
124
+ done = true;
125
+ clearTimeout(timer);
126
+ killPending.delete(entry);
127
+ if (err) reject(err);
128
+ else resolve(pid);
129
+ next?.(err ?? null, pid);
130
+ };
131
+ entry.settle = settle;
132
+ const timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
133
+ try {
134
+ process.kill(+pid, signal);
135
+ } catch (e) {
136
+ settle(e);
137
+ return;
182
138
  }
139
+ entry.registered = Date.now();
140
+ killPending.add(entry);
141
+ scheduleKillTick();
142
+ });
143
+ };
144
+ var killPending = /* @__PURE__ */ new Set();
145
+ var killTickTimer = null;
146
+ var killTickRunning = false;
147
+ var scheduleKillTick = (lastStart = 0) => {
148
+ if (killTickTimer || killTickRunning || killPending.size === 0) return;
149
+ let minInterval = Infinity;
150
+ for (const k of killPending) if (k.interval < minInterval) minInterval = k.interval;
151
+ const delay = lastStart === 0 ? 0 : Math.max(0, lastStart + minInterval - Date.now());
152
+ killTickTimer = setTimeout(runKillTick, delay);
153
+ };
154
+ var runKillTick = () => {
155
+ killTickTimer = null;
156
+ if (killPending.size === 0) return;
157
+ killTickRunning = true;
158
+ const startedAt = Date.now();
159
+ lookup().then((list) => {
160
+ const alive = new Set(list.map((p) => p.pid));
161
+ for (const k of killPending) {
162
+ if (k.registered >= startedAt) continue;
163
+ if (!alive.has(k.pid)) k.settle();
164
+ }
165
+ killTickRunning = false;
166
+ scheduleKillTick(startedAt);
167
+ }, (err) => {
168
+ for (const k of killPending) k.settle(err);
169
+ killTickRunning = false;
183
170
  });
184
- if (next) {
185
- checkKilled(next);
186
- checkTimeoutTimer = setTimeout(() => {
187
- checkIsTimeout = true;
188
- next(new Error("Kill process timeout"));
189
- }, timeout * 1e3);
190
- } else {
191
- resolve(pid);
192
- }
193
- return promise;
194
171
  };
195
- var normalizeOutput = (data) => data.reduce((m, d) => {
172
+ var normalizeOutput = (data) => data.flatMap((d) => {
196
173
  const pid = (d.PID || d.ProcessId)?.[0];
197
174
  const ppid = (d.PPID || d.ParentProcessId)?.[0];
198
- const _cmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
199
- const cmd = _cmd.length === 1 ? _cmd[0].split(/\s+/) : _cmd;
200
- if (pid && cmd.length > 0) {
201
- const c = cmd.findIndex((_v, i) => isBin(cmd.slice(0, i).join(" ")));
202
- const command = (c === -1 ? cmd : cmd.slice(0, c)).join(" ");
203
- const args = c === -1 ? [] : cmd.slice(c);
204
- m.push({
205
- pid,
206
- ppid,
207
- command,
208
- arguments: args
209
- });
210
- }
211
- return m;
212
- }, []);
213
- var makeDeferred = () => {
214
- let resolve;
215
- let reject;
216
- const promise = new Promise((res, rej) => {
217
- resolve = res;
218
- reject = rej;
219
- });
220
- return { resolve, reject, promise };
175
+ const rawCmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
176
+ const parts = rawCmd.length === 1 ? rawCmd[0].split(/\s+/) : rawCmd;
177
+ if (!pid || parts.length === 0) return [];
178
+ const binIdx = parts.findIndex((_v, i) => isBin(parts.slice(0, i).join(" ")));
179
+ const command = (binIdx === -1 ? parts : parts.slice(0, binIdx)).join(" ");
180
+ const args = binIdx === -1 ? [] : parts.slice(binIdx);
181
+ return [{ pid, ppid, command, arguments: args }];
182
+ });
183
+ var filterProcessList = (processList, query = {}) => {
184
+ const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map(String);
185
+ const commandRe = query.command ? new RegExp(query.command, "i") : null;
186
+ const argumentsRe = query.arguments ? new RegExp(query.arguments, "i") : null;
187
+ const ppid = query.ppid === void 0 ? null : String(query.ppid);
188
+ return processList.filter(
189
+ (p) => (pidList.length === 0 || pidList.includes(p.pid)) && (!commandRe || commandRe.test(p.command)) && (!argumentsRe || argumentsRe.test(p.arguments.join(" "))) && (!ppid || ppid === p.ppid)
190
+ );
191
+ };
192
+ var removeWmicPrefix = (stdout) => {
193
+ const s = stdout.indexOf(LOOKUPS.wmic.cmd + os.EOL);
194
+ const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(os.EOL) : stdout.length;
195
+ return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
221
196
  };
222
- var makePseudoDeferred = (r = {}) => ({
223
- promise: r,
224
- resolve: identity,
225
- reject(e) {
226
- throw e;
197
+ var isBin = (f) => {
198
+ if (f === "") return false;
199
+ if (!f.includes("/") && !f.includes("\\")) return true;
200
+ if (f.length > 3 && f[0] === '"')
201
+ return f.at(-1) === '"' ? isBin(f.slice(1, -1)) : false;
202
+ try {
203
+ if (!fs.existsSync(f)) return false;
204
+ const stat = fs.lstatSync(f);
205
+ return stat.isFile() || stat.isSymbolicLink();
206
+ } catch {
207
+ return false;
227
208
  }
228
- });
229
- var noop = () => {
230
209
  };
231
- var identity = (v) => v;
232
210
 
233
211
  // src/main/ts/index.ts
234
212
  var index_default = { kill, lookup, lookupSync, tree, treeSync };