@lvce-editor/process-explorer 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Lvce Editor
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.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Lvce Editor Process Explorer
2
+
3
+ Process Explorer.
package/dist/index.js ADDED
@@ -0,0 +1,873 @@
1
+ import { IpcChildWithWebSocket, IpcChildWithElectronMessagePort, IpcChildWithElectronUtilityProcess, IpcChildWithNodeWorker, IpcChildWithNodeForkedProcess } from '@lvce-editor/ipc';
2
+ import { object, number as number$1, string } from '@lvce-editor/assert';
3
+ import { VError } from '@lvce-editor/verror';
4
+ import { readFile } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { execFile as execFile$1 } from 'node:child_process';
7
+ import { promisify } from 'node:util';
8
+
9
+ const Two = '2.0';
10
+
11
+ class AssertionError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = 'AssertionError';
15
+ }
16
+ }
17
+ const getType = value => {
18
+ switch (typeof value) {
19
+ case 'number':
20
+ return 'number';
21
+ case 'function':
22
+ return 'function';
23
+ case 'string':
24
+ return 'string';
25
+ case 'object':
26
+ if (value === null) {
27
+ return 'null';
28
+ }
29
+ if (Array.isArray(value)) {
30
+ return 'array';
31
+ }
32
+ return 'object';
33
+ case 'boolean':
34
+ return 'boolean';
35
+ default:
36
+ return 'unknown';
37
+ }
38
+ };
39
+ const number = value => {
40
+ const type = getType(value);
41
+ if (type !== 'number') {
42
+ throw new AssertionError('expected value to be of type number');
43
+ }
44
+ };
45
+
46
+ const state$1$1 = {
47
+ callbacks: Object.create(null)
48
+ };
49
+ const set = (id, fn) => {
50
+ state$1$1.callbacks[id] = fn;
51
+ };
52
+ const get = id => {
53
+ return state$1$1.callbacks[id];
54
+ };
55
+ const remove = id => {
56
+ delete state$1$1.callbacks[id];
57
+ };
58
+
59
+ const state$2 = {
60
+ id: 0
61
+ };
62
+ const create$3 = () => {
63
+ return ++state$2.id;
64
+ };
65
+
66
+ const warn = (...args) => {
67
+ console.warn(...args);
68
+ };
69
+
70
+ const withResolvers$1 = () => {
71
+ /**
72
+ * @type {any}
73
+ */
74
+ let _resolve;
75
+ const promise = new Promise(resolve => {
76
+ _resolve = resolve;
77
+ });
78
+ return {
79
+ resolve: _resolve,
80
+ promise
81
+ };
82
+ };
83
+
84
+ const registerPromise = () => {
85
+ const id = create$3();
86
+ const {
87
+ resolve,
88
+ promise
89
+ } = withResolvers$1();
90
+ set(id, resolve);
91
+ return {
92
+ id,
93
+ promise
94
+ };
95
+ };
96
+ const resolve = (id, args) => {
97
+ number(id);
98
+ const fn = get(id);
99
+ if (!fn) {
100
+ console.log(args);
101
+ warn(`callback ${id} may already be disposed`);
102
+ return;
103
+ }
104
+ fn(args);
105
+ remove(id);
106
+ };
107
+
108
+ const create$2 = (method, params) => {
109
+ const {
110
+ id,
111
+ promise
112
+ } = registerPromise();
113
+ const message = {
114
+ jsonrpc: Two,
115
+ method,
116
+ params,
117
+ id
118
+ };
119
+ return {
120
+ message,
121
+ promise
122
+ };
123
+ };
124
+
125
+ class JsonRpcError extends Error {
126
+ constructor(message) {
127
+ super(message);
128
+ this.name = 'JsonRpcError';
129
+ }
130
+ }
131
+
132
+ const NewLine$1 = '\n';
133
+
134
+ const DomException = 'DOMException';
135
+ const ReferenceError$1 = 'ReferenceError';
136
+ const SyntaxError$1 = 'SyntaxError';
137
+ const TypeError$1 = 'TypeError';
138
+
139
+ const getErrorConstructor = (message, type) => {
140
+ if (type) {
141
+ switch (type) {
142
+ case DomException:
143
+ // @ts-ignore
144
+ return DOMException;
145
+ case TypeError$1:
146
+ return TypeError;
147
+ case SyntaxError$1:
148
+ return SyntaxError;
149
+ case ReferenceError$1:
150
+ return ReferenceError;
151
+ default:
152
+ return Error;
153
+ }
154
+ }
155
+ if (message.startsWith('TypeError: ')) {
156
+ return TypeError;
157
+ }
158
+ if (message.startsWith('SyntaxError: ')) {
159
+ return SyntaxError;
160
+ }
161
+ if (message.startsWith('ReferenceError: ')) {
162
+ return ReferenceError;
163
+ }
164
+ return Error;
165
+ };
166
+
167
+ const constructError = (message, type, name) => {
168
+ const ErrorConstructor = getErrorConstructor(message, type);
169
+ // @ts-ignore
170
+ if (ErrorConstructor === DOMException && name) {
171
+ return new ErrorConstructor(message, name);
172
+ }
173
+ if (ErrorConstructor === Error) {
174
+ const error = new Error(message);
175
+ if (name && name !== 'VError') {
176
+ error.name = name;
177
+ }
178
+ return error;
179
+ }
180
+ return new ErrorConstructor(message);
181
+ };
182
+
183
+ const getNewLineIndex = (string, startIndex = undefined) => {
184
+ return string.indexOf(NewLine$1, startIndex);
185
+ };
186
+
187
+ const joinLines = lines => {
188
+ return lines.join(NewLine$1);
189
+ };
190
+
191
+ const MethodNotFound = -32601;
192
+ const Custom = -32001;
193
+
194
+ const splitLines$1 = lines => {
195
+ return lines.split(NewLine$1);
196
+ };
197
+
198
+ const getParentStack = error => {
199
+ let parentStack = error.stack || error.data || error.message || '';
200
+ if (parentStack.startsWith(' at')) {
201
+ parentStack = error.message + NewLine$1 + parentStack;
202
+ }
203
+ return parentStack;
204
+ };
205
+ const restoreJsonRpcError = error => {
206
+ if (error && error instanceof Error) {
207
+ return error;
208
+ }
209
+ const currentStack = joinLines(splitLines$1(new Error().stack).slice(1));
210
+ if (error && error.code && error.code === MethodNotFound) {
211
+ const restoredError = new JsonRpcError(error.message);
212
+ const parentStack = getParentStack(error);
213
+ restoredError.stack = parentStack + NewLine$1 + currentStack;
214
+ return restoredError;
215
+ }
216
+ if (error && error.message) {
217
+ const restoredError = constructError(error.message, error.type, error.name);
218
+ if (error.data) {
219
+ if (error.data.stack && error.data.type && error.message) {
220
+ restoredError.stack = error.data.type + ': ' + error.message + NewLine$1 + error.data.stack + NewLine$1 + currentStack;
221
+ } else if (error.data.stack) {
222
+ restoredError.stack = error.data.stack;
223
+ }
224
+ if (error.data.codeFrame) {
225
+ // @ts-ignore
226
+ restoredError.codeFrame = error.data.codeFrame;
227
+ }
228
+ if (error.data.code) {
229
+ // @ts-ignore
230
+ restoredError.code = error.data.code;
231
+ }
232
+ if (error.data.type) {
233
+ // @ts-ignore
234
+ restoredError.name = error.data.type;
235
+ }
236
+ } else {
237
+ if (error.stack) {
238
+ // TODO accessing stack might be slow
239
+ const lowerStack = restoredError.stack || '';
240
+ // @ts-ignore
241
+ const indexNewLine = getNewLineIndex(lowerStack);
242
+ const parentStack = getParentStack(error);
243
+ // @ts-ignore
244
+ restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
245
+ }
246
+ if (error.codeFrame) {
247
+ // @ts-ignore
248
+ restoredError.codeFrame = error.codeFrame;
249
+ }
250
+ }
251
+ return restoredError;
252
+ }
253
+ if (typeof error === 'string') {
254
+ return new Error(`JsonRpc Error: ${error}`);
255
+ }
256
+ return new Error(`JsonRpc Error: ${error}`);
257
+ };
258
+
259
+ const unwrapJsonRpcResult = responseMessage => {
260
+ if ('error' in responseMessage) {
261
+ const restoredError = restoreJsonRpcError(responseMessage.error);
262
+ throw restoredError;
263
+ }
264
+ if ('result' in responseMessage) {
265
+ return responseMessage.result;
266
+ }
267
+ throw new JsonRpcError('unexpected response message');
268
+ };
269
+
270
+ const create$1 = (message, error) => {
271
+ return {
272
+ jsonrpc: Two,
273
+ id: message.id,
274
+ error
275
+ };
276
+ };
277
+
278
+ const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
279
+
280
+ const getErrorProperty = (error, prettyError) => {
281
+ if (error && error.code === E_COMMAND_NOT_FOUND) {
282
+ return {
283
+ code: MethodNotFound,
284
+ message: error.message,
285
+ data: error.stack
286
+ };
287
+ }
288
+ return {
289
+ code: Custom,
290
+ message: prettyError.message,
291
+ data: {
292
+ stack: prettyError.stack,
293
+ codeFrame: prettyError.codeFrame,
294
+ type: prettyError.type,
295
+ code: prettyError.code
296
+ }
297
+ };
298
+ };
299
+ const getErrorResponse = (message, error, ipc, preparePrettyError, logError) => {
300
+ const prettyError = preparePrettyError(error);
301
+ logError(error, prettyError);
302
+ const errorProperty = getErrorProperty(error, prettyError);
303
+ return create$1(message, errorProperty);
304
+ };
305
+
306
+ const create = (message, result) => {
307
+ return {
308
+ jsonrpc: Two,
309
+ id: message.id,
310
+ result: result ?? null
311
+ };
312
+ };
313
+
314
+ const getSuccessResponse = (message, result) => {
315
+ const resultProperty = result ?? null;
316
+ return create(message, resultProperty);
317
+ };
318
+
319
+ const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
320
+ try {
321
+ const result = requiresSocket(message.method) ? await execute(message.method, ipc, ...message.params) : await execute(message.method, ...message.params);
322
+ return getSuccessResponse(message, result);
323
+ } catch (error) {
324
+ return getErrorResponse(message, error, ipc, preparePrettyError, logError);
325
+ }
326
+ };
327
+
328
+ const handleJsonRpcMessage = async (ipc, message, execute, resolve, preparePrettyError, logError, requiresSocket) => {
329
+ if ('id' in message) {
330
+ if ('method' in message) {
331
+ const response = await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
332
+ try {
333
+ ipc.send(response);
334
+ } catch (error) {
335
+ const errorResponse = getErrorResponse(message, error, ipc, preparePrettyError, logError);
336
+ ipc.send(errorResponse);
337
+ }
338
+ return;
339
+ }
340
+ resolve(message.id, message);
341
+ return;
342
+ }
343
+ if ('method' in message) {
344
+ await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
345
+ return;
346
+ }
347
+ throw new JsonRpcError('unexpected message');
348
+ };
349
+ const invoke$1 = async (ipc, method, ...params) => {
350
+ const {
351
+ message,
352
+ promise
353
+ } = create$2(method, params);
354
+ ipc.send(message);
355
+ const responseMessage = await promise;
356
+ const result = unwrapJsonRpcResult(responseMessage);
357
+ return result;
358
+ };
359
+
360
+ const state$1 = {
361
+ commands: Object.create(null),
362
+ };
363
+
364
+ const registerCommand = (key, fn) => {
365
+ state$1.commands[key] = fn;
366
+ };
367
+
368
+ const registerCommands = (commandMap) => {
369
+ for (const [key, value] of Object.entries(commandMap)) {
370
+ registerCommand(key, value);
371
+ }
372
+ };
373
+
374
+ const getCommand = (key) => {
375
+ return state$1.commands[key]
376
+ };
377
+
378
+ const execute = (command, ...args) => {
379
+ const fn = getCommand(command);
380
+ if (!fn) {
381
+ throw new Error(`Command not found ${command}`)
382
+ }
383
+ return fn(...args)
384
+ };
385
+
386
+ const preparePrettyError = (error) => {
387
+ return error
388
+ };
389
+
390
+ const logError = (error) => {
391
+ console.error(error);
392
+ };
393
+
394
+ const requiresSocket = () => {
395
+ return false
396
+ };
397
+
398
+ const handleMessage = (event) => {
399
+ return handleJsonRpcMessage(event.target, event.data, execute, resolve, preparePrettyError, logError, requiresSocket)
400
+ };
401
+
402
+ const handleIpc = (ipc) => {
403
+ if ('addEventListener' in ipc) {
404
+ ipc.addEventListener('message', handleMessage);
405
+ } else {
406
+ // deprecated
407
+ ipc.on('message', handleMessage);
408
+ }
409
+ };
410
+
411
+ const NodeWorker = 1;
412
+ const NodeForkedProcess = 2;
413
+ const ElectronUtilityProcess = 3;
414
+ const ElectronMessagePort = 4;
415
+ const WebSocket = 6;
416
+
417
+ const Auto = () => {
418
+ const { argv } = process;
419
+ if (argv.includes('--ipc-type=node-worker')) {
420
+ return NodeWorker
421
+ }
422
+ if (argv.includes('--ipc-type=node-forked-process')) {
423
+ return NodeForkedProcess
424
+ }
425
+ if (argv.includes('--ipc-type=electron-utility-process')) {
426
+ return ElectronUtilityProcess
427
+ }
428
+ throw new Error(`[shared-process] unknown ipc type`)
429
+ };
430
+
431
+ const getModule$1 = (method) => {
432
+ switch (method) {
433
+ case NodeForkedProcess:
434
+ return IpcChildWithNodeForkedProcess
435
+ case NodeWorker:
436
+ return IpcChildWithNodeWorker
437
+ case ElectronUtilityProcess:
438
+ return IpcChildWithElectronUtilityProcess
439
+ case ElectronMessagePort:
440
+ return IpcChildWithElectronMessagePort
441
+ case WebSocket:
442
+ return IpcChildWithWebSocket
443
+ default:
444
+ throw new Error('unexpected ipc type')
445
+ }
446
+ };
447
+
448
+ const listen$1 = async ({ method, ...params }) => {
449
+ const module = await getModule$1(method);
450
+ // @ts-ignore
451
+ const rawIpc = await module.listen(params);
452
+ // @ts-ignore
453
+ if (module.signal) {
454
+ // @ts-ignore
455
+ module.signal(rawIpc);
456
+ }
457
+ // @ts-ignore
458
+ const ipc = module.wrap(rawIpc);
459
+ return ipc
460
+ };
461
+
462
+ const listen = async () => {
463
+ const ipc = await listen$1({ method: Auto() });
464
+ handleIpc(ipc);
465
+ };
466
+
467
+ const MainProcess = -5;
468
+
469
+ const state = {
470
+ /**
471
+ * @type {any}
472
+ */
473
+ ipc: undefined,
474
+ };
475
+
476
+ const invoke = (method, ...params) => {
477
+ return invoke$1(state.ipc, method, ...params)
478
+ };
479
+
480
+ const handleElectronMessagePort = async (messagePort, ipcId) => {
481
+ object(messagePort);
482
+ // Assert.number(ipcId)
483
+ const ipc = await listen$1({
484
+ method: ElectronMessagePort,
485
+ messagePort,
486
+ });
487
+ handleIpc(ipc);
488
+ if (ipcId === MainProcess) {
489
+ state.ipc = ipc;
490
+ }
491
+ };
492
+
493
+ const getMainProcessId = () => {
494
+ return process.ppid
495
+ };
496
+
497
+ const isWindows = process.platform === 'win32';
498
+
499
+ const getModule = () => {
500
+ if (isWindows) {
501
+ return Promise.resolve().then(function () { return ListProcessesWithMemoryUsageWindows; })
502
+ }
503
+ return Promise.resolve().then(function () { return ListProcessesWithMemoryUsageUnix; })
504
+ };
505
+
506
+ const listProcessesWithMemoryUsage$2 = async (rootPid) => {
507
+ const module = await getModule();
508
+ return module.listProcessesWithMemoryUsage(rootPid)
509
+ };
510
+
511
+ const commandMap = {
512
+ 'HandleElectronMessagePort.handleElectronMessagePort': handleElectronMessagePort,
513
+ 'ProcessId.getMainProcessId': getMainProcessId,
514
+ 'ListProcessesWithMemoryUsage.listProcessesWithMemoryUsage': listProcessesWithMemoryUsage$2,
515
+ // 'ElectronContextMenu.openContextMenu': ElectronWebContentsView.handleContextMenu,
516
+ };
517
+
518
+ const main = async () => {
519
+ registerCommands(commandMap);
520
+ await listen();
521
+ };
522
+
523
+ main();
524
+
525
+ const getName = (pid, cmd, rootPid, pidMap) => {
526
+ number$1(pid);
527
+ string(cmd);
528
+ number$1(rootPid);
529
+ object(pidMap);
530
+ if (pid === rootPid) {
531
+ return 'main'
532
+ }
533
+ if (cmd.includes('--type=zygote')) {
534
+ return 'zygote'
535
+ }
536
+ if (cmd.includes('--type=gpu-process')) {
537
+ return 'gpu-process'
538
+ }
539
+ if (cmd.includes('extensionHostMain.js')) {
540
+ return 'extension-host'
541
+ }
542
+ if (cmd.includes('ptyHostMain.js')) {
543
+ return 'pty-host'
544
+ }
545
+ if (cmd.includes('--lvce-window-kind=process-explorer')) {
546
+ return 'process-explorer'
547
+ }
548
+ if (pid in pidMap) {
549
+ return pidMap[pid] || `<unknown>`
550
+ }
551
+ if (cmd.includes('--type=renderer')) {
552
+ return `renderer`
553
+ }
554
+ if (cmd.includes('--type=utility')) {
555
+ return 'utility'
556
+ }
557
+ if (cmd.includes('tsserver.js')) {
558
+ return 'tsserver.js'
559
+ }
560
+ if (cmd.includes('typingsInstaller.js')) {
561
+ return 'typingsInstaller.js'
562
+ }
563
+ if (cmd.includes('extensionHostHelperProcessMain.js')) {
564
+ return 'extension-host-helper-process'
565
+ }
566
+ if (cmd.includes('/bin/rg') || cmd.includes('rg.exe')) {
567
+ return 'ripgrep'
568
+ }
569
+ if (cmd.startsWith('bash')) {
570
+ return 'bash'
571
+ }
572
+ if (cmd.startsWith(`/opt/sublime_text/sublime_text `)) {
573
+ return 'sublime-text'
574
+ }
575
+ if (cmd.includes('\\conhost.exe')) {
576
+ return 'conhost.exe'
577
+ }
578
+ return `${cmd}`
579
+ };
580
+
581
+ const ENOENT = 'ENOENT';
582
+ const ERR_DLOPEN_FAILED = 'ERR_DLOPEN_FAILED';
583
+ const ESRCH = 'ESRCH';
584
+
585
+ const isDlOpenError = (error) => {
586
+ return error && error instanceof Error && 'code' in error && error.code === ERR_DLOPEN_FAILED
587
+ };
588
+
589
+ const loadWindowProcessTree = async () => {
590
+ try {
591
+ return await import('@vscode/windows-process-tree')
592
+ } catch (error) {
593
+ if (isDlOpenError(error)) {
594
+ throw new VError(
595
+ `Failed to load windows process tree: The native module "@vscode/windows-process-tree" is not compatible with this node version and must be compiled against a matching electron version using electron-rebuild`,
596
+ )
597
+ }
598
+ throw new VError(error, `Failed to load windows process tree`)
599
+ }
600
+ };
601
+
602
+ const withResolvers = () => {
603
+ /**
604
+ * @type {any}
605
+ */
606
+ let _resolve;
607
+ /**
608
+ * @type {any}
609
+ */
610
+ let _reject;
611
+ const promise = new Promise((resolve, reject) => {
612
+ _resolve = resolve;
613
+ _reject = reject;
614
+ });
615
+ return {
616
+ resolve: _resolve,
617
+ reject: _reject,
618
+ promise,
619
+ }
620
+ };
621
+
622
+ /**
623
+ *
624
+ * @param {number} rootPid
625
+ * @param {number} flags
626
+ * @returns {Promise<any[] | undefined>}
627
+ */
628
+ const getProcessList = async (rootPid, flags) => {
629
+ const WindowsProcessTree = await loadWindowProcessTree();
630
+ const { resolve, promise } = withResolvers();
631
+ WindowsProcessTree.getProcessList(rootPid, resolve, flags);
632
+ return promise
633
+ };
634
+
635
+ /**
636
+ *
637
+ * @param {any[]} processList
638
+ * @returns Promise< WindowsProcessTree.IProcessCpuInfo[]>
639
+ */
640
+ const addCpuUsage = async (processList) => {
641
+ const WindowsProcessTree = await loadWindowProcessTree();
642
+ const { resolve, promise } = withResolvers();
643
+ WindowsProcessTree.getProcessCpuUsage(processList, resolve);
644
+ return promise
645
+ };
646
+
647
+ const Memory = 1;
648
+ const CommandLine = 2;
649
+
650
+ const createPidMap = async () => {
651
+ return invoke('CreatePidMap.createPidMap')
652
+ };
653
+
654
+ // listProcesses windows implementation based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/node/ps.ts (License MIT)
655
+
656
+
657
+ /**
658
+ * @param {import('@vscode/windows-process-tree').IProcessCpuInfo} item
659
+ * @param {number} rootPid
660
+ * @param {object} pidMap
661
+ */
662
+ const toResultItem = (item, rootPid, pidMap) => {
663
+ return {
664
+ name: getName(item.pid, item.commandLine, rootPid, pidMap),
665
+ pid: item.pid,
666
+ ppid: item.ppid,
667
+ memory: item.memory,
668
+ cmd: item.commandLine,
669
+ }
670
+ };
671
+
672
+ /**
673
+ *
674
+ * @param {import('@vscode/windows-process-tree').IProcessCpuInfo[]} completeProcessList
675
+ * @param {number} rootPid
676
+ */
677
+ const toResult = (completeProcessList, rootPid, pidMap) => {
678
+ const results = [];
679
+ for (const item of completeProcessList) {
680
+ results.push(toResultItem(item, rootPid, pidMap));
681
+ }
682
+ return results
683
+ };
684
+
685
+ const listProcessesWithMemoryUsage$1 = async (rootPid) => {
686
+ try {
687
+ const processList = await getProcessList(rootPid, CommandLine | Memory);
688
+ if (!processList) {
689
+ throw new VError(`Root process ${rootPid} not found`)
690
+ }
691
+ const pidMap = await createPidMap();
692
+ const completeProcessList = await addCpuUsage(processList);
693
+ const result = toResult(completeProcessList, rootPid, pidMap);
694
+ return result
695
+ } catch (error) {
696
+ // @ts-ignore
697
+ throw new VError(error, `Failed to list processes`)
698
+ }
699
+ };
700
+
701
+ const ListProcessesWithMemoryUsageWindows = {
702
+ __proto__: null,
703
+ listProcessesWithMemoryUsage: listProcessesWithMemoryUsage$1
704
+ };
705
+
706
+ const Utf8 = 'utf8';
707
+
708
+ const isEnoentErrorWindows = (error) => {
709
+ return error && error.message && error.message.includes('The system cannot find the path specified.')
710
+ };
711
+
712
+ const isEnoentErrorLinux = (error) => {
713
+ return error.code === ENOENT
714
+ };
715
+
716
+ const isEnoentError = (error) => {
717
+ if (!error) {
718
+ return false
719
+ }
720
+ return isEnoentErrorLinux(error) || isEnoentErrorWindows(error)
721
+ };
722
+
723
+ const isEsrchError = (error) => {
724
+ return error && error.code === ESRCH
725
+ };
726
+
727
+ const isMacOs = process.platform === 'darwin';
728
+
729
+ const EmptyString = '';
730
+ const NewLine = '\n';
731
+ const Space = ' ';
732
+
733
+ const parseMemory = (content) => {
734
+ const trimmedContent = content.trim();
735
+ const numberBlocks = trimmedContent.split(Space);
736
+ const pageSize = 4096;
737
+ const rss = Number.parseInt(numberBlocks[1]) * pageSize;
738
+ const shared = Number.parseInt(numberBlocks[2]) * pageSize;
739
+ const memory = rss - shared;
740
+ return memory
741
+ };
742
+
743
+ const getContent = async (pid) => {
744
+ try {
745
+ const filePath = join('/proc', `${pid}`, 'statm');
746
+ const content = await readFile(filePath, Utf8);
747
+ return content
748
+ } catch (error) {
749
+ if (isEnoentError(error) || isEsrchError(error)) {
750
+ return ''
751
+ }
752
+ throw error
753
+ }
754
+ };
755
+
756
+ const getAccurateMemoryUsage = async (pid) => {
757
+ try {
758
+ number$1(pid);
759
+ if (isMacOs) {
760
+ return 0
761
+ }
762
+ const content = await getContent(pid);
763
+ if (!content) {
764
+ return -1
765
+ }
766
+ const memory = parseMemory(content);
767
+ return memory
768
+ } catch (error) {
769
+ throw new VError(error, 'Failed to get accurate memory usage')
770
+ }
771
+ };
772
+
773
+ const addAccurateMemoryUsage = async (process) => {
774
+ const accurateMemoryUsage = await getAccurateMemoryUsage(process.pid);
775
+ return {
776
+ ...process,
777
+ memory: accurateMemoryUsage,
778
+ }
779
+ };
780
+
781
+ const SIGINT = 'SIGINT';
782
+
783
+ const execFile = promisify(execFile$1);
784
+
785
+ const getPsOutput = async () => {
786
+ try {
787
+ const { stdout } = await execFile('ps', ['-ax', '-o', 'pid=,ppid=,pcpu=,pmem=,command=']);
788
+ return stdout.trim()
789
+ } catch (error) {
790
+ // @ts-ignore
791
+ if (error && error.signal === SIGINT) {
792
+ return ''
793
+ }
794
+ throw new VError(error, `Failed to execute ps`)
795
+ }
796
+ };
797
+
798
+ const hasPositiveMemoryUsage = (process) => {
799
+ return process.memory >= 0
800
+ };
801
+
802
+ const splitLines = (lines) => {
803
+ return lines.split(NewLine)
804
+ };
805
+
806
+ // parse ps output based on vscode https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/node/ps.ts (License MIT)
807
+
808
+
809
+ const PID_CMD = /^\s*(\d+)\s+(\d+)\s+([\d.]+)\s+([\d.]+)\s+(.+)$/s;
810
+
811
+ const parsePsOutputLine = (line) => {
812
+ string(line);
813
+ const matches = PID_CMD.exec(line.trim());
814
+ if (matches && matches.length === 6) {
815
+ return {
816
+ pid: Number.parseInt(matches[1]),
817
+ ppid: Number.parseInt(matches[2]),
818
+ cmd: matches[5],
819
+ // load: parseInt(matches[3]),
820
+ // mem: parseInt(matches[4]),
821
+ }
822
+ }
823
+ throw new Error(`line could not be parsed: ${line}`)
824
+ };
825
+
826
+ const parsePsOutput = (stdout, rootPid, pidMap) => {
827
+ string(stdout);
828
+ number$1(rootPid);
829
+ object(pidMap);
830
+ if (stdout === EmptyString) {
831
+ return []
832
+ }
833
+ const lines = splitLines(stdout);
834
+ const result = [];
835
+ const depthMap = Object.create(null);
836
+ depthMap[rootPid] = 1;
837
+ const parsedLines = lines.map(parsePsOutputLine);
838
+ for (const parsedLine of parsedLines) {
839
+ const { pid, ppid, cmd } = parsedLine;
840
+ const depth = pid === rootPid ? 1 : depthMap[ppid];
841
+ if (!depth) {
842
+ continue
843
+ }
844
+ result.push({
845
+ ...parsedLine,
846
+ depth,
847
+ name: getName(pid, cmd, rootPid, pidMap),
848
+ });
849
+ depthMap[pid] = depth + 1;
850
+ }
851
+ return result
852
+ };
853
+
854
+ const listProcessesWithMemoryUsage = async (rootPid) => {
855
+ // console.time('getPsOutput')
856
+ const stdout = await getPsOutput();
857
+ const pidMap = await createPidMap();
858
+ // console.log({ stdout })
859
+ // console.timeEnd('getPsOutput')
860
+ // console.time('parsePsOutput')
861
+ const parsed = parsePsOutput(stdout, rootPid, pidMap);
862
+ // console.timeEnd('parsePsOutput')
863
+ // console.time('addAccurateMemoryUsage')
864
+ const parsedWithAccurateMemoryUsage = await Promise.all(parsed.map(addAccurateMemoryUsage));
865
+ // console.timeEnd('addAccurateMemoryUsage')
866
+ const filtered = parsedWithAccurateMemoryUsage.filter(hasPositiveMemoryUsage);
867
+ return filtered
868
+ };
869
+
870
+ const ListProcessesWithMemoryUsageUnix = {
871
+ __proto__: null,
872
+ listProcessesWithMemoryUsage
873
+ };
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@lvce-editor/process-explorer",
3
+ "version": "1.0.0",
4
+ "description": "Process Explorer",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "keywords": [
8
+ "Lvce Editor"
9
+ ],
10
+ "author": "Lvce Editor",
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/lvce-editor/lvce-editor.git",
15
+ "directory": "packages/process-explorer"
16
+ },
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "dependencies": {
21
+ "@lvce-editor/assert": "^1.2.0",
22
+ "@lvce-editor/ipc": "^8.2.1",
23
+ "@lvce-editor/json-rpc": "^1.3.0",
24
+ "@lvce-editor/verror": "^1.2.0"
25
+ },
26
+ "optionalDependencies": {
27
+ "@vscode/windows-process-tree": "^0.6.0"
28
+ },
29
+ "xo": {
30
+ "rules": {
31
+ "unicorn/filename-case": "off",
32
+ "indent": "off",
33
+ "semi": "off",
34
+ "no-unused-vars": "off",
35
+ "unicorn/numeric-separators-style": "off",
36
+ "no-extra-semi": "off",
37
+ "arrow-body-style": "off",
38
+ "padded-blocks": "off",
39
+ "capitalized-comments": "off",
40
+ "padding-line-between-statements": "off",
41
+ "arrow-parens": "off",
42
+ "no-warning-comments": "off",
43
+ "array-bracket-spacing": "off",
44
+ "comma-spacing": "off",
45
+ "unicorn/no-array-callback-reference": "off",
46
+ "comma-dangle": "off",
47
+ "operator-linebreak": "off",
48
+ "no-case-declarations": "off",
49
+ "no-undef": "off",
50
+ "object-curly-spacing": "off",
51
+ "object-shorthand": "off",
52
+ "complexity": "off",
53
+ "no-labels": "off",
54
+ "no-multi-assign": "off",
55
+ "max-params": "off",
56
+ "no-bitwise": "off",
57
+ "unicorn/prefer-math-trunc": "off",
58
+ "no-await-in-loop": "off",
59
+ "unicorn/prefer-add-event-listener": "off",
60
+ "no-unused-expressions": "off",
61
+ "node/prefer-global/process": "off",
62
+ "unicorn/prevent-abbreviations": "off",
63
+ "unicorn/no-process-exit": "off",
64
+ "quotes": "off",
65
+ "n/prefer-global/process": [
66
+ "error",
67
+ "always"
68
+ ]
69
+ },
70
+ "ignores": [
71
+ "distmin"
72
+ ]
73
+ }
74
+ }