@gjsify/utils 0.0.2
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/lib/cjs/cancel-handler.js +41 -0
- package/lib/cjs/cli.js +40 -0
- package/lib/cjs/error.js +40 -0
- package/lib/cjs/file.js +42 -0
- package/lib/cjs/fs.js +50 -0
- package/lib/cjs/index.js +30 -0
- package/lib/cjs/log.js +188 -0
- package/lib/cjs/message.js +32 -0
- package/lib/cjs/os.js +110 -0
- package/lib/cjs/path.js +79 -0
- package/lib/cjs/process.js +70 -0
- package/lib/cjs/system.js +77 -0
- package/lib/cjs/tty.js +32 -0
- package/lib/cjs/types/cancel-signals.js +15 -0
- package/lib/cjs/types/error-data.js +15 -0
- package/lib/cjs/types/index.js +24 -0
- package/lib/cjs/types/signal-methods.js +15 -0
- package/lib/cjs/types/stack-trace-frame.js +15 -0
- package/lib/cjs/types/structured-log-data.js +15 -0
- package/lib/cjs/types/uncaught-exception-data.js +15 -0
- package/lib/cjs/types/unhandled-rejection-data.js +15 -0
- package/lib/cjs/types/user-info.js +15 -0
- package/lib/cjs/version.js +26 -0
- package/lib/esm/cancel-handler.js +22 -0
- package/lib/esm/cli.js +11 -0
- package/lib/esm/error.js +21 -0
- package/lib/esm/file.js +13 -0
- package/lib/esm/fs.js +21 -0
- package/lib/esm/index.js +14 -0
- package/lib/esm/log.js +159 -0
- package/lib/esm/message.js +13 -0
- package/lib/esm/os.js +81 -0
- package/lib/esm/path.js +50 -0
- package/lib/esm/process.js +41 -0
- package/lib/esm/system.js +47 -0
- package/lib/esm/tty.js +13 -0
- package/lib/esm/types/cancel-signals.js +0 -0
- package/lib/esm/types/error-data.js +0 -0
- package/lib/esm/types/index.js +8 -0
- package/lib/esm/types/signal-methods.js +0 -0
- package/lib/esm/types/stack-trace-frame.js +0 -0
- package/lib/esm/types/structured-log-data.js +0 -0
- package/lib/esm/types/uncaught-exception-data.js +0 -0
- package/lib/esm/types/unhandled-rejection-data.js +0 -0
- package/lib/esm/types/user-info.js +0 -0
- package/lib/esm/version.js +7 -0
- package/lib/types/cancel-handler.d.ts +4 -0
- package/lib/types/cli.d.ts +1 -0
- package/lib/types/error.d.ts +7 -0
- package/lib/types/file.d.ts +1 -0
- package/lib/types/fs.d.ts +3 -0
- package/lib/types/index.d.ts +14 -0
- package/lib/types/log.d.ts +29 -0
- package/lib/types/message.d.ts +2 -0
- package/lib/types/os.d.ts +7 -0
- package/lib/types/path.d.ts +6 -0
- package/lib/types/process.d.ts +6 -0
- package/lib/types/system.d.ts +5 -0
- package/lib/types/tty.d.ts +5 -0
- package/lib/types/types/cancel-signals.d.ts +5 -0
- package/lib/types/types/error-data.d.ts +7 -0
- package/lib/types/types/index.d.ts +8 -0
- package/lib/types/types/signal-methods.d.ts +62 -0
- package/lib/types/types/stack-trace-frame.d.ts +6 -0
- package/lib/types/types/structured-log-data.d.ts +5 -0
- package/lib/types/types/uncaught-exception-data.d.ts +4 -0
- package/lib/types/types/unhandled-rejection-data.d.ts +5 -0
- package/lib/types/types/user-info.d.ts +8 -0
- package/lib/types/version.d.ts +1 -0
- package/package.json +46 -0
- package/src/cancel-handler.ts +24 -0
- package/src/cli.ts +10 -0
- package/src/error.ts +38 -0
- package/src/file.ts +12 -0
- package/src/fs.ts +24 -0
- package/src/index.ts +15 -0
- package/src/log.spec.ts +32 -0
- package/src/log.ts +218 -0
- package/src/message.ts +11 -0
- package/src/os.ts +94 -0
- package/src/path.ts +52 -0
- package/src/process.spec.ts +24 -0
- package/src/process.ts +46 -0
- package/src/system.ts +56 -0
- package/src/test.ts +6 -0
- package/src/tty.spec.ts +10 -0
- package/src/tty.ts +10 -0
- package/src/types/cancel-signals.ts +6 -0
- package/src/types/error-data.ts +8 -0
- package/src/types/index.ts +8 -0
- package/src/types/signal-methods.ts +63 -0
- package/src/types/stack-trace-frame.ts +7 -0
- package/src/types/structured-log-data.ts +5 -0
- package/src/types/uncaught-exception-data.ts +5 -0
- package/src/types/unhandled-rejection-data.ts +6 -0
- package/src/types/user-info.ts +9 -0
- package/src/version.ts +4 -0
- package/test.gjs.js +34793 -0
- package/test.gjs.js.map +7 -0
- package/test.gjs.js.meta.json +1 -0
- package/tsconfig.json +21 -0
- package/tsconfig.types.json +7 -0
- package/tsconfig.types.tsbuildinfo +1 -0
package/src/fs.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Gio from '@girs/gio-2.0';
|
|
2
|
+
|
|
3
|
+
/** Check if a file descriptor exists */
|
|
4
|
+
export const existsFD = (fd: number) => {
|
|
5
|
+
try {
|
|
6
|
+
let stream = Gio.UnixInputStream.new(fd, false);
|
|
7
|
+
stream.close(null);
|
|
8
|
+
// File descriptor 12345 exists
|
|
9
|
+
return true
|
|
10
|
+
} catch (error) {
|
|
11
|
+
// File descriptor 12345 does not exist
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function existsSync(path: string) {
|
|
17
|
+
// TODO: accept buffer and URL too
|
|
18
|
+
if (typeof path !== 'string' || path === '') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const file = Gio.File.new_for_path(path);
|
|
23
|
+
return file.query_exists(null);
|
|
24
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './types/index.js';
|
|
2
|
+
|
|
3
|
+
export * from './cancel-handler.js';
|
|
4
|
+
export * from './cli.js';
|
|
5
|
+
export * from './error.js';
|
|
6
|
+
export * from './file.js';
|
|
7
|
+
export * from './fs.js';
|
|
8
|
+
export * from './log.js';
|
|
9
|
+
export * from './message.js';
|
|
10
|
+
export * from './os.js';
|
|
11
|
+
export * from './path.js';
|
|
12
|
+
export * from './process.js';
|
|
13
|
+
export * from './system.js';
|
|
14
|
+
export * from './tty.js';
|
|
15
|
+
export * from './version.js';
|
package/src/log.spec.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, assert, spy } from '@gjsify/unit';
|
|
2
|
+
// import { logSignals } from '@gjsify/utils';
|
|
3
|
+
// import type { StructuredLogData } from '@gjsify/utils';
|
|
4
|
+
|
|
5
|
+
const createUncaughtException = async () => {
|
|
6
|
+
throw new Error("top level error");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const sleep = (ms: number) => {
|
|
10
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default async () => {
|
|
14
|
+
await describe('logSignals', async () => {
|
|
15
|
+
// TODO: Fix this test
|
|
16
|
+
|
|
17
|
+
// await it("should emit an uncaughtException event on a top level throw", async () => {
|
|
18
|
+
// const onUnhandledRejection = spy((_self, _data: StructuredLogData, _promiseData) => {});
|
|
19
|
+
|
|
20
|
+
// const signalHandlerId = logSignals.connect("unhandledRejection", onUnhandledRejection);
|
|
21
|
+
|
|
22
|
+
// createUncaughtException();
|
|
23
|
+
|
|
24
|
+
// await sleep(10);
|
|
25
|
+
|
|
26
|
+
// logSignals.disconnect(signalHandlerId)
|
|
27
|
+
|
|
28
|
+
// assert.strictEqual(onUnhandledRejection.calls.length, 1, "onUnhandledRejection should be called.")
|
|
29
|
+
// // assert.strictEqual(onUnhandledRejection.calls[0].arguments[0], error)
|
|
30
|
+
// })
|
|
31
|
+
});
|
|
32
|
+
}
|
package/src/log.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import GLib from '@girs/glib-2.0';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
StructuredLogData,
|
|
5
|
+
SignalMethods,
|
|
6
|
+
StackTraceFrame,
|
|
7
|
+
ErrorData,
|
|
8
|
+
UncaughtExceptionData,
|
|
9
|
+
UnhandledRejectionData,
|
|
10
|
+
} from './types/index.js';
|
|
11
|
+
|
|
12
|
+
export const logLevelToString = (logLevel: GLib.LogLevelFlags) => {
|
|
13
|
+
switch (logLevel) {
|
|
14
|
+
case GLib.LogLevelFlags.FLAG_FATAL:
|
|
15
|
+
return "FLAG_FATAL";
|
|
16
|
+
case GLib.LogLevelFlags.FLAG_RECURSION:
|
|
17
|
+
return "FLAG_RECURSION";
|
|
18
|
+
case GLib.LogLevelFlags.LEVEL_CRITICAL:
|
|
19
|
+
return "LEVEL_CRITICAL";
|
|
20
|
+
case GLib.LogLevelFlags.LEVEL_DEBUG:
|
|
21
|
+
return "LEVEL_DEBUG";
|
|
22
|
+
case GLib.LogLevelFlags.LEVEL_ERROR:
|
|
23
|
+
return "LEVEL_ERROR";
|
|
24
|
+
case GLib.LogLevelFlags.LEVEL_INFO:
|
|
25
|
+
return "LEVEL_INFO";
|
|
26
|
+
case GLib.LogLevelFlags.LEVEL_MASK:
|
|
27
|
+
return "LEVEL_MASK";
|
|
28
|
+
case GLib.LogLevelFlags.LEVEL_MESSAGE:
|
|
29
|
+
return "LEVEL_MESSAGE";
|
|
30
|
+
case GLib.LogLevelFlags.LEVEL_WARNING:
|
|
31
|
+
return "LEVEL_WARNING";
|
|
32
|
+
default:
|
|
33
|
+
return "UNKNOWN"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const STACK_TRACE_REGEX = /^.*@(.*):(\d+):(\d+)/;
|
|
38
|
+
|
|
39
|
+
export const parseStackTrace = (stackTraceLine: string): StackTraceFrame | null => {
|
|
40
|
+
const match = stackTraceLine.match(STACK_TRACE_REGEX);
|
|
41
|
+
if (match) {
|
|
42
|
+
const [, fileName, lineNumber, columnNumber] = match;
|
|
43
|
+
return { fileName, lineNumber: Number(lineNumber), columnNumber: Number(columnNumber), line: stackTraceLine };
|
|
44
|
+
}
|
|
45
|
+
// console.warn("Can't parse stack trace line: " + stackTraceLine);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const getStackTraceStartLineIndex = (lines: string[]) => {
|
|
50
|
+
for (let i = 1; i < lines.length; i++) {
|
|
51
|
+
const line = lines[i];
|
|
52
|
+
if(STACK_TRACE_REGEX.test(line)) {
|
|
53
|
+
return i;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return -1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const extractErrorData = (errorMessage: string): ErrorData => {
|
|
60
|
+
const lines = errorMessage.split('\n');
|
|
61
|
+
|
|
62
|
+
for (let line of lines) {
|
|
63
|
+
line = line.trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const endOfErrorType = lines[0].indexOf(': ');
|
|
67
|
+
let errorType = "Error";
|
|
68
|
+
|
|
69
|
+
if(endOfErrorType > 0) {
|
|
70
|
+
errorType = lines[0].slice(0, endOfErrorType);
|
|
71
|
+
// If error type exists
|
|
72
|
+
if(globalThis[errorType]) {
|
|
73
|
+
lines[0] = lines[0].slice(endOfErrorType + 2);
|
|
74
|
+
} else {
|
|
75
|
+
errorType = "Error";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let stackTraceLineIndex = getStackTraceStartLineIndex(lines);
|
|
80
|
+
|
|
81
|
+
const message = lines.slice(0, stackTraceLineIndex).join('\n');
|
|
82
|
+
const stackTraceLines = lines.slice(stackTraceLineIndex);
|
|
83
|
+
const frames: StackTraceFrame[] = [];
|
|
84
|
+
|
|
85
|
+
for (const stackTraceLine of stackTraceLines) {
|
|
86
|
+
const frame = parseStackTrace(stackTraceLine);
|
|
87
|
+
if(frame) {
|
|
88
|
+
frames.push(frame)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
errorType,
|
|
94
|
+
message,
|
|
95
|
+
frames,
|
|
96
|
+
stackTraceLines,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* WORKAROUND: Extract the information from the log message until https://gitlab.gnome.org/GNOME/gjs/-/issues/523 is solved
|
|
102
|
+
* @param errorMessage The original error message
|
|
103
|
+
* @returns The extracted error type, error message and stack trace
|
|
104
|
+
*/
|
|
105
|
+
const reconstructErrorFromMessage = (errorMessage: string): UncaughtExceptionData => {
|
|
106
|
+
const { errorType, frames, message, stackTraceLines } = extractErrorData(errorMessage);
|
|
107
|
+
const ErrorType = globalThis[errorType] as typeof Error;
|
|
108
|
+
const error = new ErrorType();
|
|
109
|
+
error.message = message;
|
|
110
|
+
error.stack = stackTraceLines.join("\n");
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
error,
|
|
114
|
+
errorType,
|
|
115
|
+
frames,
|
|
116
|
+
message,
|
|
117
|
+
stackTraceLines,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface LogSignals extends SignalMethods {
|
|
122
|
+
connect(sigName: "unhandledRejection", callback: (self: LogSignals, structuredData: StructuredLogData, promiseData: UnhandledRejectionData) => void): number;
|
|
123
|
+
connect(sigName: "uncaughtException", callback: (self: LogSignals, structuredData: StructuredLogData, errorData: UncaughtExceptionData) => void): number;
|
|
124
|
+
|
|
125
|
+
emit(sigName: "unhandledRejection", structuredData: StructuredLogData, promiseData: UnhandledRejectionData): void;
|
|
126
|
+
emit(sigName: "uncaughtException", structuredData: StructuredLogData, errorData: UncaughtExceptionData): void;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export class LogSignals {
|
|
130
|
+
|
|
131
|
+
private static instance: LogSignals;
|
|
132
|
+
|
|
133
|
+
/** This is a singleton because log_set_writer_func may only be called once */
|
|
134
|
+
private constructor() {
|
|
135
|
+
this.initHandler();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public static getSingleton() {
|
|
139
|
+
if(LogSignals.instance) {
|
|
140
|
+
return LogSignals.instance;
|
|
141
|
+
}
|
|
142
|
+
LogSignals.instance = new LogSignals();
|
|
143
|
+
return LogSignals.instance;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private initHandler() {
|
|
147
|
+
|
|
148
|
+
// @ts-ignore TODO: override type in ts-for-gir
|
|
149
|
+
GLib.log_set_writer_func((level: GLib.LogLevelFlags, fields?: { MESSAGE: Uint8Array, PRIORITY: Uint8Array, GLIB_DOMAIN: Uint8Array }) => {
|
|
150
|
+
|
|
151
|
+
const decoder = new TextDecoder('utf-8');
|
|
152
|
+
const message = decoder.decode(fields?.MESSAGE);
|
|
153
|
+
const priority = Number(decoder.decode(fields?.PRIORITY));
|
|
154
|
+
const domain = decoder.decode(fields?.GLIB_DOMAIN);
|
|
155
|
+
|
|
156
|
+
const data: StructuredLogData = {
|
|
157
|
+
message,
|
|
158
|
+
priority,
|
|
159
|
+
domain
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!this.handler(level, data)) {
|
|
163
|
+
// Output error as usual
|
|
164
|
+
level |= GLib.LogLevelFlags.FLAG_RECURSION;
|
|
165
|
+
GLib.log_default_handler(domain, level, message, null);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// @ts-ignore
|
|
169
|
+
// GLib.log_set_writer_default();
|
|
170
|
+
|
|
171
|
+
return GLib.LogWriterOutput.HANDLED;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A log handler to emit `unhandledRejection` and `uncaughtException` events
|
|
177
|
+
* @param level The log level
|
|
178
|
+
* @param data The structured log data
|
|
179
|
+
* @returns `true` to catch the log or `false` to output the error to the console as usual
|
|
180
|
+
*/
|
|
181
|
+
handler(level: GLib.LogLevelFlags, structuredData: StructuredLogData) {
|
|
182
|
+
if(level === GLib.LogLevelFlags.LEVEL_WARNING && structuredData.domain === "Gjs" && structuredData.message.startsWith('Unhandled promise rejection')) {
|
|
183
|
+
try {
|
|
184
|
+
const errorData = reconstructErrorFromMessage(structuredData.message);
|
|
185
|
+
// TODO we need a way to get the promise of the unhandled rejection, see https://gitlab.gnome.org/GNOME/gjs/-/issues/523
|
|
186
|
+
const fakePromise = new Promise<any>(() => {});
|
|
187
|
+
logSignals.emit("unhandledRejection", structuredData, {
|
|
188
|
+
reason: errorData.error,
|
|
189
|
+
promise: fakePromise,
|
|
190
|
+
errorType: errorData.errorType,
|
|
191
|
+
frames: errorData.frames,
|
|
192
|
+
message: errorData.message,
|
|
193
|
+
stackTraceLines: errorData.stackTraceLines,
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
printerr(error)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
} else if (level === GLib.LogLevelFlags.LEVEL_CRITICAL) {
|
|
200
|
+
const errorData = reconstructErrorFromMessage(structuredData.message);
|
|
201
|
+
logSignals.emit("uncaughtException", structuredData, errorData);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Debug
|
|
205
|
+
// print("\n\n[log_set_writer_func] \nmessage:", structuredData.message, "\nlogDomain:", structuredData.domain, `\nlevel: ${level} (${logLevelToString(level)})`, );
|
|
206
|
+
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
imports.signals.addSignalMethods(LogSignals.prototype);
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Emits log signals like `unhandledRejection` and `unhandledRejection`
|
|
215
|
+
*/
|
|
216
|
+
export const logSignals = LogSignals.getSingleton();
|
|
217
|
+
|
|
218
|
+
|
package/src/message.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// See https://github.com/denoland/deno_std/blob/44d05e7a8d445888d989d49eb3e59eee3055f2c5/node/_utils.ts#L21
|
|
2
|
+
export const notImplemented = (msg: string) => {
|
|
3
|
+
const message = msg ? `Not implemented: ${msg}` : "Not implemented";
|
|
4
|
+
throw new Error(message);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const warnNotImplemented = (msg) => {
|
|
8
|
+
const message = msg ? `Not implemented: ${msg}` : "Not implemented";
|
|
9
|
+
console.warn(message);
|
|
10
|
+
return message;
|
|
11
|
+
}
|
package/src/os.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { cli } from './cli.js';
|
|
2
|
+
import GLib from '@girs/glib-2.0';
|
|
3
|
+
import Gio from '@girs/gio-2.0';
|
|
4
|
+
|
|
5
|
+
import type { UserInfo } from './types/index.js';
|
|
6
|
+
|
|
7
|
+
/** Cache the arch detection */
|
|
8
|
+
let _arch: string = "";
|
|
9
|
+
|
|
10
|
+
/** Cache the os detection */
|
|
11
|
+
let _os: string = "";
|
|
12
|
+
|
|
13
|
+
/** Cache the target detection */
|
|
14
|
+
let _target: string = "";
|
|
15
|
+
|
|
16
|
+
/** Cache the userInfo */
|
|
17
|
+
const userInfo: {[username: string]: UserInfo} = {};
|
|
18
|
+
|
|
19
|
+
export const getArch = () => {
|
|
20
|
+
if(_arch) {
|
|
21
|
+
return _arch;
|
|
22
|
+
}
|
|
23
|
+
_arch = cli('uname -m').trim();
|
|
24
|
+
return _arch;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const getOs = () => {
|
|
28
|
+
if(_os) {
|
|
29
|
+
return _os;
|
|
30
|
+
}
|
|
31
|
+
const os = cli('uname -o').trim();
|
|
32
|
+
if(/\bDarwin\b/i.test(os)) {
|
|
33
|
+
_os = 'darwin';
|
|
34
|
+
return _os;
|
|
35
|
+
}
|
|
36
|
+
if(/\bLinux\b/i.test(os)) {
|
|
37
|
+
_os = 'linux';
|
|
38
|
+
return _os;
|
|
39
|
+
}
|
|
40
|
+
_os = 'win32';
|
|
41
|
+
return _os;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const getVendor = () => 'gjsify';
|
|
45
|
+
|
|
46
|
+
export const getEnv = () => 'gnu';
|
|
47
|
+
|
|
48
|
+
export const getTarget = () => {
|
|
49
|
+
if(_target) {
|
|
50
|
+
return _target;
|
|
51
|
+
}
|
|
52
|
+
_target = `${getArch()}-${getVendor()}-${getOs()}-${getEnv()}`;
|
|
53
|
+
return _target;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Extract user info from passwd by username */
|
|
57
|
+
const extractUserInfo = (passwd: string, username: string) => {
|
|
58
|
+
const lines = passwd.split('\n');
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
const parts = line.split(':');
|
|
61
|
+
if (parts[0] === username) {
|
|
62
|
+
return {
|
|
63
|
+
username: parts[0],
|
|
64
|
+
// password: parts[1],
|
|
65
|
+
userId: parseInt(parts[2], 10),
|
|
66
|
+
groupId: parseInt(parts[3], 10),
|
|
67
|
+
userInfo: parts[4],
|
|
68
|
+
homeDirectory: parts[5],
|
|
69
|
+
shell: parts[6]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const getUserInfo = (username?: string) => {
|
|
77
|
+
const file = Gio.File.new_for_path('/etc/passwd');
|
|
78
|
+
if(!username) {
|
|
79
|
+
username = GLib.get_user_name();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if(userInfo[username]) {
|
|
83
|
+
return userInfo[username];
|
|
84
|
+
}
|
|
85
|
+
const [success, contents] = file.load_contents(null);
|
|
86
|
+
if(!success) {
|
|
87
|
+
throw new Error(`Failed to load ${file.get_path()}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const contentStr = new TextDecoder().decode(contents);
|
|
91
|
+
|
|
92
|
+
userInfo[username] = extractUserInfo(contentStr, username);
|
|
93
|
+
return userInfo[username];
|
|
94
|
+
}
|
package/src/path.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import Gio from '@girs/gio-2.0';
|
|
2
|
+
import GLib from '@girs/glib-2.0';
|
|
3
|
+
const { File } = Gio;
|
|
4
|
+
|
|
5
|
+
const _getProgramDir = (programFile: Gio.File) => {
|
|
6
|
+
const info = programFile.query_info('standard::', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
|
|
7
|
+
|
|
8
|
+
if (info.get_is_symlink()) {
|
|
9
|
+
const symlinkFile = programFile.get_parent().resolve_relative_path(info.get_symlink_target());
|
|
10
|
+
return symlinkFile.get_parent();
|
|
11
|
+
} else {
|
|
12
|
+
return programFile.get_parent();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const resolve = (dir: string, ...filenames: string[]) => {
|
|
17
|
+
let file = File.new_for_path(dir);
|
|
18
|
+
for (const filename of filenames) {
|
|
19
|
+
file = file.resolve_relative_path(filename);
|
|
20
|
+
}
|
|
21
|
+
return file;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const getProgramExe = () => {
|
|
25
|
+
const currentDir = GLib.get_current_dir();
|
|
26
|
+
return File.new_for_path(currentDir).resolve_relative_path(imports.system.programInvocationName);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const getProgramDir = () => {
|
|
30
|
+
return _getProgramDir(getProgramExe()).get_path();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const getPathSeparator = () => {
|
|
34
|
+
const currentDir = GLib.get_current_dir();
|
|
35
|
+
return /^\//.test(currentDir) ? '/' : '\\';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const getNodeModulesPath = () => {
|
|
39
|
+
let dir = File.new_for_path(getProgramDir());
|
|
40
|
+
let found = false;
|
|
41
|
+
|
|
42
|
+
do {
|
|
43
|
+
dir = dir.resolve_relative_path("..");
|
|
44
|
+
const nodeModulesDir = dir.resolve_relative_path("node_modules");
|
|
45
|
+
found = nodeModulesDir.query_exists(null);
|
|
46
|
+
if (found) {
|
|
47
|
+
dir = nodeModulesDir;
|
|
48
|
+
}
|
|
49
|
+
} while (dir.has_parent(null) && !found)
|
|
50
|
+
|
|
51
|
+
return dir;
|
|
52
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it, expect } from '@gjsify/unit';
|
|
2
|
+
import { getPid, getPpid } from './process.js';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
await describe('getPid', async () => {
|
|
6
|
+
const pid = getPid();
|
|
7
|
+
await it('should be a number', async () => {
|
|
8
|
+
expect(typeof pid).toBe("number");
|
|
9
|
+
});
|
|
10
|
+
await it('should be greater than 0', async () => {
|
|
11
|
+
expect(pid).toBeGreaterThan(0);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
await describe('getPpid', async () => {
|
|
16
|
+
const ppid = getPpid();
|
|
17
|
+
await it('should be a number', async () => {
|
|
18
|
+
expect(typeof ppid).toBe("number");
|
|
19
|
+
});
|
|
20
|
+
await it('should be greater than 0', async () => {
|
|
21
|
+
expect(ppid).toBeGreaterThan(0);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
package/src/process.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Gio from '@girs/gio-2.0';
|
|
2
|
+
import GLib from '@girs/glib-2.0';
|
|
3
|
+
|
|
4
|
+
/** Cached process id */
|
|
5
|
+
let PID = 0;
|
|
6
|
+
/** Cached parent process id */
|
|
7
|
+
let PPID = 0;
|
|
8
|
+
/** Cached locale */
|
|
9
|
+
let LOCALE = ""
|
|
10
|
+
|
|
11
|
+
export const getLocale = () => {
|
|
12
|
+
if(!LOCALE) {
|
|
13
|
+
LOCALE = GLib.getenv("LANG").split('.')[0].replace('_', '-');
|
|
14
|
+
}
|
|
15
|
+
return LOCALE
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Get process id */
|
|
19
|
+
export const getPid = () => {
|
|
20
|
+
if(!PID) {
|
|
21
|
+
PID = new Gio.Credentials().get_unix_pid();
|
|
22
|
+
}
|
|
23
|
+
return PID;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Get parent process id */
|
|
27
|
+
export const getPpid = () => {
|
|
28
|
+
if(!PPID) {
|
|
29
|
+
const path = `/proc/${getPid()}/status`;
|
|
30
|
+
const file = Gio.File.new_for_path(path);
|
|
31
|
+
const [success, contents] = file.load_contents(null);
|
|
32
|
+
if (success) {
|
|
33
|
+
const contentStr = new TextDecoder().decode(contents);
|
|
34
|
+
const numStr = contentStr.match(/^PPid:.*$/gm)[0]?.match(/(\d+)$/gm)[0]
|
|
35
|
+
PPID = Number(numStr);
|
|
36
|
+
} else {
|
|
37
|
+
console.error(new Error("Failed to read file: " + path));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return PPID;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const getTitle = () => {
|
|
45
|
+
return GLib.get_prgname();
|
|
46
|
+
}
|
package/src/system.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import GLib from '@girs/glib-2.0';
|
|
2
|
+
import Gio from '@girs/gio-2.0';
|
|
3
|
+
import { existsSync } from './fs.js';
|
|
4
|
+
|
|
5
|
+
const File = Gio.File;
|
|
6
|
+
|
|
7
|
+
/** Cache the getArgs result */
|
|
8
|
+
const args: string[] = [];
|
|
9
|
+
|
|
10
|
+
export const getArgv = () => {
|
|
11
|
+
|
|
12
|
+
if(args.length) {
|
|
13
|
+
return args;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const [__filename] = GLib.filename_from_uri((import.meta as any).url);
|
|
17
|
+
args.push(__filename);
|
|
18
|
+
ARGV.forEach(arg => {
|
|
19
|
+
if (arg[0] !== '-') {
|
|
20
|
+
args.push(
|
|
21
|
+
existsSync(arg) ?
|
|
22
|
+
File.new_for_path(arg).get_path() :
|
|
23
|
+
arg
|
|
24
|
+
);
|
|
25
|
+
} else {
|
|
26
|
+
args.push(arg);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return args;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const getArgs = () => {
|
|
34
|
+
return getArgv().slice(2);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const parseArgv = (argv: string[]): { [key: string]: string | boolean } => {
|
|
38
|
+
let result: { [key: string]: string | boolean } = {};
|
|
39
|
+
let currentKey: string | null = null;
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < argv.length; i++) {
|
|
42
|
+
let arg = argv[i];
|
|
43
|
+
|
|
44
|
+
if (arg.startsWith("--")) {
|
|
45
|
+
currentKey = arg.substring(2);
|
|
46
|
+
result[currentKey] = true;
|
|
47
|
+
} else if (currentKey !== null) {
|
|
48
|
+
result[currentKey] = arg;
|
|
49
|
+
currentKey = null;
|
|
50
|
+
} else {
|
|
51
|
+
result[arg] = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
}
|
package/src/test.ts
ADDED
package/src/tty.spec.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describe, it, expect } from '@gjsify/unit';
|
|
2
|
+
import { existsTty } from './tty.js';
|
|
3
|
+
|
|
4
|
+
export default async () => {
|
|
5
|
+
await describe('existsTty', async () => {
|
|
6
|
+
await it('should return true', async () => {
|
|
7
|
+
expect(existsTty()).toBeTruthy();
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
}
|
package/src/tty.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { existsFD } from './fs.js';
|
|
2
|
+
|
|
3
|
+
export const STDIN_FD = 0;
|
|
4
|
+
export const STDOUT_FD = 1;
|
|
5
|
+
export const STDERR_FD = 2;
|
|
6
|
+
|
|
7
|
+
/** Check if the stdout file descriptor exists, if this is the case a tty (terminal) should exists (untested) */
|
|
8
|
+
export const existsTty = () => {
|
|
9
|
+
return existsFD(STDOUT_FD);
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './cancel-signals.js';
|
|
2
|
+
export * from './error-data.js';
|
|
3
|
+
export * from './signal-methods.js';
|
|
4
|
+
export * from './stack-trace-frame.js';
|
|
5
|
+
export * from './structured-log-data.js';
|
|
6
|
+
export * from './uncaught-exception-data.js';
|
|
7
|
+
export * from './unhandled-rejection-data.js';
|
|
8
|
+
export * from './user-info.js';
|