syscall-napi 0.1.3 → 0.1.4
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 +8 -4
- package/dist/lib/native.js +2 -2
- package/dist/lib/snippets/native-loader.js +108 -71
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -6,9 +6,11 @@ Node.js module to perform promise-based asynchronous syscalls
|
|
|
6
6
|
|
|
7
7
|
This module allows to execute syscalls on linux based systems. NAPI is being used to stay ABI compatible with future Node.js versions. The focus of this module is not (yet) performance, but providing a clear interface to the linux kernel.
|
|
8
8
|
|
|
9
|
+
Previously this module supported asynchronous operation, which turned out not to work as expected. Although it would be possible, currently this is not supported anymore. If you need to execute long running syscalls in parallel, use Workers.
|
|
10
|
+
|
|
9
11
|
## Requirements
|
|
10
12
|
|
|
11
|
-
- `Node.js >=
|
|
13
|
+
- `Node.js >= 20`, might work on older
|
|
12
14
|
|
|
13
15
|
## Installation
|
|
14
16
|
|
|
@@ -24,7 +26,8 @@ yarn install syscall-napi
|
|
|
24
26
|
|
|
25
27
|
## API
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
`syscall({ syscallNumber: bigint, args: (bigint | Uint8Array)[] })`
|
|
30
|
+
=> `{ errno: undefined, ret: BigInt } | { errno: number, ret: undefined }`
|
|
28
31
|
|
|
29
32
|
Supported argument types:
|
|
30
33
|
- `BigInt` arguments are converted to native integers
|
|
@@ -33,7 +36,7 @@ Supported argument types:
|
|
|
33
36
|
In case of an error the promise is rejected with an error object.
|
|
34
37
|
|
|
35
38
|
For params see `man 2 syscall`.
|
|
36
|
-
|
|
39
|
+
|
|
37
40
|
### `syscallNumbers.{syscall}`
|
|
38
41
|
This module provides syscall numbers (e.g. `getpid`) that are defined in `uapi/asm-generic/unistd.h` in the linux kernel.
|
|
39
42
|
|
|
@@ -43,7 +46,7 @@ This module provides syscall numbers (e.g. `getpid`) that are defined in `uapi/a
|
|
|
43
46
|
import { syscall, syscallNumbers } from "syscall-napi";
|
|
44
47
|
|
|
45
48
|
const { errno, ret: pid } = syscall({
|
|
46
|
-
syscallNumber: syscallNumbers.
|
|
49
|
+
syscallNumber: syscallNumbers.getpid,
|
|
47
50
|
args: []
|
|
48
51
|
});
|
|
49
52
|
|
|
@@ -52,4 +55,5 @@ if (errno === undefined) {
|
|
|
52
55
|
} else {
|
|
53
56
|
console.log(`errno = ${errno}`);
|
|
54
57
|
}
|
|
58
|
+
|
|
55
59
|
```
|
package/dist/lib/native.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createNativeAddonLoader } from "./snippets/native-loader.js";
|
|
|
2
2
|
|
|
3
3
|
const nativeAddonLoader = createNativeAddonLoader();
|
|
4
4
|
const native = nativeAddonLoader.loadRelativeToPackageRoot({
|
|
5
|
-
|
|
5
|
+
relativeBuildFolderPath: "./build"
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
/*type TSyscallSync = (args: bigint, ...rest: (bigint | Uint8Array)[]) => { errno: number; ret?: bigint; };*/
|
|
@@ -10,5 +10,5 @@ const native = nativeAddonLoader.loadRelativeToPackageRoot({
|
|
|
10
10
|
const syscall_sync = native.syscall_sync /*as TSyscallSync*/;
|
|
11
11
|
|
|
12
12
|
export {
|
|
13
|
-
|
|
13
|
+
syscall_sync
|
|
14
14
|
};
|
|
@@ -4,118 +4,155 @@ import nodePath from "node:path";
|
|
|
4
4
|
import nodeModule from "node:module";
|
|
5
5
|
|
|
6
6
|
const findPackageJson = ({ startPath, maxUpwardSteps }/*: { startPath: string; maxUpwardSteps: number }*/) => {
|
|
7
|
-
|
|
7
|
+
let currentPath = startPath;
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
for (let i = 0; i < maxUpwardSteps; i += 1) {
|
|
10
|
+
const packageJsonPath = nodePath.join(currentPath, "package.json");
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const parentPath = nodePath.dirname(currentPath);
|
|
12
|
+
if (nodeFs.existsSync(packageJsonPath)) {
|
|
13
|
+
return packageJsonPath;
|
|
14
|
+
}
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
if (parentPath === currentPath) {
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
16
|
+
const parentPath = nodePath.dirname(currentPath);
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
// Reached filesystem root
|
|
19
|
+
if (parentPath === currentPath) {
|
|
20
|
+
break;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
currentPath = parentPath;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
throw new Error(`Could not find package.json within ${maxUpwardSteps} directory levels from ${startPath}`);
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const findPackageRoot = ({ maxUpwardSteps }/*: { maxUpwardSteps: number }*/) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
const ourScriptUrl = import.meta.url;
|
|
31
|
+
const ourScriptPath = nodeUrl.fileURLToPath(ourScriptUrl);
|
|
32
|
+
const ourScriptDirectory = nodePath.dirname(ourScriptPath);
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const ourPackageJsonPath = findPackageJson({
|
|
35
|
+
startPath: ourScriptDirectory,
|
|
36
|
+
maxUpwardSteps,
|
|
37
|
+
});
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
const ourPackageRoot = nodePath.dirname(ourPackageJsonPath);
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
return ourPackageRoot;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
const findAddonInAddonFolder = ({ addonFolderPath }/*: { addonFolderPath: string }*/) => {
|
|
45
|
-
|
|
45
|
+
const entries = nodeFs.readdirSync(addonFolderPath);
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const addonEntries = entries.filter((entry) => {
|
|
48
|
+
return entry.endsWith(".node");
|
|
49
|
+
});
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
if (addonEntries.length === 0) {
|
|
52
|
+
throw Error(`no .node addon file found in build folder "${addonFolderPath}"`);
|
|
53
|
+
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
if (addonEntries.length > 1) {
|
|
56
|
+
throw Error(`multiple .node addon files found in build folder "${addonFolderPath}", cannot determine which to load`);
|
|
57
|
+
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
const addonFileName = addonEntries[0];
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
return addonFileName;
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
const loadAddonAtPath = ({ addonFilePath }/*: { addonFilePath: string }*/) => {
|
|
65
|
-
|
|
65
|
+
const require = nodeModule.createRequire(import.meta.url);
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
const native = require(addonFilePath);
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
return native;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
const
|
|
72
|
+
const assertOnlyOneOfDebugOrReleaseExists = ({
|
|
73
|
+
debugFolderExists,
|
|
74
|
+
releaseFolderExists
|
|
75
|
+
}/*: {
|
|
76
|
+
debugFolderExists: boolean;
|
|
77
|
+
releaseFolderExists: boolean
|
|
78
|
+
}*/) => {
|
|
79
|
+
if (debugFolderExists && releaseFolderExists) {
|
|
80
|
+
throw Error(`both Debug and Release build folders exist, please remove one to avoid ambiguity`);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const assertAtLeastOneOfDebugOrReleaseExists = ({
|
|
85
|
+
debugFolderExists,
|
|
86
|
+
releaseFolderExists
|
|
87
|
+
}/*: {
|
|
88
|
+
debugFolderExists: boolean;
|
|
89
|
+
releaseFolderExists: boolean
|
|
90
|
+
}*/) => {
|
|
91
|
+
if (!debugFolderExists && !releaseFolderExists) {
|
|
92
|
+
throw Error(`neither Debug nor Release build folders found, make sure to build the native addon first`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
73
95
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
packageRoot = findPackageRoot({ maxUpwardSteps: 10 });
|
|
79
|
-
} catch (err) {
|
|
80
|
-
throw Error(`could not find our package root, make sure to keep the package structure intact when distributing the package - a package.json and built addon at ./build are required`);
|
|
81
|
-
}
|
|
96
|
+
const determineReleaseOrDebugFolder = ({ buildFolderPath }/*: { buildFolderPath: string }*/) => {
|
|
97
|
+
const debugFolderPath = nodePath.join(buildFolderPath, "Debug");
|
|
98
|
+
const releaseFolderPath = nodePath.join(buildFolderPath, "Release");
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
const debugFolderExists = nodeFs.existsSync(debugFolderPath);
|
|
101
|
+
const releaseFolderExists = nodeFs.existsSync(releaseFolderPath);
|
|
84
102
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
try {
|
|
104
|
+
assertAtLeastOneOfDebugOrReleaseExists({ debugFolderExists, releaseFolderExists });
|
|
105
|
+
assertOnlyOneOfDebugOrReleaseExists({ debugFolderExists, releaseFolderExists });
|
|
106
|
+
} catch (ex) {
|
|
107
|
+
throw Error(`invalid build folder structure at "${buildFolderPath}"`, { cause: ex });
|
|
108
|
+
}
|
|
88
109
|
|
|
89
|
-
|
|
90
|
-
const releaseFolderPath = nodePath.join(buildFolderPath, "Release");
|
|
110
|
+
const addonFolderPath = releaseFolderExists ? releaseFolderPath : debugFolderPath;
|
|
91
111
|
|
|
92
|
-
|
|
93
|
-
|
|
112
|
+
return addonFolderPath;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const loadAddonFromFolder = ({ addonFolderPath }/*: { addonFolderPath: string }*/) => {
|
|
116
|
+
const addonFileName = findAddonInAddonFolder({ addonFolderPath });
|
|
117
|
+
|
|
118
|
+
const addonFilePath = nodePath.resolve(addonFolderPath, addonFileName);
|
|
119
|
+
|
|
120
|
+
const native = loadAddonAtPath({ addonFilePath });
|
|
121
|
+
|
|
122
|
+
return native;
|
|
123
|
+
};
|
|
94
124
|
|
|
95
|
-
|
|
96
|
-
throw Error(`neither Debug nor Release build folders found at our package root "${buildFolderPath}", make sure to build the native addon first`);
|
|
97
|
-
}
|
|
125
|
+
const createNativeAddonLoader = () => {
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
127
|
+
const loadRelativeToPackageRoot = ({ relativeBuildFolderPath }/*: { relativeBuildFolderPath: string }*/) => {
|
|
128
|
+
let packageRoot/*: string*/;
|
|
102
129
|
|
|
103
|
-
|
|
130
|
+
try {
|
|
131
|
+
packageRoot = findPackageRoot({ maxUpwardSteps: 10 });
|
|
132
|
+
} catch (err) {
|
|
133
|
+
let message = "could not find our package root";
|
|
134
|
+
message += ", make sure to keep the package structure intact when distributing the package";
|
|
135
|
+
message += " - a package.json and built addon at ./build are required";
|
|
136
|
+
throw Error(message, { cause: err });
|
|
137
|
+
}
|
|
104
138
|
|
|
105
|
-
|
|
139
|
+
const buildFolderPath = nodePath.join(packageRoot, relativeBuildFolderPath);
|
|
106
140
|
|
|
107
|
-
|
|
141
|
+
if (!nodeFs.existsSync(buildFolderPath)) {
|
|
142
|
+
throw Error(`no build folder found at our package root "${buildFolderPath}", make sure to build the native addon first`);
|
|
143
|
+
}
|
|
108
144
|
|
|
109
|
-
|
|
145
|
+
const addonFolderPath = determineReleaseOrDebugFolder({ buildFolderPath });
|
|
146
|
+
const native = loadAddonFromFolder({ addonFolderPath });
|
|
110
147
|
|
|
111
|
-
|
|
112
|
-
|
|
148
|
+
return native;
|
|
149
|
+
};
|
|
113
150
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
151
|
+
return {
|
|
152
|
+
loadRelativeToPackageRoot,
|
|
153
|
+
};
|
|
117
154
|
};
|
|
118
155
|
|
|
119
156
|
export {
|
|
120
|
-
|
|
157
|
+
createNativeAddonLoader,
|
|
121
158
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
+
"version": "0.1.4",
|
|
2
3
|
"name": "syscall-napi",
|
|
3
4
|
"type": "module",
|
|
4
|
-
"version": "0.1.3",
|
|
5
5
|
"description": "Node.js module to perform synchronous syscalls",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist/**/*",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "rm -rf dist/ && deno-node-build --root . --out dist/ --entry lib/index.ts",
|
|
14
14
|
"test": "c8 --reporter lcov --reporter html --reporter text mocha test/**/*.ts",
|
|
15
|
-
"
|
|
15
|
+
"lint": "eslint ."
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@eslint/js": "^9.39.2",
|
|
19
|
-
"@k13engineering/releasetool": "^0.0.
|
|
19
|
+
"@k13engineering/releasetool": "^0.0.5",
|
|
20
20
|
"@types/mocha": "^10.0.10",
|
|
21
21
|
"@types/node": "^25.0.3",
|
|
22
22
|
"c8": "^10.1.3",
|