@neotales/is-elevated 0.0.0 → 0.1.0-alpha.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 CHANGED
@@ -5,17 +5,17 @@
5
5
  `@neotales/is-elevated` detects whether the current process is running with elevated privileges.
6
6
  On Unix-like systems this typically means root. On Windows it checks whether the current token is elevated.
7
7
 
8
- ![logo](https://raw.githubusercontent.com/neotales/js-std/refs/heads/dev/eng/assets/logo.png)
8
+ ![logo](https://raw.githubusercontent.com/neotales/js-lib/refs/heads/dev/eng/assets/logo.png)
9
9
 
10
10
  [![JSR](https://jsr.io/badges/@neotales/is-elevated)](https://jsr.io/@neotales/is-elevated)
11
11
  [![npm version](https://badge.fury.io/js/@neotales%2Fis-elevated.svg)](https://badge.fury.io/js/@neotales%2Fis-elevated)
12
- [![GitHub version](https://badge.fury.io/gh/neotales%2Fjs-os.svg)](https://badge.fury.io/gh/neotales%2Fjs-os)
12
+ [![GitHub version](https://badge.fury.io/gh/neotales%2Fjs-lib.svg)](https://badge.fury.io/gh/neotales%2Fjs-lib)
13
13
 
14
14
  ## Documentation
15
15
 
16
16
  Documentation is available on [jsr.io](https://jsr.io/@neotales/is-elevated/doc).
17
17
 
18
- A list of other modules can be found at [github.com/neotales/js-os](https://github.com/neotales/js-os).
18
+ [Module list](https://github.com/neotales/js-lib)..
19
19
 
20
20
  ## Installation
21
21
 
@@ -25,6 +25,25 @@ npx jsr add @neotales/is-elevated
25
25
  npm install @neotales/is-elevated
26
26
  ```
27
27
 
28
+ ### Runtime support
29
+
30
+ The module README table is the support claim. A browser can still load `is-elevated`. That is not support: `isElevatedAvailable()` is false there, and `isElevated()` throws.
31
+
32
+ | Runtime | Supported | Generated from the TypeScript |
33
+ | --- | --- | --- |
34
+ | Deno | ✅ | source; requires --allow-sys and --allow-ffi |
35
+ | Node.js | ✅ | published npm ESM twin (node >= 22) |
36
+ | Bun | ✅ | source; Bun rewrites @neotales/ imports to relative paths |
37
+ | Ende | ✅ | source; requires --allow-sys |
38
+ | QuickJS-ng | ❌ | one ESM bundle |
39
+ | workerd | ✅ | one ESM bundle, imported from the worker |
40
+ | txiki.js | ✅ | one ESM bundle, run with `tjs run`; Windows backend is `tjs:ffi` |
41
+ | ClearScript | ❌ | TypeScript graph transpiled to .js, then loaded by the host |
42
+ | browsers | ❌ | one ESM bundle, no syntax rewrite; leaves ffi_*.ts out; node:module and bun:ffi are not loaded |
43
+ | JerryScript | ❌ | no bundle; FFI files import host modules the IIFE cannot include |
44
+
45
+ Load instructions: [runtime documentation](../../../docs/README.md).
46
+
28
47
  ## Usage
29
48
 
30
49
  ```typescript
@@ -37,9 +56,10 @@ if (!isElevated()) {
37
56
 
38
57
  ## Exports
39
58
 
40
- | Export | Subpath | Description |
41
- | ------------ | ----------------------- | ------------------------------------------------ |
42
- | `isElevated` | `@neotales/is-elevated` | Detects whether the current process is elevated. |
59
+ | Export | Subpath | Description |
60
+ | --------------------- | ----------------------- | ------------------------------------------------------------ |
61
+ | `isElevated` | `@neotales/is-elevated` | Detects whether the current process is elevated. |
62
+ | `isElevatedAvailable` | `@neotales/is-elevated` | Reports whether elevation detection can run in this runtime. |
43
63
 
44
64
  ```typescript
45
65
  import { isElevated } from "@neotales/is-elevated";
@@ -53,29 +73,15 @@ if (elevated) {
53
73
 
54
74
  ## Elevation Detection
55
75
 
56
- On Unix-like systems, elevation means an effective user ID of `0`. Node and Bun check `process.geteuid()` when available, then fall back to `process.getuid()`. The result is cached so repeated checks do not need to query the runtime again.
57
-
58
- On Windows, the package opens the current process token and calls `GetTokenInformation` with `TokenElevation`. Node uses native `node:ffi` when available and otherwise the optional `koffi` dependency; Bun and Deno use their native FFI implementations. The FFI implementations are loaded only on Windows.
59
-
60
- This intentionally does not use `Shell32.IsUserAnAdmin`. That API checks administrator-group membership rather than the current process token, so it can disagree under User Account Control when an administrator account is running with a filtered, non-elevated token. `TokenElevation` reports the process state directly.
61
-
62
- ## Runtime Support
76
+ On Unix-like systems, elevation means an effective user ID of `0`. The module uses an effective ID when the runtime exposes one and otherwise falls back to `Deno.uid()`. This keeps the result tied to the identity under which the process is actually executing.
63
77
 
64
- This npm package supports Node, Bun, and Deno. Deno users can import it with
65
- `npm:@neotales/is-elevated`. Call `isElevatedAvailable()` before relying on
66
- Windows elevation detection when the runtime's FFI configuration may be unknown.
78
+ On Windows, the module opens the current process token and calls `GetTokenInformation` with `TokenElevation`. The returned `TokenIsElevated` value describes the current process token, which is the relevant answer for an operation that needs administrator privileges.
67
79
 
68
- - **Deno**: run with `--allow-ffi`.
69
- - **Bun**: the native `bun:ffi` backend is selected automatically.
70
- - **Node.js**: see [Node.js FFI](#nodejs-ffi).
80
+ This intentionally does not use `Shell32.IsUserAnAdmin`. That API is a convenience membership check and can disagree with the current token under User Account Control: an administrator account may be running with a filtered, non-elevated token. Token elevation inspection reports the process state directly.
71
81
 
72
82
  ## Node.js FFI
73
83
 
74
- On Windows, Node.js requires a native FFI backend. Run Node >= 26 with
75
- `--experimental-ffi` to enable `node:ffi`, or install koffi with
76
- `npm install koffi` when optional dependencies were omitted or unavailable.
77
- When neither is available, `isElevatedAvailable()` returns `false` and
78
- `isElevated()` throws an error that links to this section.
84
+ On Windows, Node uses `node:ffi` when the module loads. It was added in 26.1.0, is still experimental, and requires `--experimental-ffi`. If it does not load, the same call goes through koffi. The generated npm package declares koffi `^2.9.0` as an optional dependency. When neither is available, `isElevatedAvailable()` returns `false` and `isElevated()` throws.
79
85
 
80
86
  ## License
81
87
 
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Based on [import-meta-ponyfill](https://github.com/gaubee/import-meta-ponyfill),
3
+ * but instead of using npm to install additional dependencies,
4
+ * this approach manually consolidates cjs/mjs/d.ts into a single file.
5
+ *
6
+ * Note that this code might be imported multiple times
7
+ * (for example, both dnt.test.polyfills.ts and dnt.polyfills.ts contain this code;
8
+ * or Node.js might dynamically clear the cache and then force a require).
9
+ * Therefore, it's important to avoid redundant writes to global objects.
10
+ * Additionally, consider that commonjs is used alongside esm,
11
+ * so the two ponyfill functions are stored independently in two separate global objects.
12
+ */
13
+ //@ts-ignore
14
+ import { createRequire } from "node:module";
15
+ //@ts-ignore
16
+ import { fileURLToPath, pathToFileURL } from "node:url";
17
+ //@ts-ignore
18
+ import { dirname } from "node:path";
19
+ const defineGlobalPonyfill = (symbolFor, fn) => {
20
+ if (!Reflect.has(globalThis, Symbol.for(symbolFor))) {
21
+ Object.defineProperty(globalThis, Symbol.for(symbolFor), {
22
+ configurable: true,
23
+ get() {
24
+ return fn;
25
+ },
26
+ });
27
+ }
28
+ };
29
+ export let import_meta_ponyfill_commonjs = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-commonjs")) ??
30
+ (() => {
31
+ const moduleImportMetaWM = new WeakMap();
32
+ return (require, module) => {
33
+ let importMetaCache = moduleImportMetaWM.get(module);
34
+ if (importMetaCache == null) {
35
+ const importMeta = Object.assign(Object.create(null), {
36
+ url: pathToFileURL(module.filename).href,
37
+ main: require.main == module,
38
+ resolve: (specifier, parentURL = importMeta.url) => {
39
+ return pathToFileURL((importMeta.url === parentURL
40
+ ? require
41
+ : createRequire(parentURL))
42
+ .resolve(specifier)).href;
43
+ },
44
+ filename: module.filename,
45
+ dirname: module.path,
46
+ });
47
+ moduleImportMetaWM.set(module, importMeta);
48
+ importMetaCache = importMeta;
49
+ }
50
+ return importMetaCache;
51
+ };
52
+ })());
53
+ defineGlobalPonyfill("import-meta-ponyfill-commonjs", import_meta_ponyfill_commonjs);
54
+ export let import_meta_ponyfill_esmodule = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-esmodule")) ??
55
+ ((importMeta) => {
56
+ const resolveFunStr = String(importMeta.resolve);
57
+ const shimWs = new WeakSet();
58
+ //@ts-ignore
59
+ const mainUrl = ("file:///" + process.argv[1].replace(/\\/g, "/"))
60
+ .replace(/\/{3,}/, "///");
61
+ const commonShim = (importMeta) => {
62
+ if (typeof importMeta.main !== "boolean") {
63
+ importMeta.main = importMeta.url === mainUrl;
64
+ }
65
+ if (typeof importMeta.filename !== "string") {
66
+ importMeta.filename = fileURLToPath(importMeta.url);
67
+ importMeta.dirname = dirname(importMeta.filename);
68
+ }
69
+ };
70
+ if (
71
+ // v16.2.0+, v14.18.0+: Add support for WHATWG URL object to parentURL parameter.
72
+ resolveFunStr === "undefined" ||
73
+ // v20.0.0+, v18.19.0+"" This API now returns a string synchronously instead of a Promise.
74
+ resolveFunStr.startsWith("async")
75
+ // enable by --experimental-import-meta-resolve flag
76
+ ) {
77
+ import_meta_ponyfill_esmodule = (importMeta) => {
78
+ if (!shimWs.has(importMeta)) {
79
+ shimWs.add(importMeta);
80
+ const importMetaUrlRequire = {
81
+ url: importMeta.url,
82
+ require: createRequire(importMeta.url),
83
+ };
84
+ // note: the parameter types must be explicitly annotated because
85
+ // `@types/node` also declares `ImportMeta.resolve`, which makes
86
+ // the contextual type an overload set that provides no inference
87
+ importMeta.resolve = function resolve(specifier, parentURL = importMeta.url) {
88
+ return pathToFileURL((importMetaUrlRequire.url === parentURL
89
+ ? importMetaUrlRequire.require
90
+ : createRequire(parentURL)).resolve(specifier)).href;
91
+ };
92
+ commonShim(importMeta);
93
+ }
94
+ return importMeta;
95
+ };
96
+ }
97
+ else {
98
+ /// native support
99
+ import_meta_ponyfill_esmodule = (importMeta) => {
100
+ if (!shimWs.has(importMeta)) {
101
+ shimWs.add(importMeta);
102
+ commonShim(importMeta);
103
+ }
104
+ return importMeta;
105
+ };
106
+ }
107
+ return import_meta_ponyfill_esmodule(importMeta);
108
+ }));
109
+ defineGlobalPonyfill("import-meta-ponyfill-esmodule", import_meta_ponyfill_esmodule);
110
+ export let import_meta_ponyfill = ((...args) => {
111
+ const _MODULE = (() => {
112
+ if (typeof require === "function" && typeof module === "object") {
113
+ return "commonjs";
114
+ }
115
+ else {
116
+ // eval("typeof import.meta");
117
+ return "esmodule";
118
+ }
119
+ })();
120
+ if (_MODULE === "commonjs") {
121
+ //@ts-ignore
122
+ import_meta_ponyfill = (r, m) => import_meta_ponyfill_commonjs(r, m);
123
+ }
124
+ else {
125
+ //@ts-ignore
126
+ import_meta_ponyfill = (im) => import_meta_ponyfill_esmodule(im);
127
+ }
128
+ //@ts-ignore
129
+ return import_meta_ponyfill(...args);
130
+ });
package/esm/ffi_bun.js CHANGED
@@ -1,22 +1,28 @@
1
- /// <reference types="@types/bun" />
2
- import { dlopen, FFIType, ptr } from "bun:ffi";
1
+ /**
2
+ * Implements native Windows elevation detection through Bun FFI.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_bun
5
+ * @internal
6
+ */
7
+ const specifier = "bun:ffi";
8
+ let ffi;
9
+ try {
10
+ ffi = await import(/* @vite-ignore */ specifier);
11
+ }
12
+ catch (cause) {
13
+ throw new Error("Unable to load bun:ffi. See https://github.com/neotales/js-lib/blob/dev/os/jsr/is-elevated/README.md#runtime-support", { cause });
14
+ }
3
15
  let elevated;
4
16
  /**
5
17
  * Reports whether the current process is running with elevated privileges.
6
18
  *
7
19
  * @param cache - Whether to reuse the result from the first evaluation.
8
20
  * @returns Whether the process has elevated privileges.
9
- * @example
10
- * ```ts
11
- * import { evalIsProcessElevated } from "./ffi_bun.js";
12
- *
13
- * const elevated = evalIsProcessElevated();
14
- * ```
15
21
  */
16
22
  export function evalIsProcessElevated(cache = true) {
17
- if (cache && elevated !== undefined) {
23
+ if (cache && elevated !== undefined)
18
24
  return elevated;
19
- }
25
+ const { FFIType, dlopen, ptr } = ffi;
20
26
  const advapi32 = dlopen("Advapi32.dll", {
21
27
  OpenProcessToken: { args: [FFIType.ptr, FFIType.u32, FFIType.ptr], returns: FFIType.bool },
22
28
  GetTokenInformation: {
@@ -33,15 +39,15 @@ export function evalIsProcessElevated(cache = true) {
33
39
  const TOKEN_QUERY = 0x0008;
34
40
  const TOKEN_ELEVATION = 20;
35
41
  const tokenHandle = new BigUint64Array(1);
36
- const success = advapi32.symbols.OpenProcessToken(kernel32.symbols.GetCurrentProcess(), TOKEN_QUERY, ptr(tokenHandle));
37
- if (!success)
42
+ const opened = advapi32.symbols.OpenProcessToken(kernel32.symbols.GetCurrentProcess(), TOKEN_QUERY, ptr(tokenHandle));
43
+ if (!opened)
38
44
  throw new Error("Failed to open process token");
39
45
  try {
40
46
  const tokenInfo = new Uint8Array(4);
41
47
  const returnLength = new Uint32Array(1);
42
48
  const result = advapi32.symbols.GetTokenInformation(tokenHandle[0], TOKEN_ELEVATION, ptr(tokenInfo), 4, ptr(returnLength));
43
49
  if (!result)
44
- throw new Error(`Failed to get token information ${kernel32.symbols.GetLastError()}`);
50
+ throw new Error(`Failed to get token information (${kernel32.symbols.GetLastError()})`);
45
51
  elevated = tokenInfo[0] !== 0;
46
52
  return elevated;
47
53
  }
package/esm/ffi_deno.js CHANGED
@@ -4,7 +4,6 @@
4
4
  * @module @neotales/is-elevated/ffi_deno
5
5
  */
6
6
  let elevated;
7
- const deno = globalThis.Deno;
8
7
  /**
9
8
  * Reports whether the current process is running with elevated privileges.
10
9
  *
@@ -12,22 +11,27 @@ const deno = globalThis.Deno;
12
11
  * @returns Whether the process has elevated privileges.
13
12
  * @example
14
13
  * ```ts
15
- * import { evalIsProcessElevated } from "./ffi_deno.js";
14
+ * import { evalIsProcessElevated } from "./ffi_deno.ts";
16
15
  *
17
16
  * const elevated = evalIsProcessElevated();
18
17
  * ```
19
18
  */
20
19
  export function evalIsProcessElevated(cache = true) {
20
+ if (Deno.build.os !== "windows") {
21
+ if (!cache || elevated === undefined)
22
+ elevated = Deno.uid() === 0;
23
+ return elevated;
24
+ }
21
25
  if (cache && elevated !== undefined)
22
26
  return elevated;
23
- const advapi32 = deno.dlopen("Advapi32.dll", {
27
+ const advapi32 = Deno.dlopen("Advapi32.dll", {
24
28
  OpenProcessToken: { parameters: ["pointer", "u32", "pointer"], result: "bool" },
25
29
  GetTokenInformation: {
26
30
  parameters: ["u64", "u32", "pointer", "u32", "pointer"],
27
31
  result: "bool",
28
32
  },
29
33
  });
30
- const kernel32 = deno.dlopen("Kernel32.dll", {
34
+ const kernel32 = Deno.dlopen("Kernel32.dll", {
31
35
  GetCurrentProcess: { parameters: [], result: "pointer" },
32
36
  CloseHandle: { parameters: ["pointer"], result: "bool" },
33
37
  GetLastError: { parameters: [], result: "i32" },
@@ -37,21 +41,22 @@ export function evalIsProcessElevated(cache = true) {
37
41
  const TOKEN_ELEVATION = 20;
38
42
  const processHandle = kernel32.symbols.GetCurrentProcess();
39
43
  const tokenHandle = new BigUint64Array(1);
40
- const tokenHandlePtr = deno.UnsafePointer.of(tokenHandle);
44
+ const tokenHandlePtr = Deno.UnsafePointer.of(tokenHandle);
41
45
  const success = advapi32.symbols.OpenProcessToken(processHandle, TOKEN_QUERY, tokenHandlePtr);
42
- if (!success)
46
+ if (!success) {
43
47
  throw new Error("Failed to open process token");
48
+ }
44
49
  try {
45
50
  const tokenInfo = new Uint8Array(4);
46
51
  const returnLength = new Uint32Array(1);
47
- const result = advapi32.symbols.GetTokenInformation(tokenHandle[0], TOKEN_ELEVATION, deno.UnsafePointer.of(tokenInfo), 4, deno.UnsafePointer.of(returnLength));
52
+ const result = advapi32.symbols.GetTokenInformation(tokenHandle[0], TOKEN_ELEVATION, Deno.UnsafePointer.of(tokenInfo), 4, Deno.UnsafePointer.of(returnLength));
48
53
  if (!result)
49
54
  throw new Error("Failed to get token information " + kernel32.symbols.GetLastError());
50
55
  elevated = tokenInfo[0] !== 0;
51
56
  return elevated;
52
57
  }
53
58
  finally {
54
- kernel32.symbols.CloseHandle(deno.UnsafePointer.create(tokenHandle[0]));
59
+ kernel32.symbols.CloseHandle(Deno.UnsafePointer.create(tokenHandle[0]));
55
60
  }
56
61
  }
57
62
  finally {
package/esm/ffi_koffi.js CHANGED
@@ -1,7 +1,13 @@
1
- import process from "node:process";
2
- const { createRequire } = process.getBuiltinModule("node:module");
3
- const require = createRequire(import.meta.url);
4
- const koffi = require("koffi");
1
+ /**
2
+ * Windows elevation through koffi, used when node:ffi is not available.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_koffi
5
+ * @internal
6
+ */
7
+ import { createRequire } from "node:module";
8
+ const require = createRequire(globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url);
9
+ const specifier = "koffi";
10
+ const koffi = require(specifier);
5
11
  const advapi32 = koffi.load("Advapi32.dll");
6
12
  const kernel32 = koffi.load("Kernel32.dll");
7
13
  const OpenProcessToken = advapi32.func("int __stdcall OpenProcessToken(void *ProcessHandle, uint32 DesiredAccess, _Out_ void **TokenHandle)");
@@ -15,17 +21,10 @@ let elevated;
15
21
  *
16
22
  * @param cache - Whether to reuse the result from the first evaluation.
17
23
  * @returns Whether the process has elevated privileges.
18
- * @example
19
- * ```ts
20
- * import { evalIsProcessElevated } from "./ffi_koffi.js";
21
- *
22
- * const elevated = evalIsProcessElevated();
23
- * ```
24
24
  */
25
25
  export function evalIsProcessElevated(cache = true) {
26
- if (cache && elevated !== undefined) {
26
+ if (cache && elevated !== undefined)
27
27
  return elevated;
28
- }
29
28
  const TOKEN_QUERY = 0x0008;
30
29
  const TOKEN_ELEVATION = 20;
31
30
  const processHandle = GetCurrentProcess();
package/esm/ffi_node.js CHANGED
@@ -1,63 +1,61 @@
1
- import process from "node:process";
2
- const { createRequire } = process.getBuiltinModule("node:module");
3
- const require = createRequire(import.meta.url);
4
- const ffi = require("node:ffi");
5
- if (!ffi) {
6
- throw new Error();
1
+ /**
2
+ * Implements native Windows elevation detection through Node.js FFI.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_node
5
+ * @internal
6
+ */
7
+ import { createRequire } from "node:module";
8
+ const require = createRequire(globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url);
9
+ const specifier = "node:ffi";
10
+ let ffi;
11
+ try {
12
+ ffi = require(specifier);
13
+ }
14
+ catch (cause) {
15
+ throw new Error(`Unable to load ${specifier}. See https://github.com/neotales/js-lib/blob/dev/os/jsr/is-elevated/README.md#nodejs-ffi`, { cause });
7
16
  }
17
+ const advapi32 = ffi.dlopen("Advapi32.dll", {
18
+ OpenProcessToken: { arguments: ["pointer", "u32", "pointer"], return: "u8" },
19
+ GetTokenInformation: {
20
+ arguments: ["pointer", "u32", "pointer", "u32", "pointer"],
21
+ return: "u8",
22
+ },
23
+ });
24
+ const kernel32 = ffi.dlopen("Kernel32.dll", {
25
+ GetCurrentProcess: { arguments: [], return: "pointer" },
26
+ CloseHandle: { arguments: ["pointer"], return: "u8" },
27
+ GetLastError: { arguments: [], return: "u32" },
28
+ });
8
29
  let elevated;
9
30
  /**
10
31
  * Reports whether the current process is running with elevated privileges.
11
32
  *
12
33
  * @param cache - Whether to reuse the result from the first evaluation.
13
34
  * @returns Whether the process has elevated privileges.
14
- * @example
15
- * ```ts
16
- * import { evalIsProcessElevated } from "./ffi_node.js";
17
- *
18
- * const elevated = evalIsProcessElevated();
19
- * ```
20
35
  */
21
36
  export function evalIsProcessElevated(cache = true) {
22
- if (cache && elevated !== undefined) {
37
+ if (cache && elevated !== undefined)
23
38
  return elevated;
39
+ const TOKEN_QUERY = 0x0008;
40
+ const TOKEN_ELEVATION = 20;
41
+ const processHandle = kernel32.functions.GetCurrentProcess();
42
+ const tokenHandleBuffer = new Uint8Array(8);
43
+ const opened = advapi32.functions.OpenProcessToken(processHandle, TOKEN_QUERY, tokenHandleBuffer);
44
+ if (!opened) {
45
+ throw new Error(`Failed to open process token (${kernel32.functions.GetLastError()})`);
24
46
  }
25
- const advapi32 = ffi.dlopen("Advapi32.dll", {
26
- OpenProcessToken: { arguments: ["pointer", "u32", "pointer"], return: "u8" },
27
- GetTokenInformation: {
28
- arguments: ["pointer", "u32", "pointer", "u32", "pointer"],
29
- return: "u8",
30
- },
31
- });
32
- const kernel32 = ffi.dlopen("Kernel32.dll", {
33
- GetCurrentProcess: { arguments: [], return: "pointer" },
34
- CloseHandle: { arguments: ["pointer"], return: "u8" },
35
- GetLastError: { arguments: [], return: "u32" },
36
- });
47
+ const tokenHandle = new DataView(tokenHandleBuffer.buffer).getBigUint64(0, true);
37
48
  try {
38
- const TOKEN_QUERY = 0x0008;
39
- const TOKEN_ELEVATION = 20;
40
- const processHandle = kernel32.functions.GetCurrentProcess();
41
- const tokenHandleBuf = new Uint8Array(8);
42
- const success = advapi32.functions.OpenProcessToken(processHandle, TOKEN_QUERY, tokenHandleBuf);
43
- if (!success)
44
- throw new Error(`Failed to open process token (${kernel32.functions.GetLastError()})`);
45
- const tokenHandle = new DataView(tokenHandleBuf.buffer).getBigUint64(0, true);
46
- try {
47
- const tokenInfo = new Uint8Array(4);
48
- const returnLength = new Uint8Array(4);
49
- const result = advapi32.functions.GetTokenInformation(tokenHandle, TOKEN_ELEVATION, tokenInfo, 4, returnLength);
50
- if (!result)
51
- throw new Error(`Failed to get token information (${kernel32.functions.GetLastError()})`);
52
- elevated = tokenInfo[0] !== 0;
53
- return elevated;
54
- }
55
- finally {
56
- kernel32.functions.CloseHandle(tokenHandle);
49
+ const tokenInfo = new Uint8Array(4);
50
+ const returnLength = new Uint8Array(4);
51
+ const result = advapi32.functions.GetTokenInformation(tokenHandle, TOKEN_ELEVATION, tokenInfo, 4, returnLength);
52
+ if (!result) {
53
+ throw new Error(`Failed to get token information (${kernel32.functions.GetLastError()})`);
57
54
  }
55
+ elevated = tokenInfo[0] !== 0;
56
+ return elevated;
58
57
  }
59
58
  finally {
60
- advapi32.lib.close();
61
- kernel32.lib.close();
59
+ kernel32.functions.CloseHandle(tokenHandle);
62
60
  }
63
61
  }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Windows elevation through txiki.js `tjs:ffi`.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_txiki
5
+ * @internal
6
+ */
7
+ const specifier = "tjs:ffi";
8
+ let ffi;
9
+ try {
10
+ ffi = await import(/* @vite-ignore */ specifier);
11
+ }
12
+ catch (cause) {
13
+ throw new Error("Unable to load tjs:ffi. See https://github.com/neotales/js-lib/blob/dev/os/jsr/is-elevated/README.md#runtime-support", { cause });
14
+ }
15
+ let elevated;
16
+ /**
17
+ * Reports whether the current process is running with elevated privileges.
18
+ *
19
+ * @param cache - Whether to reuse the result from the first evaluation.
20
+ * @returns Whether the process has elevated privileges.
21
+ */
22
+ export function evalIsProcessElevated(cache = true) {
23
+ if (cache && elevated !== undefined)
24
+ return elevated;
25
+ const advapi32 = ffi.dlopen("Advapi32.dll", {
26
+ OpenProcessToken: { args: ["ptr", "u32", "buffer"], returns: "bool_u32" },
27
+ GetTokenInformation: { args: ["ptr", "u32", "buffer", "u32", "buffer"], returns: "bool_u32" },
28
+ });
29
+ const kernel32 = ffi.dlopen("Kernel32.dll", {
30
+ GetCurrentProcess: { args: [], returns: "ptr" },
31
+ CloseHandle: { args: ["ptr"], returns: "bool_u32" },
32
+ GetLastError: { args: [], returns: "u32" },
33
+ });
34
+ try {
35
+ const TOKEN_QUERY = 0x0008;
36
+ const TOKEN_ELEVATION = 20;
37
+ const tokenHandle = new Uint8Array(8);
38
+ const opened = advapi32.symbols.OpenProcessToken(kernel32.symbols.GetCurrentProcess(), TOKEN_QUERY, tokenHandle);
39
+ if (!opened)
40
+ throw new Error(`Failed to open process token (${kernel32.symbols.GetLastError()})`);
41
+ try {
42
+ const tokenInfo = new Uint8Array(4);
43
+ const returnLength = new Uint8Array(4);
44
+ const result = advapi32.symbols.GetTokenInformation(tokenHandle, TOKEN_ELEVATION, tokenInfo, 4, returnLength);
45
+ if (!result)
46
+ throw new Error(`Failed to get token information (${kernel32.symbols.GetLastError()})`);
47
+ elevated = tokenInfo[0] !== 0;
48
+ return elevated;
49
+ }
50
+ finally {
51
+ kernel32.symbols.CloseHandle(tokenHandle);
52
+ }
53
+ }
54
+ finally {
55
+ advapi32.close();
56
+ kernel32.close();
57
+ }
58
+ }
package/esm/mod.js ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Detects whether the current process is running with elevated privileges.
3
+ *
4
+ * @module @neotales/is-elevated
5
+ */
6
+ import "./_dnt.polyfills.js";
7
+ let elevated;
8
+ function evalUnixElevation(cache = true) {
9
+ if (cache && elevated !== undefined)
10
+ return elevated;
11
+ let uid = null;
12
+ if ("process" in globalThis && process.geteuid)
13
+ uid = process.geteuid();
14
+ if (uid === null && "process" in globalThis && process.getuid)
15
+ uid = process.getuid();
16
+ // Ende has no process. Deno is a permanent alias there, so this is the Ende path too.
17
+ const globals = globalThis;
18
+ if (uid === null && globals.Deno?.euid)
19
+ uid = globals.Deno.euid();
20
+ if (uid === null && globals.Deno?.uid)
21
+ uid = globals.Deno.uid();
22
+ elevated = uid === 0;
23
+ return elevated;
24
+ }
25
+ function unsupportedNodeFfi() {
26
+ throw new Error("Unable to determine Windows process elevation because neither node:ffi nor koffi is available. Run Node.js >= 26 with --experimental-ffi, or install koffi. See https://github.com/neotales/js-lib/blob/dev/os/jsr/is-elevated/README.md#nodejs-ffi");
27
+ }
28
+ function unsupportedRuntimeFfi() {
29
+ throw new Error(`Unable to determine Windows process elevation because the runtime FFI backend is unavailable.${"Deno" in globalThis ? " Run Deno with --allow-ffi." : ""} See https://github.com/neotales/js-lib/blob/dev/os/jsr/is-elevated/README.md#runtime-support`);
30
+ }
31
+ function windows() {
32
+ if ("process" in globalThis && process.platform === "win32")
33
+ return true;
34
+ const nav = globalThis;
35
+ return nav.navigator?.userAgentData?.platform === "Windows";
36
+ }
37
+ let impl = evalUnixElevation;
38
+ let isSupported = "process" in globalThis || "Deno" in globalThis || "Bun" in globalThis || "tjs" in globalThis;
39
+ if (isSupported && windows()) {
40
+ let isNode = false;
41
+ try {
42
+ if ("Deno" in globalThis) {
43
+ impl = (await import("./ffi_deno.js")).evalIsProcessElevated;
44
+ }
45
+ else if ("Bun" in globalThis) {
46
+ impl = (await import("./ffi_bun.js")).evalIsProcessElevated;
47
+ }
48
+ else if ("tjs" in globalThis) {
49
+ impl = (await import("./ffi_txiki.js")).evalIsProcessElevated;
50
+ }
51
+ else {
52
+ isNode = true;
53
+ try {
54
+ impl = (await import("./ffi_node.js")).evalIsProcessElevated;
55
+ }
56
+ catch {
57
+ impl = (await import("./ffi_koffi.js")).evalIsProcessElevated;
58
+ }
59
+ }
60
+ // Loading a backend alone does not prove FFI permissions and native
61
+ // libraries are usable, so evaluate once before advertising support.
62
+ impl();
63
+ isSupported = true;
64
+ }
65
+ catch (error) {
66
+ const debug = "process" in globalThis && process.env.DEBUG === "true";
67
+ if (debug)
68
+ console.debug(error);
69
+ isSupported = false;
70
+ impl = isNode ? unsupportedNodeFfi : unsupportedRuntimeFfi;
71
+ }
72
+ }
73
+ /**
74
+ * Returns whether elevation detection is available in the current runtime.
75
+ *
76
+ * @returns `true` when {@linkcode isElevated} can evaluate the current process.
77
+ */
78
+ export function isElevatedAvailable() {
79
+ return isSupported;
80
+ }
81
+ /**
82
+ * Reports whether the current process is running with elevated privileges.
83
+ *
84
+ * @param cache - Whether to reuse the result from the first evaluation.
85
+ * @returns Whether the process has elevated privileges.
86
+ * @throws {Error} On Windows when the runtime has no usable FFI backend.
87
+ * @example
88
+ * import { isElevated } from "jsr:@neotales/is-elevated";
89
+ *
90
+ * if (isElevated()) {
91
+ * console.log("The process is elevated.");
92
+ * }
93
+ */
94
+ export function isElevated(cache = true) {
95
+ if (!isSupported) {
96
+ if (impl !== evalUnixElevation)
97
+ impl();
98
+ unsupportedRuntimeFfi();
99
+ }
100
+ return impl(cache);
101
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json CHANGED
@@ -1,58 +1,42 @@
1
1
  {
2
2
  "name": "@neotales/is-elevated",
3
- "version": "0.0.0",
4
- "description": "isElevated function determines if the current process is running elevated (admin/root) across windows, linux, and darwin platforms.",
3
+ "version": "0.1.0-alpha.0",
5
4
  "keywords": [
6
- "admin",
7
- "elevated",
8
- "root",
9
- "std",
10
- "sudo",
11
- "windows",
12
- "linux",
13
- "darwin",
14
- "is-elevated"
5
+ "is-elevated",
6
+ "deno",
7
+ "node",
8
+ "bun",
9
+ "ende",
10
+ "workerd",
11
+ "txiki"
15
12
  ],
16
- "homepage": "https://github.com/neotales/js-os",
17
- "bugs": {
18
- "url": "https://github.com/neotales/js-os/issues"
19
- },
20
- "license": "MIT",
21
13
  "repository": {
22
14
  "type": "git",
23
- "url": "git+https://github.com/neotales/js-os.git",
24
- "directory": "npm/is-elevated"
15
+ "url": "git+https://github.com/neotales/js-lib.git",
16
+ "directory": "os/npm/is-elevated"
25
17
  },
26
- "files": [
27
- "esm",
28
- "types"
29
- ],
30
- "type": "module",
18
+ "license": "MIT",
19
+ "module": "./esm/mod.js",
20
+ "types": "./types/mod.d.ts",
31
21
  "exports": {
32
22
  ".": {
33
- "types": "./types/index.d.ts",
34
- "import": "./esm/index.js"
23
+ "import": {
24
+ "types": "./types/mod.d.ts",
25
+ "default": "./esm/mod.js"
26
+ },
27
+ "default": {
28
+ "types": "./types/mod.d.ts",
29
+ "default": "./esm/mod.js"
30
+ }
35
31
  }
36
32
  },
37
- "publishConfig": {
38
- "access": "public"
39
- },
40
- "devDependencies": {
41
- "@types/bun": "^1.3.14",
42
- "@types/node": "^25.9.3",
43
- "typescript": "^6.0.3"
33
+ "type": "module",
34
+ "engines": {
35
+ "node": ">=22"
44
36
  },
45
37
  "optionalDependencies": {
46
38
  "koffi": "^2.9.0"
47
39
  },
48
- "engines": {
49
- "node": ">=22"
50
- },
51
- "scripts": {
52
- "build": "tsc -p tsconfig.json",
53
- "test": "node --test esm/*.test.js",
54
- "test:ffi": "node --experimental-ffi --test esm/*.test.js",
55
- "test:bun": "bun test ./esm",
56
- "test:deno": "deno test -A esm/*.test.js"
57
- }
40
+ "_generatedBy": "dnt@0.44.0",
41
+ "scripts": {}
58
42
  }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Based on [import-meta-ponyfill](https://github.com/gaubee/import-meta-ponyfill),
3
+ * but instead of using npm to install additional dependencies,
4
+ * this approach manually consolidates cjs/mjs/d.ts into a single file.
5
+ *
6
+ * Note that this code might be imported multiple times
7
+ * (for example, both dnt.test.polyfills.ts and dnt.polyfills.ts contain this code;
8
+ * or Node.js might dynamically clear the cache and then force a require).
9
+ * Therefore, it's important to avoid redundant writes to global objects.
10
+ * Additionally, consider that commonjs is used alongside esm,
11
+ * so the two ponyfill functions are stored independently in two separate global objects.
12
+ */
13
+ import { createRequire } from "node:module";
14
+ import { type URL } from "node:url";
15
+ declare global {
16
+ interface ImportMeta {
17
+ /** A string representation of the fully qualified module URL. When the
18
+ * module is loaded locally, the value will be a file URL (e.g.
19
+ * `file:///path/module.ts`).
20
+ *
21
+ * You can also parse the string as a URL to determine more information about
22
+ * how the current module was loaded. For example to determine if a module was
23
+ * local or not:
24
+ *
25
+ * ```ts
26
+ * const url = new URL(import.meta.url);
27
+ * if (url.protocol === "file:") {
28
+ * console.log("this module was loaded locally");
29
+ * }
30
+ * ```
31
+ */
32
+ url: string;
33
+ /**
34
+ * A function that returns resolved specifier as if it would be imported
35
+ * using `import(specifier)`.
36
+ *
37
+ * ```ts
38
+ * console.log(import.meta.resolve("./foo.js"));
39
+ * // file:///dev/foo.js
40
+ * ```
41
+ *
42
+ * @param specifier The module specifier to resolve relative to `parent`.
43
+ * @param parent The absolute parent module URL to resolve from.
44
+ * @returns The absolute (`file:`) URL string for the resolved module.
45
+ */
46
+ resolve(specifier: string, parent?: string | URL | undefined): string;
47
+ /** A flag that indicates if the current module is the main module that was
48
+ * called when starting the program under Deno.
49
+ *
50
+ * ```ts
51
+ * if (import.meta.main) {
52
+ * // this was loaded as the main module, maybe do some bootstrapping
53
+ * }
54
+ * ```
55
+ */
56
+ main: boolean;
57
+ /** The absolute path of the current module.
58
+ *
59
+ * This property is only provided for local modules (ie. using `file://` URLs).
60
+ *
61
+ * Example:
62
+ * ```
63
+ * // Unix
64
+ * console.log(import.meta.filename); // /home/alice/my_module.ts
65
+ *
66
+ * // Windows
67
+ * console.log(import.meta.filename); // C:\alice\my_module.ts
68
+ * ```
69
+ */
70
+ filename: string;
71
+ /** The absolute path of the directory containing the current module.
72
+ *
73
+ * This property is only provided for local modules (ie. using `file://` URLs).
74
+ *
75
+ * * Example:
76
+ * ```
77
+ * // Unix
78
+ * console.log(import.meta.dirname); // /home/alice
79
+ *
80
+ * // Windows
81
+ * console.log(import.meta.dirname); // C:\alice
82
+ * ```
83
+ */
84
+ dirname: string;
85
+ }
86
+ }
87
+ type NodeRequest = ReturnType<typeof createRequire>;
88
+ type NodeModule = NonNullable<NodeRequest["main"]>;
89
+ interface ImportMetaPonyfillCommonjs {
90
+ (require: NodeRequest, module: NodeModule): ImportMeta;
91
+ }
92
+ interface ImportMetaPonyfillEsmodule {
93
+ (importMeta: ImportMeta): ImportMeta;
94
+ }
95
+ interface ImportMetaPonyfill extends ImportMetaPonyfillCommonjs, ImportMetaPonyfillEsmodule {
96
+ }
97
+ export declare let import_meta_ponyfill_commonjs: ImportMetaPonyfillCommonjs;
98
+ export declare let import_meta_ponyfill_esmodule: ImportMetaPonyfillEsmodule;
99
+ export declare let import_meta_ponyfill: ImportMetaPonyfill;
100
+ export {};
@@ -1,13 +1,13 @@
1
+ /**
2
+ * Implements native Windows elevation detection through Bun FFI.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_bun
5
+ * @internal
6
+ */
1
7
  /**
2
8
  * Reports whether the current process is running with elevated privileges.
3
9
  *
4
10
  * @param cache - Whether to reuse the result from the first evaluation.
5
11
  * @returns Whether the process has elevated privileges.
6
- * @example
7
- * ```ts
8
- * import { evalIsProcessElevated } from "./ffi_bun.js";
9
- *
10
- * const elevated = evalIsProcessElevated();
11
- * ```
12
12
  */
13
13
  export declare function evalIsProcessElevated(cache?: boolean): boolean;
@@ -10,7 +10,7 @@
10
10
  * @returns Whether the process has elevated privileges.
11
11
  * @example
12
12
  * ```ts
13
- * import { evalIsProcessElevated } from "./ffi_deno.js";
13
+ * import { evalIsProcessElevated } from "./ffi_deno.ts";
14
14
  *
15
15
  * const elevated = evalIsProcessElevated();
16
16
  * ```
@@ -1,13 +1,13 @@
1
+ /**
2
+ * Windows elevation through koffi, used when node:ffi is not available.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_koffi
5
+ * @internal
6
+ */
1
7
  /**
2
8
  * Reports whether the current process is running with elevated privileges.
3
9
  *
4
10
  * @param cache - Whether to reuse the result from the first evaluation.
5
11
  * @returns Whether the process has elevated privileges.
6
- * @example
7
- * ```ts
8
- * import { evalIsProcessElevated } from "./ffi_koffi.js";
9
- *
10
- * const elevated = evalIsProcessElevated();
11
- * ```
12
12
  */
13
13
  export declare function evalIsProcessElevated(cache?: boolean): boolean;
@@ -1,13 +1,13 @@
1
+ /**
2
+ * Implements native Windows elevation detection through Node.js FFI.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_node
5
+ * @internal
6
+ */
1
7
  /**
2
8
  * Reports whether the current process is running with elevated privileges.
3
9
  *
4
10
  * @param cache - Whether to reuse the result from the first evaluation.
5
11
  * @returns Whether the process has elevated privileges.
6
- * @example
7
- * ```ts
8
- * import { evalIsProcessElevated } from "./ffi_node.js";
9
- *
10
- * const elevated = evalIsProcessElevated();
11
- * ```
12
12
  */
13
13
  export declare function evalIsProcessElevated(cache?: boolean): boolean;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Windows elevation through txiki.js `tjs:ffi`.
3
+ *
4
+ * @module @neotales/is-elevated/ffi_txiki
5
+ * @internal
6
+ */
7
+ /**
8
+ * Reports whether the current process is running with elevated privileges.
9
+ *
10
+ * @param cache - Whether to reuse the result from the first evaluation.
11
+ * @returns Whether the process has elevated privileges.
12
+ */
13
+ export declare function evalIsProcessElevated(cache?: boolean): boolean;
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Detects whether the current process is running with elevated privileges.
3
+ *
4
+ * @module @neotales/is-elevated
5
+ */
6
+ import "./_dnt.polyfills.js";
1
7
  /**
2
8
  * Returns whether elevation detection is available in the current runtime.
3
9
  *
@@ -11,7 +17,7 @@ export declare function isElevatedAvailable(): boolean;
11
17
  * @returns Whether the process has elevated privileges.
12
18
  * @throws {Error} On Windows when the runtime has no usable FFI backend.
13
19
  * @example
14
- * import { isElevated } from "@neotales/is-elevated";
20
+ * import { isElevated } from "jsr:@neotales/is-elevated";
15
21
  *
16
22
  * if (isElevated()) {
17
23
  * console.log("The process is elevated.");
package/esm/index.js DELETED
@@ -1,71 +0,0 @@
1
- import process from "node:process";
2
- let elevated;
3
- function evalUnixElevation(cache = true) {
4
- if (cache && elevated !== undefined) {
5
- return elevated;
6
- }
7
- const uid = process.geteuid?.() ?? process.getuid?.();
8
- elevated = uid === 0;
9
- return elevated;
10
- }
11
- const runtime = globalThis;
12
- const isWindows = process.platform === "win32";
13
- function unsupportedNodeFfi() {
14
- throw new Error("Unable to determine Windows process elevation because neither node:ffi nor koffi is available. Run Node.js >= 26 with --experimental-ffi, or install koffi with npm install koffi. See https://github.com/neotales/js-os/blob/dev/npm/is-elevated/README.md#nodejs-ffi");
15
- }
16
- function unsupportedRuntimeFfi() {
17
- throw new Error("Unable to determine Windows process elevation because the runtime FFI backend is unavailable. Run Deno with --allow-ffi. See https://github.com/neotales/js-os/blob/dev/npm/is-elevated/README.md#runtime-support");
18
- }
19
- let impl = evalUnixElevation;
20
- let isSupported = !isWindows;
21
- if (isWindows) {
22
- try {
23
- if (runtime.Deno) {
24
- impl = (await import("./ffi_deno.js")).evalIsProcessElevated;
25
- }
26
- else if (runtime.Bun) {
27
- impl = (await import("./ffi_bun.js")).evalIsProcessElevated;
28
- }
29
- else {
30
- try {
31
- impl = (await import("./ffi_node.js")).evalIsProcessElevated;
32
- }
33
- catch {
34
- impl = (await import("./ffi_koffi.js")).evalIsProcessElevated;
35
- }
36
- }
37
- // A backend import can succeed while FFI permissions or DLL loading fail.
38
- // Probe it once before exposing a supported evaluator.
39
- impl();
40
- isSupported = true;
41
- }
42
- catch (error) {
43
- if (process.env.DEBUG === "true")
44
- console.debug(error);
45
- impl = runtime.Deno || runtime.Bun ? unsupportedRuntimeFfi : unsupportedNodeFfi;
46
- }
47
- }
48
- /**
49
- * Returns whether elevation detection is available in the current runtime.
50
- *
51
- * @returns `true` when {@linkcode isElevated} can evaluate the current process.
52
- */
53
- export function isElevatedAvailable() {
54
- return isSupported;
55
- }
56
- /**
57
- * Reports whether the current process is running with elevated privileges.
58
- *
59
- * @param cache - Whether to reuse the result from the first evaluation.
60
- * @returns Whether the process has elevated privileges.
61
- * @throws {Error} On Windows when the runtime has no usable FFI backend.
62
- * @example
63
- * import { isElevated } from "@neotales/is-elevated";
64
- *
65
- * if (isElevated()) {
66
- * console.log("The process is elevated.");
67
- * }
68
- */
69
- export function isElevated(cache = true) {
70
- return impl(cache);
71
- }
package/esm/index.test.js DELETED
@@ -1,39 +0,0 @@
1
- import { strictEqual, throws } from "node:assert/strict";
2
- import process from "node:process";
3
- import { test } from "node:test";
4
- import { isElevated, isElevatedAvailable } from "./index.js";
5
- import { evalIsProcessElevated as evalNodeIsProcessElevated } from "./node.js";
6
- test("isElevated returns a boolean", () => {
7
- if (process.platform === "win32" && !isElevatedAvailable())
8
- return;
9
- strictEqual(typeof isElevated(), "boolean");
10
- strictEqual(typeof isElevated(false), "boolean");
11
- });
12
- test("isElevatedAvailable returns a boolean", () => {
13
- strictEqual(typeof isElevatedAvailable(), "boolean");
14
- });
15
- test("isElevated caches by default", () => {
16
- if (process.platform === "win32" && !isElevatedAvailable())
17
- return;
18
- strictEqual(isElevated(), isElevated());
19
- });
20
- test("node evaluator returns a boolean", () => {
21
- strictEqual(typeof evalNodeIsProcessElevated(), "boolean");
22
- strictEqual(typeof evalNodeIsProcessElevated(false), "boolean");
23
- });
24
- test("node evaluator matches effective uid semantics on unix-like runtimes", { skip: process.platform === "win32" || (!process.geteuid && !process.getuid) }, () => {
25
- strictEqual(evalNodeIsProcessElevated(false), (process.geteuid?.() ?? process.getuid?.()) === 0);
26
- });
27
- test("isElevated matches effective uid semantics on unix-like runtimes", { skip: process.platform === "win32" || (!process.geteuid && !process.getuid) }, () => {
28
- strictEqual(isElevated(false), (process.geteuid?.() ?? process.getuid?.()) === 0);
29
- });
30
- test("node evaluator falls back to false when uid is unavailable", { skip: process.platform !== "win32" }, () => {
31
- strictEqual(evalNodeIsProcessElevated(false), false);
32
- });
33
- test("root helper returns a boolean on Windows-specific runtimes", { skip: process.platform !== "win32" }, () => {
34
- if (!isElevatedAvailable()) {
35
- throws(() => isElevated(), /node:ffi nor koffi.*#nodejs-ffi/);
36
- return;
37
- }
38
- strictEqual(typeof isElevated(false), "boolean");
39
- });
package/esm/node.js DELETED
@@ -1,20 +0,0 @@
1
- import process from "node:process";
2
- let elevated;
3
- /**
4
- * Reports whether the current process has an effective user ID of zero.
5
- *
6
- * @param cache - Whether to reuse the result from the first evaluation.
7
- * @returns Whether the process has an effective user ID of zero.
8
- * @example
9
- * import { evalIsProcessElevated } from "./node.js";
10
- *
11
- * const elevated = evalIsProcessElevated();
12
- */
13
- export function evalIsProcessElevated(cache = true) {
14
- if (cache && elevated !== undefined) {
15
- return elevated;
16
- }
17
- const uid = process.geteuid?.() ?? process.getuid?.();
18
- elevated = uid === 0;
19
- return elevated;
20
- }
@@ -1 +0,0 @@
1
- export {};
package/types/node.d.ts DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * Reports whether the current process has an effective user ID of zero.
3
- *
4
- * @param cache - Whether to reuse the result from the first evaluation.
5
- * @returns Whether the process has an effective user ID of zero.
6
- * @example
7
- * import { evalIsProcessElevated } from "./node.js";
8
- *
9
- * const elevated = evalIsProcessElevated();
10
- */
11
- export declare function evalIsProcessElevated(cache?: boolean): boolean;