relaxnative 0.1.0-beta.1
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/LICENSE +21 -0
- package/README.md +592 -0
- package/dist/chunk-22W76CYR.js +607 -0
- package/dist/chunk-24NXCU65.js +254 -0
- package/dist/chunk-2APMRURB.js +65 -0
- package/dist/chunk-2CHBHJPT.js +607 -0
- package/dist/chunk-2I4JHZI7.js +287 -0
- package/dist/chunk-2JOHYYQO.js +607 -0
- package/dist/chunk-3GW77EWF.js +505 -0
- package/dist/chunk-5J5CAKCD.js +266 -0
- package/dist/chunk-5NTDZ7YZ.js +377 -0
- package/dist/chunk-5TA6MROS.js +529 -0
- package/dist/chunk-5WVEBKMJ.js +1019 -0
- package/dist/chunk-6O5TIEEI.js +545 -0
- package/dist/chunk-6XU5DETO.js +896 -0
- package/dist/chunk-7BIZ6P3B.js +176 -0
- package/dist/chunk-7DKO777J.js +285 -0
- package/dist/chunk-7JYWUH4Y.js +268 -0
- package/dist/chunk-7NMCEP2V.js +756 -0
- package/dist/chunk-A7N4YBP2.js +379 -0
- package/dist/chunk-AZTCDV6R.js +572 -0
- package/dist/chunk-B34XEGM6.js +559 -0
- package/dist/chunk-BFHBLVXW.js +607 -0
- package/dist/chunk-BLOJ33LO.js +65 -0
- package/dist/chunk-BYPXCWTI.js +375 -0
- package/dist/chunk-C4KJD2AN.js +1044 -0
- package/dist/chunk-CJALJTRQ.js +814 -0
- package/dist/chunk-D4PK367Z.js +627 -0
- package/dist/chunk-DCWBZPEV.js +287 -0
- package/dist/chunk-DI7KSUEC.js +676 -0
- package/dist/chunk-DQ2KXIOO.js +665 -0
- package/dist/chunk-DV5STE3W.js +986 -0
- package/dist/chunk-EG5KNEKP.js +1027 -0
- package/dist/chunk-EOA2OWFA.js +1020 -0
- package/dist/chunk-ES3B6EZJ.js +56 -0
- package/dist/chunk-ETIXNPU5.js +741 -0
- package/dist/chunk-EUZBU2H7.js +824 -0
- package/dist/chunk-F6V7XDEB.js +150 -0
- package/dist/chunk-FNJKUFNF.js +1019 -0
- package/dist/chunk-FZB37DWL.js +453 -0
- package/dist/chunk-G3NDHZNZ.js +453 -0
- package/dist/chunk-G4YR34LE.js +410 -0
- package/dist/chunk-GU4XXISM.js +264 -0
- package/dist/chunk-GVPSQXGJ.js +1027 -0
- package/dist/chunk-HD5C4RNU.js +676 -0
- package/dist/chunk-HDIVY47T.js +287 -0
- package/dist/chunk-HFLRTDNK.js +985 -0
- package/dist/chunk-HGWRCVQ5.js +287 -0
- package/dist/chunk-HRG3SVKK.js +995 -0
- package/dist/chunk-HUGFULJ3.js +1027 -0
- package/dist/chunk-IDYSBXYS.js +344 -0
- package/dist/chunk-ISDDUQVI.js +1019 -0
- package/dist/chunk-IZ632ZCJ.js +286 -0
- package/dist/chunk-J5XI4L52.js +218 -0
- package/dist/chunk-JTIO7BUH.js +582 -0
- package/dist/chunk-JTWSFMF2.js +1020 -0
- package/dist/chunk-K5TV62T4.js +736 -0
- package/dist/chunk-K7MTG53V.js +985 -0
- package/dist/chunk-KGLZB3H2.js +676 -0
- package/dist/chunk-KYAB35P5.js +741 -0
- package/dist/chunk-KYDW3YVX.js +453 -0
- package/dist/chunk-L3MEMPRH.js +361 -0
- package/dist/chunk-LFTO3Z7N.js +757 -0
- package/dist/chunk-LLZ4I4OR.js +405 -0
- package/dist/chunk-LMRUM4U4.js +207 -0
- package/dist/chunk-LT5OGU6T.js +559 -0
- package/dist/chunk-LZQQOC3M.js +741 -0
- package/dist/chunk-LZYUNF6Q.js +1017 -0
- package/dist/chunk-MCTPVW4G.js +453 -0
- package/dist/chunk-MGLWXBIB.js +65 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-MTE6XDGC.js +541 -0
- package/dist/chunk-NDJUNDAE.js +676 -0
- package/dist/chunk-NG7SNFUD.js +1027 -0
- package/dist/chunk-ONVWKYK7.js +739 -0
- package/dist/chunk-OVMTFGE7.js +1042 -0
- package/dist/chunk-P5RQPRJ4.js +741 -0
- package/dist/chunk-PFABSW6Y.js +401 -0
- package/dist/chunk-PVVUJA2M.js +65 -0
- package/dist/chunk-Q3CDTGTX.js +676 -0
- package/dist/chunk-QKAKWDOQ.js +967 -0
- package/dist/chunk-QMPRDU6I.js +598 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RB6RHB6C.js +254 -0
- package/dist/chunk-RD2K7ODW.js +175 -0
- package/dist/chunk-RHBHJND2.js +582 -0
- package/dist/chunk-RUP6POSE.js +453 -0
- package/dist/chunk-RYNSM23L.js +582 -0
- package/dist/chunk-S72S7XXS.js +255 -0
- package/dist/chunk-SDV5AAPW.js +213 -0
- package/dist/chunk-STXQPXUY.js +242 -0
- package/dist/chunk-TUSF5AEG.js +140 -0
- package/dist/chunk-V74SNTE6.js +736 -0
- package/dist/chunk-VHM5XETC.js +453 -0
- package/dist/chunk-VKQIXLNL.js +273 -0
- package/dist/chunk-VPG23Z7Y.js +545 -0
- package/dist/chunk-VSP234PR.js +627 -0
- package/dist/chunk-WH4JPUWF.js +598 -0
- package/dist/chunk-WLAUJL3K.js +409 -0
- package/dist/chunk-WXCN2QJ7.js +350 -0
- package/dist/chunk-X3JZKLJC.js +896 -0
- package/dist/chunk-X7SAP7FC.js +582 -0
- package/dist/chunk-XEH6PRYE.js +968 -0
- package/dist/chunk-XI65CAQV.js +211 -0
- package/dist/chunk-Y7OSHR6W.js +235 -0
- package/dist/chunk-YN4WUMVD.js +1020 -0
- package/dist/chunk-YUWJ2C4Y.js +1020 -0
- package/dist/chunk-YXLBPWNU.js +263 -0
- package/dist/chunk-YYJJHO7R.js +407 -0
- package/dist/chunk-Z2RBHUIH.js +757 -0
- package/dist/chunk-Z6G3KIOM.js +1027 -0
- package/dist/chunk-ZPPXCDSH.js +361 -0
- package/dist/chunk-ZRTY24SZ.js +582 -0
- package/dist/chunk-ZSDFBCQG.js +741 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +339 -0
- package/dist/esmLoader.d.ts +10 -0
- package/dist/esmLoader.js +112 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +126 -0
- package/dist/memory/memory.selftest.d.ts +2 -0
- package/dist/memory/memory.selftest.js +25 -0
- package/dist/worker/processEntry.d.ts +2 -0
- package/dist/worker/processEntry.js +160 -0
- package/dist/worker/workerEntry.d.ts +2 -0
- package/dist/worker/workerEntry.js +27 -0
- package/native/examples/add.c +6 -0
- package/native/examples/add_test.c +23 -0
- package/native/relaxnative_test.h +36 -0
- package/package.json +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ravi Kishan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD033 -->
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://ravikisha.github.io/assets/relaxnative.png" width="400" alt="Relaxnative logo" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h1 align="center">Relaxnative</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
Native C/C++/Rust for Node.js with zero-config compilation, caching, isolation modes, and a supply-chain trust system for third‑party native packages.
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://github.com/Ravikisha/RelaxNative">GitHub</a>
|
|
15
|
+
·
|
|
16
|
+
<a href="#quickstart">Quickstart</a>
|
|
17
|
+
·
|
|
18
|
+
<a href="#annotations">Annotations</a>
|
|
19
|
+
·
|
|
20
|
+
<a href="#cli">CLI</a>
|
|
21
|
+
·
|
|
22
|
+
<a href="#registry-relaxregistry">RelaxRegistry</a>
|
|
23
|
+
·
|
|
24
|
+
<a href="#express-example">Express example</a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What you get
|
|
30
|
+
|
|
31
|
+
- **Compile-on-demand**: import a `.c`/`.cpp`/`.rs` file and call exported functions.
|
|
32
|
+
- **Deterministic cache**: native builds are cached and re-used.
|
|
33
|
+
- **Isolation modes**:
|
|
34
|
+
- `in-process` (fastest) - this mode runs native code in the same thread as the JavaScript code, providing the lowest overhead but also the least isolation.
|
|
35
|
+
- `worker` (async dispatch via worker threads) - this mode runs native code in a separate worker thread, providing better isolation at the cost of some overhead.
|
|
36
|
+
- `process` (crash isolation + best-effort runtime safety guards) - this mode runs native code in a separate process, providing the highest level of isolation but also the highest overhead.
|
|
37
|
+
- **RelaxRegistry packages**: install native “packages” into `native/registry/`.
|
|
38
|
+
- **Supply-chain trust levels** for registry packages: `local`, `community`, `verified`.
|
|
39
|
+
|
|
40
|
+
> Native code is inherently unsafe. Isolation and guards help, but they’re not a perfect sandbox.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm i relaxnative
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Requirements:
|
|
51
|
+
- Node.js >= 18
|
|
52
|
+
- A C compiler (clang or gcc). Rust optional (rustc + cargo).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
### 1) Create a native file
|
|
59
|
+
|
|
60
|
+
`native/add.c`
|
|
61
|
+
|
|
62
|
+
```c
|
|
63
|
+
// @sync
|
|
64
|
+
int add(int a, int b) {
|
|
65
|
+
return a + b;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2) Import and call it from JS/TS
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { loadNative } from 'relaxnative';
|
|
73
|
+
|
|
74
|
+
const mod = await loadNative('native/add.c', { isolation: 'worker' });
|
|
75
|
+
console.log(mod.add(1, 2));
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## API
|
|
81
|
+
|
|
82
|
+
### `loadNative(sourcePath, options?)`
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { loadNative } from 'relaxnative';
|
|
86
|
+
|
|
87
|
+
const mod = await loadNative('native/add.c', {
|
|
88
|
+
isolation: 'worker',
|
|
89
|
+
config: {
|
|
90
|
+
functionMode: { add: 'sync' },
|
|
91
|
+
defaultMode: 'sync',
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Options:
|
|
97
|
+
- `isolation?: 'in-process' | 'worker' | 'process'`
|
|
98
|
+
- `config?: { functionMode?: Record<string, 'sync'|'async'>; defaultMode?: 'sync'|'async' }`
|
|
99
|
+
|
|
100
|
+
Notes:
|
|
101
|
+
- Default isolation is `worker`.
|
|
102
|
+
- In `process` isolation, calls are IPC-based and therefore async.
|
|
103
|
+
|
|
104
|
+
### Native memory helpers
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { native } from 'relaxnative';
|
|
108
|
+
|
|
109
|
+
const buf = native.alloc(1024);
|
|
110
|
+
buf.write(Uint8Array.from([1, 2, 3]));
|
|
111
|
+
console.log(buf.address); // numeric pointer
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Isolation modes
|
|
117
|
+
|
|
118
|
+
### `in-process`
|
|
119
|
+
- fastest
|
|
120
|
+
- unsafe: native crashes take down your Node process
|
|
121
|
+
|
|
122
|
+
### `worker`
|
|
123
|
+
- worker-thread dispatch for async calls
|
|
124
|
+
- sync calls may execute directly for low overhead
|
|
125
|
+
|
|
126
|
+
### `process`
|
|
127
|
+
- forked helper process
|
|
128
|
+
- crash containment
|
|
129
|
+
- best-effort Node runtime guards (module import denial for fs/network/spawn)
|
|
130
|
+
- call timeout enforcement (kills helper)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Annotations
|
|
135
|
+
|
|
136
|
+
Relaxnative reads annotations from up to **3 lines above** a function definition.
|
|
137
|
+
|
|
138
|
+
Supported:
|
|
139
|
+
- `@sync`
|
|
140
|
+
- `@async`
|
|
141
|
+
- `@cost low|medium|high`
|
|
142
|
+
|
|
143
|
+
### What annotations mean
|
|
144
|
+
|
|
145
|
+
- `@sync`
|
|
146
|
+
- The JS wrapper returns a plain value.
|
|
147
|
+
- In `worker` isolation, this may still execute on the main thread for low overhead.
|
|
148
|
+
- Best for quick, safe-ish functions (or when you explicitly accept crash risk in `in-process`).
|
|
149
|
+
- `@async`
|
|
150
|
+
- The JS wrapper returns a `Promise`.
|
|
151
|
+
- In `worker` isolation, the call always goes through a worker thread.
|
|
152
|
+
- Best for CPU-heavy work where you don't want to block the event loop.
|
|
153
|
+
- `@cost low|medium|high`
|
|
154
|
+
- A hint used for readability and future scheduling heuristics.
|
|
155
|
+
- Today it doesn't change performance by itself, but it's useful documentation.
|
|
156
|
+
|
|
157
|
+
### C/C++ example
|
|
158
|
+
|
|
159
|
+
```c
|
|
160
|
+
// @async
|
|
161
|
+
// @cost high
|
|
162
|
+
int heavy(int n) {
|
|
163
|
+
long x = 0;
|
|
164
|
+
for (int i = 0; i < n * 10000000; i++) x += i;
|
|
165
|
+
return (int)x;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Rust example
|
|
170
|
+
|
|
171
|
+
```rust
|
|
172
|
+
// @sync
|
|
173
|
+
#[no_mangle]
|
|
174
|
+
pub extern "C" fn add(a: i32, b: i32) -> i32 {
|
|
175
|
+
a + b
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Types & FFI contract
|
|
182
|
+
|
|
183
|
+
Relaxnative parses your function signatures and maps them to FFI types.
|
|
184
|
+
This is intentionally conservative: **if we don't recognize a type, we fail fast**.
|
|
185
|
+
|
|
186
|
+
### Core rule of thumb
|
|
187
|
+
|
|
188
|
+
- **Scalars** (like `int`, `double`, `uint32_t`) map to JS `number`.
|
|
189
|
+
- **Pointers** (like `double*`, `uint32_t*`) map to one of:
|
|
190
|
+
- a **TypedArray** (preferred when available)
|
|
191
|
+
- a numeric **address** (advanced; obtained via `native.alloc(...).address`)
|
|
192
|
+
|
|
193
|
+
### Supported scalar C types (common)
|
|
194
|
+
|
|
195
|
+
- `int`, `unsigned int`
|
|
196
|
+
- `float`, `double`
|
|
197
|
+
- `long` (treated as 64-bit)
|
|
198
|
+
- `size_t`
|
|
199
|
+
- fixed-width ints: `int8_t`, `uint8_t`, `int16_t`, `uint16_t`, `int32_t`, `uint32_t`, `int64_t`, `uint64_t`
|
|
200
|
+
|
|
201
|
+
### Supported pointer forms
|
|
202
|
+
|
|
203
|
+
- `uint8_t*` / `unsigned char*` treated as **byte buffers**
|
|
204
|
+
- Pass a `Uint8Array` (recommended)
|
|
205
|
+
- Or pass a numeric pointer from `native.alloc()`
|
|
206
|
+
- Typed pointers: `uint32_t*` becomes `pointer<uint32_t>` internally
|
|
207
|
+
- Pass a `Uint32Array` directly
|
|
208
|
+
|
|
209
|
+
### Strings
|
|
210
|
+
|
|
211
|
+
- `const char*` parameters are treated as **cstring**.
|
|
212
|
+
- JS side: pass a JS `string`.
|
|
213
|
+
- `char*`/`const char*` returns are treated as cstring.
|
|
214
|
+
- JS side: expect a JS `string`.
|
|
215
|
+
|
|
216
|
+
### Example: buffer in + buffer out
|
|
217
|
+
|
|
218
|
+
```c
|
|
219
|
+
// @sync
|
|
220
|
+
void xor_u8(const uint8_t* a, const uint8_t* b, uint8_t* out, int n) {
|
|
221
|
+
for (int i = 0; i < n; i++) out[i] = a[i] ^ b[i];
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import { loadNative } from 'relaxnative';
|
|
227
|
+
|
|
228
|
+
const { xor_u8 } = await loadNative('native/xor.c', { isolation: 'worker' });
|
|
229
|
+
const a = new Uint8Array(1024);
|
|
230
|
+
const b = new Uint8Array(1024);
|
|
231
|
+
const out = new Uint8Array(1024);
|
|
232
|
+
xor_u8(a, b, out, out.length);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Example: histogram output (typed pointer)
|
|
236
|
+
|
|
237
|
+
```c
|
|
238
|
+
// @async
|
|
239
|
+
void histogram_u8(const uint8_t* data, int n, uint32_t* out256) {
|
|
240
|
+
for (int i = 0; i < 256; i++) out256[i] = 0;
|
|
241
|
+
for (int i = 0; i < n; i++) out256[data[i]]++;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
import { loadNative } from 'relaxnative';
|
|
247
|
+
|
|
248
|
+
const { histogram_u8 } = await loadNative('native/histogram.c', { isolation: 'worker' });
|
|
249
|
+
const data = new Uint8Array(1024 * 1024);
|
|
250
|
+
const out = new Uint32Array(256);
|
|
251
|
+
await histogram_u8(data, data.length, out);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
If you see a type error like `Unexpected Uint32Array value, expected number`, it usually means the C signature was parsed as a generic pointer instead of a typed pointer. Prefer fixed-width types like `uint32_t*`.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## When to use Relaxnative (good fits)
|
|
259
|
+
|
|
260
|
+
Relaxnative shines when you have **large batches** of work and the native call does enough computation to amortize the FFI overhead.
|
|
261
|
+
|
|
262
|
+
Good fits:
|
|
263
|
+
- CPU-bound kernels on large arrays (SIMD-able loops)
|
|
264
|
+
- image/audio primitives, DSP, analytics kernels, checksums/hashing
|
|
265
|
+
- tight numeric loops (matmul-ish, dot, saxpy) where JS becomes the bottleneck
|
|
266
|
+
- code you already have in C/C++/Rust and want to reuse from Node
|
|
267
|
+
- isolating risky/3rd-party native code in `process` mode with best-effort guards
|
|
268
|
+
|
|
269
|
+
### When *not* to use it (bad fits)
|
|
270
|
+
|
|
271
|
+
Avoid Relaxnative when:
|
|
272
|
+
- you're calling a native function **many times with tiny inputs** (per-call overhead dominates)
|
|
273
|
+
- the work is IO-bound (files/network); native won't magically make IO faster
|
|
274
|
+
- you need a strict sandbox (process guards are **not** a syscall-enforced sandbox)
|
|
275
|
+
- your function depends on complex C structs/callbacks (today's type support is intentionally small)
|
|
276
|
+
- the native code isn't deterministic/pure and can corrupt process memory
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## CLI
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
npx relaxnative --help
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Diagnostics
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npx relaxnative doctor
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Native test harness
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
npx relaxnative test native/examples --isolation worker
|
|
296
|
+
npx relaxnative test native/examples --isolation process
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Test signatures:
|
|
300
|
+
- `int test_name()` → `0` pass, non‑zero fail
|
|
301
|
+
- `const char* test_name()` → `NULL`/"" pass, non‑empty message fail
|
|
302
|
+
|
|
303
|
+
### Benchmarks
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npx relaxnative bench examples/add.c add --traditional
|
|
307
|
+
npx relaxnative bench examples/loop.c loop_sum --traditional --iterations 5 --warmup 1
|
|
308
|
+
npx relaxnative bench examples/buffer.c sum_u8 --traditional --iterations 3 --warmup 1
|
|
309
|
+
npx relaxnative bench examples/dot.c dot_f64 --traditional --iterations 2 --warmup 1
|
|
310
|
+
npx relaxnative bench examples/saxpy.c saxpy_f64 --traditional --iterations 1 --warmup 1
|
|
311
|
+
npx relaxnative bench examples/matmul.c matmul_f32 --traditional --iterations 1 --warmup 1
|
|
312
|
+
npx relaxnative bench examples/xor.c xor_u8 --traditional --iterations 1 --warmup 1
|
|
313
|
+
npx relaxnative bench examples/crc32.c crc32_u8 --traditional --iterations 1 --warmup 1
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Additional built-in demo baselines are provided for:
|
|
317
|
+
- `dot_f64` (dot product)
|
|
318
|
+
- `saxpy_f64` (vector kernel)
|
|
319
|
+
- `matmul_f32` (naive matrix multiply)
|
|
320
|
+
- `xor_u8` (buffer XOR)
|
|
321
|
+
- `crc32_u8` (checksum)
|
|
322
|
+
- `histogram_u8` (analytics/image primitive)
|
|
323
|
+
|
|
324
|
+
#### Benchmark results:
|
|
325
|
+
|
|
326
|
+
1. A Simple Vector Kernal
|
|
327
|
+
```bash
|
|
328
|
+
❯ npx relaxnative bench examples/saxpy.c saxpy_f64 --traditional --iterations 1 --warmup 1
|
|
329
|
+
traditional-js (baseline)
|
|
330
|
+
iterations: 1 (warmup 1)
|
|
331
|
+
calls/sec: 49.636
|
|
332
|
+
avg ms: 19.818
|
|
333
|
+
min ms: 19.818
|
|
334
|
+
max ms: 19.818
|
|
335
|
+
|
|
336
|
+
Speedup vs baseline (higher is better)
|
|
337
|
+
sync: 364.94x
|
|
338
|
+
worker: 368.56x
|
|
339
|
+
|
|
340
|
+
saxpy_f64 (sync)
|
|
341
|
+
iterations: 1 (warmup 1)
|
|
342
|
+
calls/sec: 18114.301
|
|
343
|
+
avg ms: 0.036
|
|
344
|
+
min ms: 0.036
|
|
345
|
+
max ms: 0.036
|
|
346
|
+
|
|
347
|
+
saxpy_f64 (worker)
|
|
348
|
+
iterations: 1 (warmup 1)
|
|
349
|
+
calls/sec: 18293.575
|
|
350
|
+
avg ms: 0.025
|
|
351
|
+
min ms: 0.025
|
|
352
|
+
max ms: 0.025
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
2. Matrix Multiplication
|
|
356
|
+
```bash
|
|
357
|
+
❯ npx relaxnative bench examples/matmul.c matmul_f32 --traditional --iterations 1 --warmup 1
|
|
358
|
+
traditional-js (baseline)
|
|
359
|
+
iterations: 1 (warmup 1)
|
|
360
|
+
calls/sec: 5.829
|
|
361
|
+
avg ms: 171.228
|
|
362
|
+
min ms: 171.228
|
|
363
|
+
max ms: 171.228
|
|
364
|
+
|
|
365
|
+
Speedup vs baseline (higher is better)
|
|
366
|
+
sync: 1240.74x
|
|
367
|
+
worker: 2710.71x
|
|
368
|
+
|
|
369
|
+
matmul_f32 (sync)
|
|
370
|
+
iterations: 1 (warmup 1)
|
|
371
|
+
calls/sec: 7232.070
|
|
372
|
+
avg ms: 0.112
|
|
373
|
+
min ms: 0.112
|
|
374
|
+
max ms: 0.112
|
|
375
|
+
|
|
376
|
+
matmul_f32 (worker)
|
|
377
|
+
iterations: 1 (warmup 1)
|
|
378
|
+
calls/sec: 15800.284
|
|
379
|
+
avg ms: 0.025
|
|
380
|
+
min ms: 0.025
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Cache
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
npx relaxnative cache status
|
|
387
|
+
npx relaxnative cache clean
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Registry (RelaxRegistry)
|
|
393
|
+
|
|
394
|
+
Install local packages (offline, deterministic):
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
npx relaxnative add file:examples/registry/fast-matrix
|
|
398
|
+
npx relaxnative list
|
|
399
|
+
npx relaxnative remove fast-matrix
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Trust levels
|
|
403
|
+
|
|
404
|
+
`relax.json`:
|
|
405
|
+
|
|
406
|
+
- `trust: "local" | "community" | "verified"`
|
|
407
|
+
|
|
408
|
+
Behavior:
|
|
409
|
+
- `local` → trusted, no prompts
|
|
410
|
+
- `community` → warnings + confirmation (only once; decision saved in `native/registry/.trust.json`)
|
|
411
|
+
- `verified` → silent install, requires `registrySignature`
|
|
412
|
+
|
|
413
|
+
### Verified signature
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"trust": "verified",
|
|
418
|
+
"registrySignature": { "alg": "sha256", "digest": "..." }
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Digest is computed over `relax.json` with `registrySignature` removed.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Express example
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
mkdir my-app
|
|
430
|
+
cd my-app
|
|
431
|
+
npm init -y
|
|
432
|
+
npm i express relaxnative
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
`native/loop.c`
|
|
436
|
+
|
|
437
|
+
```c
|
|
438
|
+
// @sync
|
|
439
|
+
long loop_sum(long n) {
|
|
440
|
+
long x = 0;
|
|
441
|
+
for (long i = 0; i < n; i++) x += i;
|
|
442
|
+
return x;
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`server.mjs`
|
|
447
|
+
|
|
448
|
+
```js
|
|
449
|
+
import express from 'express';
|
|
450
|
+
import { loadNative } from 'relaxnative';
|
|
451
|
+
|
|
452
|
+
const app = express();
|
|
453
|
+
const native = await loadNative('native/loop.c', { isolation: 'worker' });
|
|
454
|
+
|
|
455
|
+
app.get('/sum', (req, res) => {
|
|
456
|
+
const n = Number(req.query.n ?? 1_000_000);
|
|
457
|
+
res.json({ n, v: native.loop_sum(n) });
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
app.listen(3000, () => console.log('http://localhost:3000'));
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Developer documentation
|
|
466
|
+
|
|
467
|
+
High-level structure:
|
|
468
|
+
- `src/loader.ts` — compile + parse + bind + wrap
|
|
469
|
+
- `src/compiler/*` — compiler detection + cached compilation
|
|
470
|
+
- `src/parser/*` — Tree-sitter parsing + annotations
|
|
471
|
+
- `src/ffi/*` — koffi binding generation
|
|
472
|
+
- `src/worker/*` — worker/process isolation
|
|
473
|
+
- `src/registry/*` — registry installer + trust enforcement
|
|
474
|
+
|
|
475
|
+
Debug flags:
|
|
476
|
+
- `RELAXNATIVE_DEBUG=1`
|
|
477
|
+
- `RELAXNATIVE_TRACE=1` (prints extra call tracing; useful for debugging segfaults)
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## LLM Prompt (Architecture + code generation)
|
|
482
|
+
|
|
483
|
+
Copy/paste this prompt into ChatGPT / Claude / Copilot Chat when you want the model to plan and scaffold an app using Relaxnative.
|
|
484
|
+
|
|
485
|
+
### Prompt
|
|
486
|
+
|
|
487
|
+
You are a **Senior Node.js + Native Systems Engineer**.
|
|
488
|
+
|
|
489
|
+
I’m using the **Relaxnative** library for Node.js, which provides:
|
|
490
|
+
- `loadNative(path, { isolation })` to compile+load `.c/.cpp/.rs`
|
|
491
|
+
- isolation modes: `in-process`, `worker`, `process`
|
|
492
|
+
- native annotations: `@sync`, `@async`, `@cost low|medium|high`
|
|
493
|
+
- a CLI (`relaxnative doctor/test/bench/cache/add/list/remove`)
|
|
494
|
+
- RelaxRegistry packages with supply-chain trust levels: `local`, `community`, `verified`
|
|
495
|
+
- a small runtime safety guard layer in `process` isolation for permissions/timeouts
|
|
496
|
+
|
|
497
|
+
You must follow these rules:
|
|
498
|
+
- Prefer **fixed-width types** in C signatures (`uint32_t`, `uint8_t`, etc.) to avoid ambiguity.
|
|
499
|
+
- For bulk data, prefer **TypedArrays** (`Uint8Array`, `Float64Array`, `Uint32Array`) over lists.
|
|
500
|
+
- Avoid tiny-call micro-optimizations; solve performance by batching and reducing call count.
|
|
501
|
+
- If you can crash Node (native code!), default to `isolation: 'process'` during development.
|
|
502
|
+
|
|
503
|
+
**My question/problem:**
|
|
504
|
+
<PASTE YOUR PROBLEM HERE>
|
|
505
|
+
|
|
506
|
+
#### Your output must include:
|
|
507
|
+
|
|
508
|
+
1) **Feasibility & fit**
|
|
509
|
+
- Is this a good use case for Relaxnative? If no, explain briefly and propose a safer alternative.
|
|
510
|
+
- Identify which parts should remain in JS and which should become native.
|
|
511
|
+
|
|
512
|
+
2) **Isolation + security defaults**
|
|
513
|
+
- Choose an isolation mode and justify it.
|
|
514
|
+
- If 3rd-party code is involved, use `process` isolation and explain trust levels.
|
|
515
|
+
- Propose a `relax.json` permissions/limits policy if packaging a RelaxRegistry module.
|
|
516
|
+
|
|
517
|
+
3) **Native API design contract**
|
|
518
|
+
- Function signatures (C or Rust) with types suitable for FFI.
|
|
519
|
+
- How data buffers/arrays are passed (TypedArray ↔ pointer address / NativeBuffer).
|
|
520
|
+
- Error-handling strategy (return codes, sentinel values, etc.).
|
|
521
|
+
|
|
522
|
+
4) **Implementation plan**
|
|
523
|
+
- Step-by-step tasks (files to create, where they live).
|
|
524
|
+
- A minimal working prototype first, then optimizations.
|
|
525
|
+
|
|
526
|
+
5) **Code generation**
|
|
527
|
+
- Provide:
|
|
528
|
+
- native source file(s) with Relaxnative annotations
|
|
529
|
+
- the Node/TS loader code using `loadNative()`
|
|
530
|
+
- a benchmark command using `npx relaxnative bench ... --traditional`
|
|
531
|
+
- optional: a test using Vitest
|
|
532
|
+
|
|
533
|
+
6) **Performance checklist**
|
|
534
|
+
- Specify what to measure and how.
|
|
535
|
+
- Identify what sizes/iteration counts are needed to overcome FFI overhead.
|
|
536
|
+
|
|
537
|
+
Constraints:
|
|
538
|
+
- Use ESM syntax.
|
|
539
|
+
- Prefer deterministic builds and offline-friendly behavior.
|
|
540
|
+
- Keep the first version simple and correct.
|
|
541
|
+
|
|
542
|
+
### Minimal copy/paste prompt (for ChatGPT / Claude)
|
|
543
|
+
|
|
544
|
+
Paste this when you want an LLM to generate a Relaxnative kernel:
|
|
545
|
+
|
|
546
|
+
> You are a Senior Node.js + C/Rust engineer. Generate a Relaxnative native kernel.
|
|
547
|
+
>
|
|
548
|
+
> Requirements:
|
|
549
|
+
> - Provide a `.c` (or `.rs`) file with exported functions.
|
|
550
|
+
> - Use annotations on the 1 lines above each function: `@sync`/`@async` and `@cost low|medium|high`.
|
|
551
|
+
> - Use fixed-width C types where possible: `uint8_t`, `uint32_t`, `int32_t`, etc.
|
|
552
|
+
> - For buffers, use `uint8_t*` and pass `Uint8Array` from JS.
|
|
553
|
+
> - For `uint32_t*` outputs, pass `Uint32Array` from JS.
|
|
554
|
+
> - Provide a Node ESM usage snippet using `loadNative(path, { isolation: 'worker' })`.
|
|
555
|
+
> - Provide a benchmark command using: `npx relaxnative bench <file> <fn> --traditional`.
|
|
556
|
+
> - Include a quick correctness test (Vitest preferred).
|
|
557
|
+
> Example:
|
|
558
|
+
> ```ts
|
|
559
|
+
> import { loadNative } from 'relaxnative';
|
|
560
|
+
>
|
|
561
|
+
> const { add } = await loadNative('./add.so', { isolation: 'worker' });
|
|
562
|
+
>
|
|
563
|
+
> // Test the native function
|
|
564
|
+
> test('add', () => {
|
|
565
|
+
> expect(add(1, 2)).toBe(3);
|
|
566
|
+
> });
|
|
567
|
+
> ```
|
|
568
|
+
>
|
|
569
|
+
>```c
|
|
570
|
+
> #include <stdint.h>
|
|
571
|
+
>
|
|
572
|
+
> // Example native function
|
|
573
|
+
> @sync @cost low
|
|
574
|
+
> uint32_t add(uint32_t a, uint32_t b) {
|
|
575
|
+
> return a + b;
|
|
576
|
+
> }
|
|
577
|
+
>
|
|
578
|
+
>```
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Support ☕
|
|
585
|
+
|
|
586
|
+
If you found this project helpful, consider buying me a coffee!
|
|
587
|
+
|
|
588
|
+
[](https://www.buymeacoffee.com/ravikisha)
|
|
589
|
+
|
|
590
|
+
## License
|
|
591
|
+
|
|
592
|
+
MIT © Ravi Kishan [Portfolio](https://www.ravikishan.me)
|