UPDATE: AS OF 2023-09-26 THE FRENCH POLICE TRIED INTIMIDATING ME TO STOP TALKING ABOUT THIS AFFAIR - DURING AN ILLEGAL RAID WITH DRAWN GUNS AT MY HOME - AT THIS POINT THE NODE.JS PROJECT ALSO DECIDED TO BAN ME FROM THEIR REPOSITORIES UNLESS I AGREE TO STOP DISCUSSING THE EXTORTION. SHOULD THEY RECONSIDER THIS DECISION, I AM STILL WILLING TO MERGE THIS PR. I COMPLETELY REFUSE TO STOP DISCUSSING THE JUDICIARY CORRUPTION.
My final GSoC 2022 report can be found here: https://github.com/mmomtchev/mmomtchev/blob/master/GSoC-2022/ZOO-Project-mmomtchev-GSoC-2022-Final-Report.md
This repository contains the packaging used to generate the Ubuntu and Debian (upcoming) packages of libnode
with PR #43542 bringing full Node-API and node-addon-api
support to libnode
.
Ubuntu PPA:
Unlike the distributions built-in packages, these packages:
Include PR#43542 allowing to embed the Node.js entirely through the binary stable Node-API from both C and C++. This PR might or might not get merged (it is currently under discussions)
I am currently maintaining this project as a semi-permanent fork because, as part of the extortion about the judicial affairs that include corruption, I am being blocked from contributing to open-source projects.
Are similar to the NodeSource binary distributions
libnode headers are installed in /usr/include/libnode
to avoid a conflict with an eventual NodeSource nodejs
installation - you should make sure that /usr/include/libnode
appears before /usr/include/node
in your compiler search path
Use self-contained packages that include all Node.js dependencies in the same executable - as very few people use both libnode
and libuv
or v8
independently in the same project bundling these separately has no real benefit
Provide current versions even for old distributions
Are not supported on all hardware platforms
Are not restrained by Debian dogma (some of the included dependencies are not fully GPL-compatible - but it is still qualifies as free software)
node-addon-api
is also available as a separate package
The packages have been developed as part of GSoC 2022 on a project sponsored by the Open Source Geospatial Foundation and are copyright by Google and distributed under MIT License.
I am maintaining them as a courtesy to the open source community.
Highly experimental
The C/C++ code is linked against a shared library (about 80Mb) which includes a fully self-contained Node.js runtime in which the standard bootstrap code has been modified.
Running napi_create_platform
initializes Node.js/V8 and running napi_create_environment
creates an isolate which can be roughly compared to one worker_thread
in Node.js. An environment can be accessed only by the C/C++ thread which created it. When calling into JavaSscript, Node.js/V8 run in the context of the calling thread.
A special method, napi_run_environment
allows the draining of the event loop - which also happens in the context of the worker thread. While the C/C++ code is running, the event loop is not. This means that if the C/C++ code does not call napi_run_environment
often enough, background network transfers will eventually overrun their buffers and will start failing. If the C/C++ code is very CPU-intensive, the all Node.js/V8 interaction should probably be relegated to a separate thread. This is basically the same rule as when combining async code with CPU-intensive tasks in Node.js. The only difference is that JS must exit its currently running function to resume the background processing, while C/C++ must call napi_run_environment
.
All JS objects are managed in the Node.js/V8 heap and may be accessed only through the napi_
primitives and only in the thread that created the environment. All the napi_*
functions come from the standard Node-API that is used for Node.js native addons - these are intercepted by the creation of a special napi_env
that represents the embedded environment. The structure that renders this possible is hidden in the environment instance data - so napi_set_instance_data
and napi_get_instance_data
are never to be used on this environment.
runMicroTasks is undefined
- Your program is raising an exception in a C/C++ async handler that will lead to program termination anyway - it is just that the message is very cryptic - this happens because the exception is processed in a context without builtinsaxios_example
crashes - Your installed libnode
version does not match the exampleinspect-brk
: this can be worked around by adding require('inspector').waitForDebugger(); debugger;
in the beginning of the source file (which is in fact the bootstrapper) or refer to debugger-example.cc for a clean solution.error: ‘napi_create_platform’ was not declared in this scope
- make sure your code includes this before the includes:
#define NAPI_EXPERIMENTAL
#define NAPI_EMBEDDING
Node.js 16.x and (in progress) Node.js 18.x on
sudo add-apt-repository ppa:mmomtchev/libnode
sudo apt update
sudo apt install libnode93 libnode-dev # C only
sudo apt install node-addon-api # with C++
Node.js 16.x branch
git clone -b napi-libnode-v16.x https://github.com/mmomtchev/node.git
cd node && ./configure --shared && make -j4
Node.js 18.x branch
git clone -b napi-libnode-v18.x https://github.com/mmomtchev/node.git
cd node && ./configure --shared && make -j4
Node.js main branch
git clone -b napi-libnode https://github.com/mmomtchev/node.git
cd node && ./configure --shared && make -j4
C++ API extensions
git clone -b napi-embedding https://github.com/mmomtchev/node-addon-api.git
// Then include `napi.h` from $(pwd)/node-addon-api
This version of libnode
can be used from both C and C++ with a simple Node-API interface:
Compile with:
gcc -I/usr/include/libnode -o libnode-napi-example libnode-napi-example.c -lnode
#include <stdio.h>
#include <string.h>
#define NAPI_EXPERIMENTAL
#include <node_api.h>
int main() {
// !!! All napi calls for one given environment must
// !!! be made from the same thread that created it
// (except everything napi_threadsafe_function related)
// This the V8 engine, there must be only one
napi_platform platform;
// This is a V8 isolate, there may be multiple
napi_env env;
// This holds local references, when it is closed
// they become available to the GC
napi_handle_scope scope;
// These are JS values
napi_value global;
napi_value key;
napi_value cb;
napi_value result;
const char *main_script = "console.log('hello world'); "
"function callMe() { console.log('called you'); }"
// or you can use vm.runInThisContext
"global.callMe = callMe;";
// Do only once
if (napi_create_platform(0, NULL, 0, NULL, NULL, 0, &platform) != napi_ok) {
fprintf(stderr, "Failed creating the platform\n");
return -1;
}
// Do for each environment (V8 isolate)
// 'hello world' will be printed here
if (napi_create_environment(platform, NULL, main_script, &env) != napi_ok) {
fprintf(stderr, "Failed running JS\n");
return -1;
}
// Here you can interact with the environment through Node-API env
// (refer to the Node-API doc)
if (napi_get_global(env, &global) != napi_ok) {
fprintf(stderr, "Failed accessing the global object\n");
return -1;
}
napi_create_string_utf8(env, "callMe", strlen("callMe"), &key);
if (napi_get_property(env, global, key, &cb) != napi_ok) {
fprintf(stderr, "Failed accessing the global object\n");
return -1;
}
// This cycle can be repeated
{
// Call a JS function
// V8 will run in this thread
if (napi_call_function(env, global, cb, 0, NULL, &result) != napi_ok) {
fprintf(stderr, "Failed calling JS callback\n");
return -1;
}
// (optional) Call this to flush all pending async callbacks
// V8 will run in this thread
if (napi_run_environment(env) != napi_ok) {
fprintf(stderr, "Failed flushing pending JS callbacks\n");
return -1;
}
}
// Shutdown everyhing
napi_close_handle_scope(env, scope);
if (napi_destroy_environment(env, NULL) != napi_ok) {
return -1;
}
if (napi_destroy_platform(platform) != napi_ok) {
fprintf(stderr, "Failed destroying the platform\n");
return -1;
}
return 0;
}
Compile with
g++ -I/usr/include/libnode -I/usr/include/node -o libnode-napi libnode-napi-example.cc -lnode
#include <stdio.h>
#define NAPI_EXPERIMENTAL
#define NAPI_EMBEDDING
#include <napi.h>
int main() {
// !!! All napi calls for one given environment must
// !!! be made from the same thread that created it
// (except everything napi_threadsafe_function related)
try {
// This the V8 engine, there must be only one
Napi::Platform platform;
// This is the custom bootstrap script if you require any
// (or you can use the default which provides require and import)
const char *main_script =
"console.log('hello world'); "
"function callMe(s) { console.log('called ' + s); }"
// or you can use vm.runInThisContext
"global.callMe = callMe;";
// This is a V8 isolate, there may be multiple
// 'hello world' will be printed here
Napi::PlatformEnv env(platform, main_script);
try {
// This holds local references, when it is closed
// they become available to the GC
// Here you can interact with the environment through Node::Env
// (refer to the node-addon-api doc)
Napi::HandleScope scope(env);
Napi::Object global = env.Global();
Napi::Function callMe = global.Get("callMe").As<Napi::Function>();
// This cycle can be repeated
{
// Call a JS function
// V8 will run in this thread
callMe({Napi::String::New(env, "you")});
// (optional) Call this to flush all pending async callbacks
// V8 will run in this thread
env.Run();
}
} catch (const Napi::Error &e) {
fprintf(stderr, "Caught a JS exception: %s\n", e.what());
}
} catch (napi_status r) {
fprintf(stderr, "Failed initializing the JS environment: %d\n", (int)r);
}
return 0;
}
node_modules
from C++libnode
supports loading of both CJS and ES6 modules from C and C++, refer to
CJS and ES6
#include <stdio.h>
#define NAPI_EXPERIMENTAL
#define NAPI_EMBEDDING
#include <napi.h>
int main() {
try {
Napi::Platform platform;
Napi::PlatformEnv env(platform);
try {
Napi::HandleScope scope(env);
// require axios
// The default bootstrap script creates a ES6/CJS-compatible
// environment with global.require() and global.import()
Napi::Function require =
env.Global().Get("require").As<Napi::Function>();
Napi::Object axios =
require({Napi::String::New(env, "axios")}).ToObject();
// As this is an async function, it will return immediately
// Async code should be called with MakeCallback instead of
// a normal Call - otherwise the Promise/nextTick handlers
// might not run
Napi::Promise r =
axios.Get("get")
.As<Napi::Function>()
.MakeCallback(
env.Global(),
{Napi::String::New(env, "https://www.google.com")})
.As<Napi::Promise>();
// At this point the event loop is stopped, unless the
// function returned an already resolved Promise, it won't
// get resolved until the event loop is restarted If the
// event loop is not restarted soon enough, the network will
// eventually timeout - same as in Node.js
// Promise resolve handler
// (same as JS - we retrieve the `then` property, which is a
// function, then we call it, passing a handler as argument
// and the Promise as this)
r.Get("then").As<Napi::Function>().Call(
r,
{Napi::Function::New(env, [](const Napi::CallbackInfo &info) {
// If you throw here, your program will get
// terminated - same as JS - but with a very
// cryptic message about `runMicroTasks` being
// undefined
Napi::HandleScope scope(info.Env());
if (!info[0].IsObject()) {
printf("Axios returned: %s\n",
info[0].ToString().Utf8Value().c_str());
return;
}
std::string data =
info[0].ToObject().Get("data").ToString().Utf8Value();
printf("Result is:\n\n%s\n", data.c_str());
})});
// Promise reject handler
// (if you want to catch exceptions in `then` you have to
// attach your handler to the value returned by `then` -
// here you are attaching to the base Promise itself)
r.Get("catch").As<Napi::Function>().Call(
r,
{Napi::Function::New(env, [](const Napi::CallbackInfo &info) {
Napi::HandleScope scope(info.Env());
if (!info[0].IsNull()) {
printf("Axios error: %s",
info[0].As<Napi::Error>().what());
return;
}
})});
// This will have the effect of a JS await - it will restart
// the event loop (ie one of the above 2 lambdas will run
// here)
env.Run();
// All async tasks have been completed
} catch (const Napi::Error &e) {
fprintf(stderr, "Caught a JS exception: %s\n", e.what());
return -1;
}
} catch (napi_status r) {
fprintf(stderr, "Failed initializing Node.js environment: %d\n",
(int)r);
return -1;
}
return 0;
}
made in Annecy
made on solar power