@gtkx/utils 1.6.0 → 2.0.0-beta.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/README.md +5 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/path.d.ts +5 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +10 -0
- package/dist/path.js.map +1 -0
- package/dist/process/index.d.ts +1 -0
- package/dist/process/index.d.ts.map +1 -1
- package/dist/process/index.js +1 -0
- package/dist/process/index.js.map +1 -1
- package/dist/process/kill-marked-processes.d.ts +3 -2
- package/dist/process/kill-marked-processes.d.ts.map +1 -1
- package/dist/process/kill-marked-processes.js +44 -13
- package/dist/process/kill-marked-processes.js.map +1 -1
- package/dist/process/kill-process-group.d.ts +16 -0
- package/dist/process/kill-process-group.d.ts.map +1 -0
- package/dist/process/kill-process-group.js +72 -0
- package/dist/process/kill-process-group.js.map +1 -0
- package/dist/process/process-guard.js +77 -2
- package/dist/process/process-guard.js.map +1 -1
- package/dist/process/resolve-executable.d.ts +2 -2
- package/dist/process/resolve-executable.d.ts.map +1 -1
- package/dist/process/resolve-executable.js +3 -37
- package/dist/process/resolve-executable.js.map +1 -1
- package/dist/process/spawn-with-parent-death-signal.d.ts +1 -0
- package/dist/process/spawn-with-parent-death-signal.d.ts.map +1 -1
- package/dist/process/spawn-with-parent-death-signal.js +66 -3
- package/dist/process/spawn-with-parent-death-signal.js.map +1 -1
- package/package.json +9 -3
- package/src/index.ts +1 -1
- package/src/path.ts +17 -0
- package/src/process/index.ts +5 -0
- package/src/process/kill-marked-processes.ts +62 -13
- package/src/process/kill-process-group.ts +104 -0
- package/src/process/process-guard.ts +106 -2
- package/src/process/resolve-executable.ts +4 -46
- package/src/process/spawn-with-parent-death-signal.ts +106 -3
- package/dist/map/get-or-insert.d.ts +0 -8
- package/dist/map/get-or-insert.d.ts.map +0 -1
- package/dist/map/get-or-insert.js +0 -10
- package/dist/map/get-or-insert.js.map +0 -1
- package/dist/map/index.d.ts +0 -2
- package/dist/map/index.d.ts.map +0 -1
- package/dist/map/index.js +0 -2
- package/dist/map/index.js.map +0 -1
- package/src/map/get-or-insert.ts +0 -18
- package/src/map/index.ts +0 -1
|
@@ -4,22 +4,49 @@ import { Socket } from "node:net";
|
|
|
4
4
|
import { dirname, extname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { killMarkedProcesses, PROCESS_MARKER } from "./kill-marked-processes.ts";
|
|
7
|
+
import {
|
|
8
|
+
type CleanupDirectoryIdentity,
|
|
9
|
+
cleanupDirectoryIdentity,
|
|
10
|
+
killProcessGroup,
|
|
11
|
+
type ProcessGroupIdentity,
|
|
12
|
+
processGroupIdentity,
|
|
13
|
+
removeCleanupDirectory,
|
|
14
|
+
} from "./kill-process-group.ts";
|
|
7
15
|
import { resolveExecutable } from "./resolve-executable.ts";
|
|
8
16
|
|
|
9
17
|
type ParentDeathSpawnOptions = {
|
|
10
18
|
stdio?: StdioOptions;
|
|
11
19
|
env?: NodeJS.ProcessEnv;
|
|
20
|
+
cleanupDirectories?: string[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type GuardJob = {
|
|
24
|
+
marker: string;
|
|
25
|
+
processGroup: ProcessGroupIdentity;
|
|
26
|
+
cleanupDirectories: CleanupDirectoryIdentity[];
|
|
12
27
|
};
|
|
13
28
|
|
|
14
29
|
type GuardState = {
|
|
15
30
|
child?: ChildProcess | undefined;
|
|
31
|
+
jobs: Map<string, GuardJob>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type SpawnRollback = {
|
|
35
|
+
child: ChildProcess;
|
|
36
|
+
marker: string;
|
|
37
|
+
processGroup?: ProcessGroupIdentity | undefined;
|
|
38
|
+
cleanupDirectories?: CleanupDirectoryIdentity[] | undefined;
|
|
16
39
|
};
|
|
17
40
|
|
|
18
41
|
const MODULE_PATH = fileURLToPath(import.meta.url);
|
|
19
42
|
const GUARD_PATH = join(dirname(MODULE_PATH), `process-guard${extname(MODULE_PATH)}`);
|
|
20
43
|
const RUN_ID = randomBytes(8).toString("hex");
|
|
21
44
|
const RUN_PREFIX = `${PROCESS_MARKER}=${RUN_ID}/`;
|
|
22
|
-
const guard: GuardState = {};
|
|
45
|
+
const guard: GuardState = { jobs: new Map() };
|
|
46
|
+
|
|
47
|
+
const writeGuardCommand = (child: ChildProcess, operation: "+" | "-", job: GuardJob): void => {
|
|
48
|
+
child.stdin?.write(`${operation}${JSON.stringify(job)}\n`);
|
|
49
|
+
};
|
|
23
50
|
|
|
24
51
|
const releaseGuard = (child: ChildProcess): void => {
|
|
25
52
|
child.unref();
|
|
@@ -41,13 +68,62 @@ const startGuard = (): void => {
|
|
|
41
68
|
});
|
|
42
69
|
|
|
43
70
|
child.on("error", () => {
|
|
44
|
-
guard.child
|
|
71
|
+
if (guard.child === child) {
|
|
72
|
+
guard.child = undefined;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
child.on("exit", () => {
|
|
77
|
+
if (guard.child === child) {
|
|
78
|
+
guard.child = undefined;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
child.stdin.on("error", () => {
|
|
83
|
+
if (guard.child === child) {
|
|
84
|
+
guard.child = undefined;
|
|
85
|
+
}
|
|
45
86
|
});
|
|
46
87
|
|
|
47
88
|
guard.child = child;
|
|
89
|
+
|
|
90
|
+
for (const job of guard.jobs.values()) {
|
|
91
|
+
writeGuardCommand(child, "+", job);
|
|
92
|
+
}
|
|
93
|
+
|
|
48
94
|
releaseGuard(child);
|
|
49
95
|
};
|
|
50
96
|
|
|
97
|
+
const registerJob = (job: GuardJob): void => {
|
|
98
|
+
guard.jobs.set(job.marker, job);
|
|
99
|
+
|
|
100
|
+
if (guard.child) {
|
|
101
|
+
writeGuardCommand(guard.child, "+", job);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const unregisterJob = (job: GuardJob): void => {
|
|
106
|
+
guard.jobs.delete(job.marker);
|
|
107
|
+
|
|
108
|
+
if (guard.child) {
|
|
109
|
+
writeGuardCommand(guard.child, "-", job);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const rollbackSpawn = ({ child, marker, processGroup, cleanupDirectories = [] }: SpawnRollback): void => {
|
|
114
|
+
if (processGroup === undefined) {
|
|
115
|
+
child.kill("SIGKILL");
|
|
116
|
+
} else {
|
|
117
|
+
killProcessGroup(processGroup);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
killMarkedProcesses(marker);
|
|
121
|
+
|
|
122
|
+
for (const identity of cleanupDirectories) {
|
|
123
|
+
removeCleanupDirectory(identity);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
51
127
|
function spawnWithParentDeathSignal(
|
|
52
128
|
command: string,
|
|
53
129
|
args: string[],
|
|
@@ -56,6 +132,7 @@ function spawnWithParentDeathSignal(
|
|
|
56
132
|
const executable = resolveExecutable(command);
|
|
57
133
|
const setpriv = resolveExecutable("setpriv");
|
|
58
134
|
const jobValue = `${RUN_ID}/${randomBytes(4).toString("hex")}`;
|
|
135
|
+
const marker = `${PROCESS_MARKER}=${jobValue}`;
|
|
59
136
|
startGuard();
|
|
60
137
|
|
|
61
138
|
const child = spawn(setpriv, ["--pdeathsig", "SIGKILL", executable, ...args], {
|
|
@@ -64,8 +141,34 @@ function spawnWithParentDeathSignal(
|
|
|
64
141
|
env: { ...(options.env ?? process.env), [PROCESS_MARKER]: jobValue },
|
|
65
142
|
});
|
|
66
143
|
|
|
144
|
+
const processGroupId = child.pid;
|
|
145
|
+
const group = processGroupId === undefined ? undefined : processGroupIdentity(processGroupId);
|
|
146
|
+
|
|
147
|
+
if (group === undefined) {
|
|
148
|
+
rollbackSpawn({ child, marker });
|
|
149
|
+
throw new Error(`Failed to identify process group for ${command}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const cleanupDirectories = (options.cleanupDirectories ?? []).map((path) => cleanupDirectoryIdentity(path));
|
|
153
|
+
|
|
154
|
+
if (cleanupDirectories.includes(undefined)) {
|
|
155
|
+
const captured = cleanupDirectories.filter(
|
|
156
|
+
(identity): identity is CleanupDirectoryIdentity => identity !== undefined,
|
|
157
|
+
);
|
|
158
|
+
rollbackSpawn({ child, marker, processGroup: group, cleanupDirectories: captured });
|
|
159
|
+
throw new Error(`Failed to identify a cleanup directory for ${command}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const job: GuardJob = {
|
|
163
|
+
marker,
|
|
164
|
+
processGroup: group,
|
|
165
|
+
cleanupDirectories: cleanupDirectories.filter((identity) => identity !== undefined),
|
|
166
|
+
};
|
|
167
|
+
registerJob(job);
|
|
168
|
+
|
|
67
169
|
child.on("exit", () => {
|
|
68
|
-
killMarkedProcesses(
|
|
170
|
+
killMarkedProcesses(job.marker);
|
|
171
|
+
unregisterJob(job);
|
|
69
172
|
});
|
|
70
173
|
|
|
71
174
|
return child;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
type GetSet<K, V> = {
|
|
2
|
-
get(key: K): V | undefined;
|
|
3
|
-
set(key: K, value: V): unknown;
|
|
4
|
-
has(key: K): boolean;
|
|
5
|
-
};
|
|
6
|
-
declare function getOrInsert<K, V>(map: GetSet<K, V>, key: K, factory: (key: K) => V): V;
|
|
7
|
-
export { getOrInsert };
|
|
8
|
-
//# sourceMappingURL=get-or-insert.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-or-insert.d.ts","sourceRoot":"","sources":["../../src/map/get-or-insert.ts"],"names":[],"mappings":"AAAA,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI;IAChB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC3B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IAC/B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,iBAAS,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAS/E;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-or-insert.js","sourceRoot":"","sources":["../../src/map/get-or-insert.ts"],"names":[],"mappings":"AAMA,SAAS,WAAW,CAAO,GAAiB,EAAE,GAAM,EAAE,OAAsB;IACxE,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACf,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAM,CAAC;IAC7B,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAEpB,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC","sourcesContent":["type GetSet<K, V> = {\n get(key: K): V | undefined;\n set(key: K, value: V): unknown;\n has(key: K): boolean;\n};\n\nfunction getOrInsert<K, V>(map: GetSet<K, V>, key: K, factory: (key: K) => V): V {\n if (map.has(key)) {\n return map.get(key) as V;\n }\n\n const value = factory(key);\n map.set(key, value);\n\n return value;\n}\n\nexport { getOrInsert };\n"]}
|
package/dist/map/index.d.ts
DELETED
package/dist/map/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/map/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/map/index.js
DELETED
package/dist/map/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/map/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export { getOrInsert } from \"./get-or-insert.ts\";\n"]}
|
package/src/map/get-or-insert.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
type GetSet<K, V> = {
|
|
2
|
-
get(key: K): V | undefined;
|
|
3
|
-
set(key: K, value: V): unknown;
|
|
4
|
-
has(key: K): boolean;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
function getOrInsert<K, V>(map: GetSet<K, V>, key: K, factory: (key: K) => V): V {
|
|
8
|
-
if (map.has(key)) {
|
|
9
|
-
return map.get(key) as V;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const value = factory(key);
|
|
13
|
-
map.set(key, value);
|
|
14
|
-
|
|
15
|
-
return value;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export { getOrInsert };
|
package/src/map/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { getOrInsert } from "./get-or-insert.ts";
|