@gtkx/utils 1.0.0-rc.4 → 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/README.md +5 -5
- package/dist/class/any-class.d.ts +2 -0
- package/dist/class/any-class.d.ts.map +1 -1
- package/dist/class/any-class.js.map +1 -1
- package/dist/map/get-or-insert.d.ts +1 -1
- package/dist/map/get-or-insert.d.ts.map +1 -1
- package/dist/map/get-or-insert.js.map +1 -1
- package/dist/process/index.d.ts +2 -1
- package/dist/process/index.d.ts.map +1 -1
- package/dist/process/index.js +2 -1
- package/dist/process/index.js.map +1 -1
- package/dist/process/install-graceful-shutdown.d.ts +1 -1
- package/dist/process/install-graceful-shutdown.d.ts.map +1 -1
- package/dist/process/install-graceful-shutdown.js.map +1 -1
- package/dist/process/kill-marked-processes.d.ts +4 -0
- package/dist/process/kill-marked-processes.d.ts.map +1 -0
- package/dist/process/kill-marked-processes.js +38 -0
- package/dist/process/kill-marked-processes.js.map +1 -0
- package/dist/process/process-guard.d.ts +2 -0
- package/dist/process/process-guard.d.ts.map +1 -0
- package/dist/process/process-guard.js +16 -0
- package/dist/process/process-guard.js.map +1 -0
- package/dist/process/resolve-executable.d.ts +2 -1
- package/dist/process/resolve-executable.d.ts.map +1 -1
- package/dist/process/resolve-executable.js +6 -3
- package/dist/process/resolve-executable.js.map +1 -1
- package/dist/process/spawn-with-parent-death-signal.d.ts +8 -0
- package/dist/process/spawn-with-parent-death-signal.d.ts.map +1 -0
- package/dist/process/spawn-with-parent-death-signal.js +50 -0
- package/dist/process/spawn-with-parent-death-signal.js.map +1 -0
- package/dist/util/package-version.d.ts +1 -1
- package/dist/util/package-version.d.ts.map +1 -1
- package/dist/util/package-version.js +2 -2
- package/dist/util/package-version.js.map +1 -1
- package/package.json +1 -1
- package/src/class/any-class.ts +2 -0
- package/src/map/get-or-insert.ts +1 -1
- package/src/process/index.ts +2 -1
- package/src/process/install-graceful-shutdown.ts +1 -1
- package/src/process/kill-marked-processes.ts +44 -0
- package/src/process/process-guard.ts +20 -0
- package/src/process/resolve-executable.ts +7 -3
- package/src/process/spawn-with-parent-death-signal.ts +74 -0
- package/src/util/package-version.ts +2 -2
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ React Native and similar frameworks hide the native toolkit so one API can run e
|
|
|
108
108
|
|
|
109
109
|
### Why Node.js, and why generated bindings
|
|
110
110
|
|
|
111
|
-
GTKX runs on Node.js, which puts native modules, the npm ecosystem, and the tooling built for Node.js APIs within reach. GJS is GNOME's own runtime, built on SpiderMonkey rather than V8; node-gtk runs on Node.js but is lightly maintained, on the older nan/V8 ABI rather than N-API, and still centered on GTK3. The [Why GTKX guide](https://gtkx.dev/guide/why-gtkx) covers
|
|
111
|
+
GTKX runs on Node.js, which puts native modules, the npm ecosystem, and the tooling built for Node.js APIs within reach. GJS is GNOME's own runtime, built on SpiderMonkey rather than V8; node-gtk runs on Node.js but is lightly maintained, on the older nan/V8 ABI rather than N-API, and still centered on GTK3. The [Why GTKX guide](https://gtkx.dev/guide/why-gtkx) covers what running on Node.js means day to day.
|
|
112
112
|
|
|
113
113
|
GTKX generates the TypeScript types and the native FFI calls from the same GObject-Introspection data, so the types cannot drift from the calls they back. Codegen covers the whole GTK4 and Adwaita surface.
|
|
114
114
|
|
|
@@ -121,10 +121,10 @@ GTKX is Linux-only and needs Node.js 24 or later. See [Requirements](#requiremen
|
|
|
121
121
|
Scaffold a new app with the `create-gtkx` initializer:
|
|
122
122
|
|
|
123
123
|
```sh
|
|
124
|
-
npm create gtkx
|
|
124
|
+
npm create gtkx
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
The same command works with other package managers: `pnpm create gtkx
|
|
127
|
+
The same command works with other package managers: `pnpm create gtkx` or `yarn create gtkx`.
|
|
128
128
|
|
|
129
129
|
Then run your new app:
|
|
130
130
|
|
|
@@ -145,7 +145,7 @@ The documentation at **[gtkx.dev](https://gtkx.dev)** includes a step-by-step tu
|
|
|
145
145
|
|
|
146
146
|
GTKX is Linux-only. You need:
|
|
147
147
|
|
|
148
|
-
- Linux with the GTK4 (4.20 or later), Adwaita (1.8 or later)
|
|
148
|
+
- Linux with the GTK4 (4.20 or later) and GLib development libraries, plus Adwaita (1.8 or later) once your project binds `Adw-1`
|
|
149
149
|
- Node.js 24 or later
|
|
150
150
|
|
|
151
151
|
The `@gtkx/native` addon ships prebuilt for x64 and arm64 glibc Linux; other targets need to build it from the GTKX repository, which requires a Rust toolchain.
|
|
@@ -161,7 +161,7 @@ Explore the [example apps](https://github.com/gtkx-org/gtkx/tree/main/examples):
|
|
|
161
161
|
|
|
162
162
|
## Status
|
|
163
163
|
|
|
164
|
-
GTKX 1.0 is
|
|
164
|
+
GTKX 1.0 is released.
|
|
165
165
|
|
|
166
166
|
## Contributing
|
|
167
167
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"any-class.d.ts","sourceRoot":"","sources":["../../src/class/any-class.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,MAChD,GAAG,IAAI,EAAE,KAAK,EAAE,KACf,CAAC,CAAC,GAAG;IACN,SAAS,EAAE,CAAC,CAAC;CAChB,CAAC;AAEF,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"any-class.d.ts","sourceRoot":"","sources":["../../src/class/any-class.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,KAAK,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,MAChD,GAAG,IAAI,EAAE,KAAK,EAAE,KACf,CAAC,CAAC,GAAG;IACN,iDAAiD;IACjD,SAAS,EAAE,CAAC,CAAC;CAChB,CAAC;AAEF,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"any-class.js","sourceRoot":"","sources":["../../src/class/any-class.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"any-class.js","sourceRoot":"","sources":["../../src/class/any-class.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,CAAC","sourcesContent":["/** Any class constructor, abstract ones included, whose instances are `T`. */\ntype AnyClass<T extends object = object> = (abstract new (\n ...args: never[]\n) => T) & {\n /** Object the class's instances inherit from. */\n prototype: T;\n};\n\nexport { type AnyClass };\n"]}
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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 +1 @@
|
|
|
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,
|
|
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/process/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { exitCodeForSignal } from "./exit-code-for-signal.ts";
|
|
2
2
|
export { installGracefulShutdown } from "./install-graceful-shutdown.ts";
|
|
3
|
-
export { resolveExecutable } from "./resolve-executable.ts";
|
|
3
|
+
export { resolveExecutable, tryResolveExecutable } from "./resolve-executable.ts";
|
|
4
|
+
export { spawnWithParentDeathSignal } from "./spawn-with-parent-death-signal.ts";
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/process/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/process/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC"}
|
package/dist/process/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { exitCodeForSignal } from "./exit-code-for-signal.js";
|
|
2
2
|
export { installGracefulShutdown } from "./install-graceful-shutdown.js";
|
|
3
|
-
export { resolveExecutable } from "./resolve-executable.js";
|
|
3
|
+
export { resolveExecutable, tryResolveExecutable } from "./resolve-executable.js";
|
|
4
|
+
export { spawnWithParentDeathSignal } from "./spawn-with-parent-death-signal.js";
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/process/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC","sourcesContent":["export { exitCodeForSignal } from \"./exit-code-for-signal.ts\";\nexport { installGracefulShutdown } from \"./install-graceful-shutdown.ts\";\nexport { resolveExecutable } from \"./resolve-executable.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/process/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC","sourcesContent":["export { exitCodeForSignal } from \"./exit-code-for-signal.ts\";\nexport { installGracefulShutdown } from \"./install-graceful-shutdown.ts\";\nexport { resolveExecutable, tryResolveExecutable } from \"./resolve-executable.ts\";\nexport { spawnWithParentDeathSignal } from \"./spawn-with-parent-death-signal.ts\";\n"]}
|
|
@@ -6,5 +6,5 @@ type GracefulShutdownOptions = {
|
|
|
6
6
|
exitCode?: (signal: NodeJS.Signals, isGraceful: boolean) => number;
|
|
7
7
|
};
|
|
8
8
|
declare function installGracefulShutdown(options: GracefulShutdownOptions): void;
|
|
9
|
-
export { installGracefulShutdown
|
|
9
|
+
export { installGracefulShutdown };
|
|
10
10
|
//# sourceMappingURL=install-graceful-shutdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-graceful-shutdown.d.ts","sourceRoot":"","sources":["../../src/process/install-graceful-shutdown.ts"],"names":[],"mappings":"AAGA,KAAK,uBAAuB,GAAG;IAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,KAAK,MAAM,CAAC;CACtE,CAAC;AA2FF,iBAAS,uBAAuB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAiBvE;AAED,OAAO,EAAE,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"install-graceful-shutdown.d.ts","sourceRoot":"","sources":["../../src/process/install-graceful-shutdown.ts"],"names":[],"mappings":"AAGA,KAAK,uBAAuB,GAAG;IAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,KAAK,MAAM,CAAC;CACtE,CAAC;AA2FF,iBAAS,uBAAuB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAiBvE;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-graceful-shutdown.js","sourceRoot":"","sources":["../../src/process/install-graceful-shutdown.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"install-graceful-shutdown.js","sourceRoot":"","sources":["../../src/process/install-graceful-shutdown.ts"],"names":[],"mappings":"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","sourcesContent":["import { error } from \"../log/default-logger.ts\";\nimport { exitCodeForSignal } from \"./exit-code-for-signal.ts\";\n\ntype GracefulShutdownOptions = {\n onSignal: (signal: NodeJS.Signals) => void | Promise<void>;\n onForce?: () => void;\n forceKillAfterMs?: number;\n coalesceWindowMs?: number;\n exitCode?: (signal: NodeJS.Signals, isGraceful: boolean) => number;\n};\n\ntype ShutdownState = {\n options: GracefulShutdownOptions;\n forceKillMs: number;\n coalesceWindowMs: number;\n firstSignal: NodeJS.Signals | null;\n hasExited: boolean;\n isCoalescing: boolean;\n forceTimer: NodeJS.Timeout | null;\n coalesceTimer: NodeJS.Timeout | null;\n};\n\nconst HANDLED_SIGNALS = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"] as const satisfies NodeJS.Signals[];\nconst DEFAULT_FORCE_KILL_TIMEOUT_MS = 5000;\nconst DEFAULT_COALESCE_WINDOW_MS = 500;\n\nconst clearTimers = (state: ShutdownState): void => {\n if (state.forceTimer) {\n clearTimeout(state.forceTimer);\n state.forceTimer = null;\n }\n\n if (state.coalesceTimer) {\n clearTimeout(state.coalesceTimer);\n state.coalesceTimer = null;\n }\n};\n\nconst finish = (state: ShutdownState, signal: NodeJS.Signals, isGraceful: boolean): void => {\n if (state.hasExited) {\n return;\n }\n\n state.hasExited = true;\n clearTimers(state);\n const { exitCode } = state.options;\n const defaultCode = isGraceful ? 0 : exitCodeForSignal(signal);\n process.exit(exitCode ? exitCode(signal, isGraceful) : defaultCode);\n};\n\nconst runShutdown = async (state: ShutdownState, signal: NodeJS.Signals): Promise<void> => {\n try {\n await state.options.onSignal(signal);\n finish(state, signal, true);\n } catch (error_) {\n error(\"graceful shutdown failed\", error_);\n finish(state, signal, false);\n }\n};\n\nconst beginShutdown = (state: ShutdownState, signal: NodeJS.Signals): void => {\n state.firstSignal = signal;\n\n if (state.coalesceWindowMs > 0) {\n state.isCoalescing = true;\n\n state.coalesceTimer = setTimeout(() => {\n state.isCoalescing = false;\n }, state.coalesceWindowMs);\n\n state.coalesceTimer.unref();\n }\n\n if (state.options.onForce && state.forceKillMs > 0) {\n state.forceTimer = setTimeout(() => {\n state.options.onForce?.();\n finish(state, signal, false);\n }, state.forceKillMs);\n\n state.forceTimer.unref();\n }\n\n void runShutdown(state, signal);\n};\n\nconst handle = (state: ShutdownState, signal: NodeJS.Signals): void => {\n if (state.firstSignal === null) {\n beginShutdown(state, signal);\n\n return;\n }\n\n if (state.isCoalescing) {\n return;\n }\n\n state.options.onForce?.();\n finish(state, signal, false);\n};\n\nfunction installGracefulShutdown(options: GracefulShutdownOptions): void {\n const state: ShutdownState = {\n options,\n forceKillMs: options.forceKillAfterMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS,\n coalesceWindowMs: options.coalesceWindowMs ?? DEFAULT_COALESCE_WINDOW_MS,\n firstSignal: null,\n hasExited: false,\n isCoalescing: false,\n forceTimer: null,\n coalesceTimer: null,\n };\n\n for (const sig of HANDLED_SIGNALS) {\n process.on(sig, () => {\n handle(state, sig);\n });\n }\n}\n\nexport { installGracefulShutdown };\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-marked-processes.d.ts","sourceRoot":"","sources":["../../src/process/kill-marked-processes.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,cAAc,uBAAuB,CAAC;AA2B5C,iBAAS,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAYjD;AAED,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
const PROCESS_MARKER = "GTKX_PROCESS_GUARD";
|
|
3
|
+
const MAX_KILL_PASSES = 8;
|
|
4
|
+
const isMarked = (pid, prefix) => {
|
|
5
|
+
try {
|
|
6
|
+
return readFileSync(`/proc/${String(pid)}/environ`, "utf8")
|
|
7
|
+
.split("\0")
|
|
8
|
+
.some((assignment) => assignment.startsWith(prefix));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const markedPids = (prefix) => readdirSync("/proc")
|
|
15
|
+
.map(Number)
|
|
16
|
+
.filter((pid) => Number.isSafeInteger(pid) && pid > 1 && pid !== process.pid)
|
|
17
|
+
.filter((pid) => isMarked(pid, prefix));
|
|
18
|
+
const killPid = (pid) => {
|
|
19
|
+
try {
|
|
20
|
+
process.kill(pid, "SIGKILL");
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
function killMarkedProcesses(prefix) {
|
|
27
|
+
for (let pass = 0; pass < MAX_KILL_PASSES; pass += 1) {
|
|
28
|
+
const pids = markedPids(prefix);
|
|
29
|
+
if (pids.length === 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const pid of pids) {
|
|
33
|
+
killPid(pid);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export { PROCESS_MARKER, killMarkedProcesses };
|
|
38
|
+
//# sourceMappingURL=kill-marked-processes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-marked-processes.js","sourceRoot":"","sources":["../../src/process/kill-marked-processes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEpD,MAAM,cAAc,GAAG,oBAAoB,CAAC;AAC5C,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,MAAc,EAAW,EAAE;IACtD,IAAI,CAAC;QACD,OAAO,YAAY,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC;aACtD,KAAK,CAAC,IAAI,CAAC;aACX,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,MAAc,EAAY,EAAE,CAC5C,WAAW,CAAC,OAAO,CAAC;KACf,GAAG,CAAC,MAAM,CAAC;KACX,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;KAC5E,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;AAEhD,MAAM,OAAO,GAAG,CAAC,GAAW,EAAQ,EAAE;IAClC,IAAI,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO;IACX,CAAC;AACL,CAAC,CAAC;AAEF,SAAS,mBAAmB,CAAC,MAAc;IACvC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,eAAe,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAEhC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO;QACX,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;IACL,CAAC;AACL,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC","sourcesContent":["import { readdirSync, readFileSync } from \"node:fs\";\n\nconst PROCESS_MARKER = \"GTKX_PROCESS_GUARD\";\nconst MAX_KILL_PASSES = 8;\n\nconst isMarked = (pid: number, prefix: string): boolean => {\n try {\n return readFileSync(`/proc/${String(pid)}/environ`, \"utf8\")\n .split(\"\\0\")\n .some((assignment) => assignment.startsWith(prefix));\n } catch {\n return false;\n }\n};\n\nconst markedPids = (prefix: string): number[] =>\n readdirSync(\"/proc\")\n .map(Number)\n .filter((pid) => Number.isSafeInteger(pid) && pid > 1 && pid !== process.pid)\n .filter((pid) => isMarked(pid, prefix));\n\nconst killPid = (pid: number): void => {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n return;\n }\n};\n\nfunction killMarkedProcesses(prefix: string): void {\n for (let pass = 0; pass < MAX_KILL_PASSES; pass += 1) {\n const pids = markedPids(prefix);\n\n if (pids.length === 0) {\n return;\n }\n\n for (const pid of pids) {\n killPid(pid);\n }\n }\n}\n\nexport { PROCESS_MARKER, killMarkedProcesses };\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-guard.d.ts","sourceRoot":"","sources":["../../src/process/process-guard.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { killMarkedProcesses } from "./kill-marked-processes.js";
|
|
2
|
+
const GUARD_PREFIX = process.argv[2] ?? "";
|
|
3
|
+
const WATCHED_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];
|
|
4
|
+
const sweep = () => {
|
|
5
|
+
if (GUARD_PREFIX.length > 0) {
|
|
6
|
+
killMarkedProcesses(GUARD_PREFIX);
|
|
7
|
+
}
|
|
8
|
+
process.exit(0);
|
|
9
|
+
};
|
|
10
|
+
process.stdin.resume();
|
|
11
|
+
process.stdin.on("end", sweep);
|
|
12
|
+
process.stdin.on("error", sweep);
|
|
13
|
+
for (const signal of WATCHED_SIGNALS) {
|
|
14
|
+
process.on(signal, sweep);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=process-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-guard.js","sourceRoot":"","sources":["../../src/process/process-guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3C,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAqC,CAAC;AAE5F,MAAM,KAAK,GAAG,GAAS,EAAE;IACrB,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,mBAAmB,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC;AAEF,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;AACvB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC/B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAEjC,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC","sourcesContent":["import { killMarkedProcesses } from \"./kill-marked-processes.ts\";\n\nconst GUARD_PREFIX = process.argv[2] ?? \"\";\nconst WATCHED_SIGNALS = [\"SIGTERM\", \"SIGINT\", \"SIGHUP\"] as const satisfies NodeJS.Signals[];\n\nconst sweep = (): void => {\n if (GUARD_PREFIX.length > 0) {\n killMarkedProcesses(GUARD_PREFIX);\n }\n\n process.exit(0);\n};\n\nprocess.stdin.resume();\nprocess.stdin.on(\"end\", sweep);\nprocess.stdin.on(\"error\", sweep);\n\nfor (const signal of WATCHED_SIGNALS) {\n process.on(signal, sweep);\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
declare function tryResolveExecutable(command: string): string | undefined;
|
|
1
2
|
declare function resolveExecutable(command: string): string;
|
|
2
|
-
export { resolveExecutable };
|
|
3
|
+
export { resolveExecutable, tryResolveExecutable };
|
|
3
4
|
//# sourceMappingURL=resolve-executable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-executable.d.ts","sourceRoot":"","sources":["../../src/process/resolve-executable.ts"],"names":[],"mappings":"AA2BA,iBAAS,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"resolve-executable.d.ts","sourceRoot":"","sources":["../../src/process/resolve-executable.ts"],"names":[],"mappings":"AA2BA,iBAAS,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUjE;AAED,iBAAS,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -19,18 +19,21 @@ const findOnPath = (command) => {
|
|
|
19
19
|
}
|
|
20
20
|
return undefined;
|
|
21
21
|
};
|
|
22
|
-
function
|
|
22
|
+
function tryResolveExecutable(command) {
|
|
23
23
|
if (isAbsolute(command)) {
|
|
24
24
|
return command;
|
|
25
25
|
}
|
|
26
26
|
if (command.includes("/")) {
|
|
27
27
|
return resolve(command);
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
return findOnPath(command);
|
|
30
|
+
}
|
|
31
|
+
function resolveExecutable(command) {
|
|
32
|
+
const found = tryResolveExecutable(command);
|
|
30
33
|
if (found === undefined) {
|
|
31
34
|
throw new Error(`Cannot find the "${command}" executable on PATH`);
|
|
32
35
|
}
|
|
33
36
|
return found;
|
|
34
37
|
}
|
|
35
|
-
export { resolveExecutable };
|
|
38
|
+
export { resolveExecutable, tryResolveExecutable };
|
|
36
39
|
//# sourceMappingURL=resolve-executable.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-executable.js","sourceRoot":"","sources":["../../src/process/resolve-executable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEjE,MAAM,YAAY,GAAG,CAAC,IAAY,EAAW,EAAE;IAC3C,IAAI,CAAC;QACD,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QAEjC,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAe,EAAsB,EAAE;IACvD,MAAM,WAAW,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAElG,KAAK,MAAM,SAAS,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE3C,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,OAAO,SAAS,CAAC;QACrB,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC,CAAC;AAEF,SAAS,
|
|
1
|
+
{"version":3,"file":"resolve-executable.js","sourceRoot":"","sources":["../../src/process/resolve-executable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEjE,MAAM,YAAY,GAAG,CAAC,IAAY,EAAW,EAAE;IAC3C,IAAI,CAAC;QACD,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QAEjC,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAe,EAAsB,EAAE;IACvD,MAAM,WAAW,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAElG,KAAK,MAAM,SAAS,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE3C,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,OAAO,SAAS,CAAC;QACrB,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC,CAAC;AAEF,SAAS,oBAAoB,CAAC,OAAe;IACzC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe;IACtC,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAE5C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,sBAAsB,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,CAAC","sourcesContent":["import { accessSync, constants } from \"node:fs\";\nimport { delimiter, isAbsolute, join, resolve } from \"node:path\";\n\nconst isExecutable = (path: string): boolean => {\n try {\n accessSync(path, constants.X_OK);\n\n return true;\n } catch {\n return false;\n }\n};\n\nconst findOnPath = (command: string): string | undefined => {\n const searchPaths = (process.env.PATH ?? \"\").split(delimiter).filter((entry) => entry.length > 0);\n\n for (const directory of searchPaths) {\n const candidate = join(directory, command);\n\n if (isExecutable(candidate)) {\n return candidate;\n }\n }\n\n return undefined;\n};\n\nfunction tryResolveExecutable(command: string): string | undefined {\n if (isAbsolute(command)) {\n return command;\n }\n\n if (command.includes(\"/\")) {\n return resolve(command);\n }\n\n return findOnPath(command);\n}\n\nfunction resolveExecutable(command: string): string {\n const found = tryResolveExecutable(command);\n\n if (found === undefined) {\n throw new Error(`Cannot find the \"${command}\" executable on PATH`);\n }\n\n return found;\n}\n\nexport { resolveExecutable, tryResolveExecutable };\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ChildProcess, type StdioOptions } from "node:child_process";
|
|
2
|
+
type ParentDeathSpawnOptions = {
|
|
3
|
+
stdio?: StdioOptions;
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
};
|
|
6
|
+
declare function spawnWithParentDeathSignal(command: string, args: string[], options?: ParentDeathSpawnOptions): ChildProcess;
|
|
7
|
+
export { spawnWithParentDeathSignal };
|
|
8
|
+
//# sourceMappingURL=spawn-with-parent-death-signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-with-parent-death-signal.d.ts","sourceRoot":"","sources":["../../src/process/spawn-with-parent-death-signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQjF,KAAK,uBAAuB,GAAG;IAC3B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAC3B,CAAC;AAuCF,iBAAS,0BAA0B,CAC/B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,uBAA4B,GACtC,YAAY,CAiBd;AAED,OAAO,EAAE,0BAA0B,EAAE,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { Socket } from "node:net";
|
|
4
|
+
import { dirname, extname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { killMarkedProcesses, PROCESS_MARKER } from "./kill-marked-processes.js";
|
|
7
|
+
import { resolveExecutable } from "./resolve-executable.js";
|
|
8
|
+
const MODULE_PATH = fileURLToPath(import.meta.url);
|
|
9
|
+
const GUARD_PATH = join(dirname(MODULE_PATH), `process-guard${extname(MODULE_PATH)}`);
|
|
10
|
+
const RUN_ID = randomBytes(8).toString("hex");
|
|
11
|
+
const RUN_PREFIX = `${PROCESS_MARKER}=${RUN_ID}/`;
|
|
12
|
+
const guard = {};
|
|
13
|
+
const releaseGuard = (child) => {
|
|
14
|
+
child.unref();
|
|
15
|
+
const { stdin } = child;
|
|
16
|
+
if (stdin instanceof Socket) {
|
|
17
|
+
stdin.unref();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const startGuard = () => {
|
|
21
|
+
if (guard.child) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const child = spawn(process.execPath, [GUARD_PATH, RUN_PREFIX], {
|
|
25
|
+
detached: true,
|
|
26
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
27
|
+
});
|
|
28
|
+
child.on("error", () => {
|
|
29
|
+
guard.child = undefined;
|
|
30
|
+
});
|
|
31
|
+
guard.child = child;
|
|
32
|
+
releaseGuard(child);
|
|
33
|
+
};
|
|
34
|
+
function spawnWithParentDeathSignal(command, args, options = {}) {
|
|
35
|
+
const executable = resolveExecutable(command);
|
|
36
|
+
const setpriv = resolveExecutable("setpriv");
|
|
37
|
+
const jobValue = `${RUN_ID}/${randomBytes(4).toString("hex")}`;
|
|
38
|
+
startGuard();
|
|
39
|
+
const child = spawn(setpriv, ["--pdeathsig", "SIGKILL", executable, ...args], {
|
|
40
|
+
detached: true,
|
|
41
|
+
stdio: options.stdio ?? "ignore",
|
|
42
|
+
env: { ...(options.env ?? process.env), [PROCESS_MARKER]: jobValue },
|
|
43
|
+
});
|
|
44
|
+
child.on("exit", () => {
|
|
45
|
+
killMarkedProcesses(`${PROCESS_MARKER}=${jobValue}`);
|
|
46
|
+
});
|
|
47
|
+
return child;
|
|
48
|
+
}
|
|
49
|
+
export { spawnWithParentDeathSignal };
|
|
50
|
+
//# sourceMappingURL=spawn-with-parent-death-signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-with-parent-death-signal.js","sourceRoot":"","sources":["../../src/process/spawn-with-parent-death-signal.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn, type StdioOptions } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport { Socket } from \"node:net\";\nimport { dirname, extname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { killMarkedProcesses, PROCESS_MARKER } from \"./kill-marked-processes.ts\";\nimport { resolveExecutable } from \"./resolve-executable.ts\";\n\ntype ParentDeathSpawnOptions = {\n stdio?: StdioOptions;\n env?: NodeJS.ProcessEnv;\n};\n\ntype GuardState = {\n child?: ChildProcess | undefined;\n};\n\nconst MODULE_PATH = fileURLToPath(import.meta.url);\nconst GUARD_PATH = join(dirname(MODULE_PATH), `process-guard${extname(MODULE_PATH)}`);\nconst RUN_ID = randomBytes(8).toString(\"hex\");\nconst RUN_PREFIX = `${PROCESS_MARKER}=${RUN_ID}/`;\nconst guard: GuardState = {};\n\nconst releaseGuard = (child: ChildProcess): void => {\n child.unref();\n const { stdin } = child;\n\n if (stdin instanceof Socket) {\n stdin.unref();\n }\n};\n\nconst startGuard = (): void => {\n if (guard.child) {\n return;\n }\n\n const child = spawn(process.execPath, [GUARD_PATH, RUN_PREFIX], {\n detached: true,\n stdio: [\"pipe\", \"ignore\", \"ignore\"],\n });\n\n child.on(\"error\", () => {\n guard.child = undefined;\n });\n\n guard.child = child;\n releaseGuard(child);\n};\n\nfunction spawnWithParentDeathSignal(\n command: string,\n args: string[],\n options: ParentDeathSpawnOptions = {},\n): ChildProcess {\n const executable = resolveExecutable(command);\n const setpriv = resolveExecutable(\"setpriv\");\n const jobValue = `${RUN_ID}/${randomBytes(4).toString(\"hex\")}`;\n startGuard();\n\n const child = spawn(setpriv, [\"--pdeathsig\", \"SIGKILL\", executable, ...args], {\n detached: true,\n stdio: options.stdio ?? \"ignore\",\n env: { ...(options.env ?? process.env), [PROCESS_MARKER]: jobValue },\n });\n\n child.on(\"exit\", () => {\n killMarkedProcesses(`${PROCESS_MARKER}=${jobValue}`);\n });\n\n return child;\n}\n\nexport { spawnWithParentDeathSignal };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-version.d.ts","sourceRoot":"","sources":["../../src/util/package-version.ts"],"names":[],"mappings":"AAEA,iBAAS,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"package-version.d.ts","sourceRoot":"","sources":["../../src/util/package-version.ts"],"names":[],"mappings":"AAEA,iBAAS,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
function packageVersion(importMetaUrl) {
|
|
3
|
-
return createRequire(importMetaUrl)(
|
|
2
|
+
function packageVersion(importMetaUrl, specifier) {
|
|
3
|
+
return createRequire(importMetaUrl)(specifier).version;
|
|
4
4
|
}
|
|
5
5
|
export { packageVersion };
|
|
6
6
|
//# sourceMappingURL=package-version.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-version.js","sourceRoot":"","sources":["../../src/util/package-version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,SAAS,cAAc,CAAC,aAAqB;
|
|
1
|
+
{"version":3,"file":"package-version.js","sourceRoot":"","sources":["../../src/util/package-version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,SAAS,cAAc,CAAC,aAAqB,EAAE,SAAiB;IAC5D,OAAQ,aAAa,CAAC,aAAa,CAAC,CAAC,SAAS,CAAyB,CAAC,OAAO,CAAC;AACpF,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,CAAC","sourcesContent":["import { createRequire } from \"node:module\";\n\nfunction packageVersion(importMetaUrl: string, specifier: string): string {\n return (createRequire(importMetaUrl)(specifier) as { version: string }).version;\n}\n\nexport { packageVersion };\n"]}
|
package/package.json
CHANGED
package/src/class/any-class.ts
CHANGED
package/src/map/get-or-insert.ts
CHANGED
package/src/process/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { exitCodeForSignal } from "./exit-code-for-signal.ts";
|
|
2
2
|
export { installGracefulShutdown } from "./install-graceful-shutdown.ts";
|
|
3
|
-
export { resolveExecutable } from "./resolve-executable.ts";
|
|
3
|
+
export { resolveExecutable, tryResolveExecutable } from "./resolve-executable.ts";
|
|
4
|
+
export { spawnWithParentDeathSignal } from "./spawn-with-parent-death-signal.ts";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
const PROCESS_MARKER = "GTKX_PROCESS_GUARD";
|
|
4
|
+
const MAX_KILL_PASSES = 8;
|
|
5
|
+
|
|
6
|
+
const isMarked = (pid: number, prefix: string): boolean => {
|
|
7
|
+
try {
|
|
8
|
+
return readFileSync(`/proc/${String(pid)}/environ`, "utf8")
|
|
9
|
+
.split("\0")
|
|
10
|
+
.some((assignment) => assignment.startsWith(prefix));
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const markedPids = (prefix: string): number[] =>
|
|
17
|
+
readdirSync("/proc")
|
|
18
|
+
.map(Number)
|
|
19
|
+
.filter((pid) => Number.isSafeInteger(pid) && pid > 1 && pid !== process.pid)
|
|
20
|
+
.filter((pid) => isMarked(pid, prefix));
|
|
21
|
+
|
|
22
|
+
const killPid = (pid: number): void => {
|
|
23
|
+
try {
|
|
24
|
+
process.kill(pid, "SIGKILL");
|
|
25
|
+
} catch {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function killMarkedProcesses(prefix: string): void {
|
|
31
|
+
for (let pass = 0; pass < MAX_KILL_PASSES; pass += 1) {
|
|
32
|
+
const pids = markedPids(prefix);
|
|
33
|
+
|
|
34
|
+
if (pids.length === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const pid of pids) {
|
|
39
|
+
killPid(pid);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { PROCESS_MARKER, killMarkedProcesses };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { killMarkedProcesses } from "./kill-marked-processes.ts";
|
|
2
|
+
|
|
3
|
+
const GUARD_PREFIX = process.argv[2] ?? "";
|
|
4
|
+
const WATCHED_SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"] as const satisfies NodeJS.Signals[];
|
|
5
|
+
|
|
6
|
+
const sweep = (): void => {
|
|
7
|
+
if (GUARD_PREFIX.length > 0) {
|
|
8
|
+
killMarkedProcesses(GUARD_PREFIX);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
process.exit(0);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
process.stdin.resume();
|
|
15
|
+
process.stdin.on("end", sweep);
|
|
16
|
+
process.stdin.on("error", sweep);
|
|
17
|
+
|
|
18
|
+
for (const signal of WATCHED_SIGNALS) {
|
|
19
|
+
process.on(signal, sweep);
|
|
20
|
+
}
|
|
@@ -25,7 +25,7 @@ const findOnPath = (command: string): string | undefined => {
|
|
|
25
25
|
return undefined;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
function
|
|
28
|
+
function tryResolveExecutable(command: string): string | undefined {
|
|
29
29
|
if (isAbsolute(command)) {
|
|
30
30
|
return command;
|
|
31
31
|
}
|
|
@@ -34,7 +34,11 @@ function resolveExecutable(command: string): string {
|
|
|
34
34
|
return resolve(command);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
return findOnPath(command);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveExecutable(command: string): string {
|
|
41
|
+
const found = tryResolveExecutable(command);
|
|
38
42
|
|
|
39
43
|
if (found === undefined) {
|
|
40
44
|
throw new Error(`Cannot find the "${command}" executable on PATH`);
|
|
@@ -43,4 +47,4 @@ function resolveExecutable(command: string): string {
|
|
|
43
47
|
return found;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
|
-
export { resolveExecutable };
|
|
50
|
+
export { resolveExecutable, tryResolveExecutable };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ChildProcess, spawn, type StdioOptions } from "node:child_process";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { Socket } from "node:net";
|
|
4
|
+
import { dirname, extname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { killMarkedProcesses, PROCESS_MARKER } from "./kill-marked-processes.ts";
|
|
7
|
+
import { resolveExecutable } from "./resolve-executable.ts";
|
|
8
|
+
|
|
9
|
+
type ParentDeathSpawnOptions = {
|
|
10
|
+
stdio?: StdioOptions;
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type GuardState = {
|
|
15
|
+
child?: ChildProcess | undefined;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const MODULE_PATH = fileURLToPath(import.meta.url);
|
|
19
|
+
const GUARD_PATH = join(dirname(MODULE_PATH), `process-guard${extname(MODULE_PATH)}`);
|
|
20
|
+
const RUN_ID = randomBytes(8).toString("hex");
|
|
21
|
+
const RUN_PREFIX = `${PROCESS_MARKER}=${RUN_ID}/`;
|
|
22
|
+
const guard: GuardState = {};
|
|
23
|
+
|
|
24
|
+
const releaseGuard = (child: ChildProcess): void => {
|
|
25
|
+
child.unref();
|
|
26
|
+
const { stdin } = child;
|
|
27
|
+
|
|
28
|
+
if (stdin instanceof Socket) {
|
|
29
|
+
stdin.unref();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const startGuard = (): void => {
|
|
34
|
+
if (guard.child) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const child = spawn(process.execPath, [GUARD_PATH, RUN_PREFIX], {
|
|
39
|
+
detached: true,
|
|
40
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
child.on("error", () => {
|
|
44
|
+
guard.child = undefined;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
guard.child = child;
|
|
48
|
+
releaseGuard(child);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function spawnWithParentDeathSignal(
|
|
52
|
+
command: string,
|
|
53
|
+
args: string[],
|
|
54
|
+
options: ParentDeathSpawnOptions = {},
|
|
55
|
+
): ChildProcess {
|
|
56
|
+
const executable = resolveExecutable(command);
|
|
57
|
+
const setpriv = resolveExecutable("setpriv");
|
|
58
|
+
const jobValue = `${RUN_ID}/${randomBytes(4).toString("hex")}`;
|
|
59
|
+
startGuard();
|
|
60
|
+
|
|
61
|
+
const child = spawn(setpriv, ["--pdeathsig", "SIGKILL", executable, ...args], {
|
|
62
|
+
detached: true,
|
|
63
|
+
stdio: options.stdio ?? "ignore",
|
|
64
|
+
env: { ...(options.env ?? process.env), [PROCESS_MARKER]: jobValue },
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
child.on("exit", () => {
|
|
68
|
+
killMarkedProcesses(`${PROCESS_MARKER}=${jobValue}`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return child;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { spawnWithParentDeathSignal };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
|
|
3
|
-
function packageVersion(importMetaUrl: string): string {
|
|
4
|
-
return (createRequire(importMetaUrl)(
|
|
3
|
+
function packageVersion(importMetaUrl: string, specifier: string): string {
|
|
4
|
+
return (createRequire(importMetaUrl)(specifier) as { version: string }).version;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export { packageVersion };
|