@webpod/ps 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpod/ps",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "A process lookup utility",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -26,7 +26,7 @@
26
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
30
  "test:size": "size-limit",
31
31
  "publish:draft": "npm run build && npm publish --no-git-tag-version"
32
32
  },
@@ -65,16 +65,19 @@
65
65
  "esbuild-plugin-entry-chunks": "^0.1.18",
66
66
  "fast-glob": "^3.3.3",
67
67
  "minimist": "^1.2.8",
68
- "mocha": "^11.7.5",
69
- "oxlint": "^1.58.0",
68
+ "mocha": "^12.0.0-beta-9.2",
69
+ "oxlint": "^1.59.0",
70
70
  "size-limit": "^12.0.1",
71
71
  "ts-node": "^10.9.2",
72
72
  "typedoc": "^0.28.18",
73
- "typescript": "^5.9.2"
73
+ "typescript": "^6.0.2"
74
74
  },
75
75
  "repository": {
76
76
  "type": "git",
77
77
  "url": "git://github.com/webpod/ps.git"
78
78
  },
79
- "license": "MIT"
79
+ "license": "MIT",
80
+ "overrides": {
81
+ "chokidar": "^4.0.3"
82
+ }
80
83
  }
@@ -65,8 +65,11 @@ 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",
@@ -95,117 +98,137 @@ var LOOKUPS = {
95
98
  }
96
99
  }
97
100
  };
98
- var lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
99
- var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
100
- 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);
101
103
  lookup.sync = lookupSync;
102
- var _lookup = ({ query = {}, cb = noop, sync = false }) => {
103
- const { promise, resolve, reject } = sync ? makeSyncDeferred([]) : makeDeferred();
104
- const result = [];
105
- const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[lookupFlow];
104
+ function runLookup(query, cb, sync) {
105
+ const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[LOOKUP_FLOW];
106
106
  const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
107
- const callback = (err, { stdout }) => {
107
+ let result = [];
108
+ let error;
109
+ const handle = (err, { stdout }) => {
108
110
  if (err) {
109
- reject(err);
110
- cb(err);
111
+ error = err;
111
112
  return;
112
113
  }
113
- result.push(...filterProcessList(normalizeOutput(parseOutput(stdout)), query));
114
- resolve(result);
115
- cb(null, result);
114
+ result = filterProcessList(normalizeOutput(parseOutput(stdout)), query);
116
115
  };
117
- (0, import_spawn.exec)({ cmd, args, callback, sync, run(cb2) {
118
- cb2();
119
- } });
120
- return Object.assign(promise, result);
121
- };
122
- var tree = (opts, cb) => __async(null, null, function* () {
123
- return _tree({ opts, cb });
124
- });
125
- var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
126
- tree.sync = treeSync;
127
- var _tree = ({ cb = noop, opts, sync = false }) => {
128
- if (typeof opts === "string" || typeof opts === "number") {
129
- 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;
130
123
  }
131
- const onData = (all) => {
132
- var _a;
133
- if (opts === void 0) return all;
134
- const list = pickTree(all, opts.pid, (_a = opts.recursive) != null ? _a : false);
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);
135
148
  cb(null, list);
136
149
  return list;
137
- };
138
- const onError = (err) => {
150
+ } catch (err) {
139
151
  cb(err);
140
152
  throw err;
141
- };
153
+ }
154
+ });
155
+ var treeSync = (opts, cb = noop) => {
142
156
  try {
143
- const all = _lookup({ sync });
144
- return sync ? onData(all) : all.then(onData, onError);
157
+ const list = pickFromTree(lookupSync(), opts);
158
+ cb(null, list);
159
+ return list;
145
160
  } catch (err) {
146
161
  cb(err);
147
- return Promise.reject(err);
162
+ throw err;
148
163
  }
149
164
  };
150
- var inflight = null;
151
- var queued = null;
152
- var sharedSnapshot = (since) => {
153
- var _a;
154
- if (inflight && inflight.startedAt >= since) return inflight.promise;
155
- if (queued) return queued;
156
- const after = (_a = inflight == null ? void 0 : inflight.promise.catch(noop)) != null ? _a : Promise.resolve();
157
- return queued = after.then(() => {
158
- queued = null;
159
- const startedAt = Date.now();
160
- const promise = lookup().then((list) => ({ startedAt, list }));
161
- inflight = { startedAt, promise };
162
- return promise.finally(() => {
163
- inflight = (inflight == null ? void 0 : inflight.promise) === promise ? null : inflight;
164
- });
165
- });
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);
166
170
  };
167
171
  var pickTree = (list, pid, recursive = false) => {
168
172
  const children = list.filter((p) => p.ppid === String(pid));
169
- return [
170
- ...children,
171
- ...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
172
- ];
173
+ return recursive ? children.flatMap((p) => [p, ...pickTree(list, p.pid, true)]) : children;
173
174
  };
174
175
  var kill = (pid, opts, next) => {
175
176
  if (typeof opts === "function") return kill(pid, void 0, opts);
176
177
  if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
177
- const { promise, resolve, reject } = makeDeferred();
178
178
  const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
179
179
  const sPid = String(pid);
180
- let done = false;
181
- const state = {};
182
- const settle = (err) => {
183
- if (done) return;
184
- done = true;
185
- clearTimeout(state.timer);
186
- if (err) reject(err);
187
- else resolve(pid);
188
- next == null ? void 0 : next(err != null ? err : null, pid);
189
- };
190
- try {
191
- import_node_process.default.kill(+pid, signal);
192
- } catch (e) {
193
- settle(e);
194
- return promise;
195
- }
196
- let since = Date.now();
197
- state.timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
198
- const poll = () => sharedSnapshot(since).then(({ startedAt, list }) => {
199
- if (done) return;
200
- since = startedAt + 1;
201
- if (list.some((p) => p.pid === sPid)) {
202
- setTimeout(poll, Math.max(0, startedAt + interval - Date.now()));
203
- } else {
204
- settle();
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;
199
+ }
200
+ entry.registered = Date.now();
201
+ killPending.add(entry);
202
+ scheduleKillTick();
203
+ });
204
+ };
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();
205
225
  }
206
- }, settle);
207
- poll();
208
- return promise;
226
+ killTickRunning = false;
227
+ scheduleKillTick(startedAt);
228
+ }, (err) => {
229
+ for (const k of killPending) k.settle(err);
230
+ killTickRunning = false;
231
+ });
209
232
  };
210
233
  var normalizeOutput = (data) => data.flatMap((d) => {
211
234
  var _a, _b;
@@ -246,25 +269,6 @@ var isBin = (f) => {
246
269
  return false;
247
270
  }
248
271
  };
249
- var makeDeferred = () => {
250
- let resolve;
251
- let reject;
252
- const promise = new Promise((res, rej) => {
253
- resolve = res;
254
- reject = rej;
255
- });
256
- return { resolve, reject, promise };
257
- };
258
- var makeSyncDeferred = (result) => ({
259
- promise: result,
260
- resolve: () => {
261
- },
262
- reject(e) {
263
- throw e;
264
- }
265
- });
266
- var noop = () => {
267
- };
268
272
 
269
273
  // src/main/ts/index.ts
270
274
  var index_default = { kill, lookup, lookupSync, tree, treeSync };
@@ -4,8 +4,11 @@ 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",
@@ -34,113 +37,137 @@ var LOOKUPS = {
34
37
  }
35
38
  }
36
39
  };
37
- var lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
38
- var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
39
- 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);
40
42
  lookup.sync = lookupSync;
41
- var _lookup = ({ query = {}, cb = noop, sync = false }) => {
42
- const { promise, resolve, reject } = sync ? makeSyncDeferred([]) : makeDeferred();
43
- const result = [];
44
- const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[lookupFlow];
43
+ function runLookup(query, cb, sync) {
44
+ const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[LOOKUP_FLOW];
45
45
  const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
46
- const callback = (err, { stdout }) => {
46
+ let result = [];
47
+ let error;
48
+ const handle = (err, { stdout }) => {
47
49
  if (err) {
48
- reject(err);
49
- cb(err);
50
+ error = err;
50
51
  return;
51
52
  }
52
- result.push(...filterProcessList(normalizeOutput(parseOutput(stdout)), query));
53
- resolve(result);
54
- cb(null, result);
53
+ result = filterProcessList(normalizeOutput(parseOutput(stdout)), query);
55
54
  };
56
- exec({ cmd, args, callback, sync, run(cb2) {
57
- cb2();
58
- } });
59
- return Object.assign(promise, result);
60
- };
61
- var tree = async (opts, cb) => _tree({ opts, cb });
62
- var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
63
- tree.sync = treeSync;
64
- var _tree = ({ cb = noop, opts, sync = false }) => {
65
- if (typeof opts === "string" || typeof opts === "number") {
66
- 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;
67
62
  }
68
- const onData = (all) => {
69
- if (opts === void 0) return all;
70
- const list = pickTree(all, opts.pid, opts.recursive ?? false);
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);
71
87
  cb(null, list);
72
88
  return list;
73
- };
74
- const onError = (err) => {
89
+ } catch (err) {
75
90
  cb(err);
76
91
  throw err;
77
- };
92
+ }
93
+ };
94
+ var treeSync = (opts, cb = noop) => {
78
95
  try {
79
- const all = _lookup({ sync });
80
- return sync ? onData(all) : all.then(onData, onError);
96
+ const list = pickFromTree(lookupSync(), opts);
97
+ cb(null, list);
98
+ return list;
81
99
  } catch (err) {
82
100
  cb(err);
83
- return Promise.reject(err);
101
+ throw err;
84
102
  }
85
103
  };
86
- var inflight = null;
87
- var queued = null;
88
- var sharedSnapshot = (since) => {
89
- if (inflight && inflight.startedAt >= since) return inflight.promise;
90
- if (queued) return queued;
91
- const after = inflight?.promise.catch(noop) ?? Promise.resolve();
92
- return queued = after.then(() => {
93
- queued = null;
94
- const startedAt = Date.now();
95
- const promise = lookup().then((list) => ({ startedAt, list }));
96
- inflight = { startedAt, promise };
97
- return promise.finally(() => {
98
- inflight = inflight?.promise === promise ? null : inflight;
99
- });
100
- });
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);
101
109
  };
102
110
  var pickTree = (list, pid, recursive = false) => {
103
111
  const children = list.filter((p) => p.ppid === String(pid));
104
- return [
105
- ...children,
106
- ...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
107
- ];
112
+ return recursive ? children.flatMap((p) => [p, ...pickTree(list, p.pid, true)]) : children;
108
113
  };
109
114
  var kill = (pid, opts, next) => {
110
115
  if (typeof opts === "function") return kill(pid, void 0, opts);
111
116
  if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
112
- const { promise, resolve, reject } = makeDeferred();
113
117
  const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
114
118
  const sPid = String(pid);
115
- let done = false;
116
- const state = {};
117
- const settle = (err) => {
118
- if (done) return;
119
- done = true;
120
- clearTimeout(state.timer);
121
- if (err) reject(err);
122
- else resolve(pid);
123
- next?.(err ?? null, pid);
124
- };
125
- try {
126
- process.kill(+pid, signal);
127
- } catch (e) {
128
- settle(e);
129
- return promise;
130
- }
131
- let since = Date.now();
132
- state.timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
133
- const poll = () => sharedSnapshot(since).then(({ startedAt, list }) => {
134
- if (done) return;
135
- since = startedAt + 1;
136
- if (list.some((p) => p.pid === sPid)) {
137
- setTimeout(poll, Math.max(0, startedAt + interval - Date.now()));
138
- } else {
139
- settle();
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;
140
138
  }
141
- }, settle);
142
- poll();
143
- return promise;
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;
170
+ });
144
171
  };
145
172
  var normalizeOutput = (data) => data.flatMap((d) => {
146
173
  const pid = (d.PID || d.ProcessId)?.[0];
@@ -180,25 +207,6 @@ var isBin = (f) => {
180
207
  return false;
181
208
  }
182
209
  };
183
- var makeDeferred = () => {
184
- let resolve;
185
- let reject;
186
- const promise = new Promise((res, rej) => {
187
- resolve = res;
188
- reject = rej;
189
- });
190
- return { resolve, reject, promise };
191
- };
192
- var makeSyncDeferred = (result) => ({
193
- promise: result,
194
- resolve: () => {
195
- },
196
- reject(e) {
197
- throw e;
198
- }
199
- });
200
- var noop = () => {
201
- };
202
210
 
203
211
  // src/main/ts/index.ts
204
212
  var index_default = { kill, lookup, lookupSync, tree, treeSync };