Writing Extensions
Extensions are shared libraries (.so) written in C that register new JavaScript functions and objects into the runtime.
Extension Structure
Every extension needs:
- Include
js-cgi-module.h - An init function that registers globals
- An optional shutdown function for cleanup
- The
JSCGI_MODULEmacro to export the entry point
Minimal Example
#include "js-cgi-module.h"
static JSValue js_hello(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
return JS_NewString(ctx, "Hello from my extension!");
}
static int my_ext_init(JSContext *ctx, JSValue global) {
JS_SetPropertyStr(ctx, global, "hello",
JS_NewCFunction(ctx, js_hello, "hello", 0));
return 0;
}
static int my_ext_shutdown(void) {
return 0;
}
JSCGI_MODULE(my_ext, "1.0.0", my_ext_init, my_ext_shutdown);
Building
gcc -shared -fPIC -O2 -I/usr/include/js-cgi -o my_ext.so my_ext.c
The -I flag points to the directory containing js-cgi-module.h (installed to /usr/include/js-cgi/ or included in the download archive).
Installing
sudo cp my_ext.so /usr/lib/js-cgi/modules/
Add to /etc/js-cgi/js-cgi.ini:
extension = my_ext.so
The JSCGI_MODULE Macro
JSCGI_MODULE(name, version, init_function, shutdown_function);
| Parameter | Description |
|---|---|
name | Extension identifier (no quotes, valid C identifier) |
version | Version string |
init_function | Called on startup, receives JSContext and global object |
shutdown_function | Called on shutdown, can be NULL |
Registering Functions
The init function receives the js-cgi context and global object. Use the js-cgi C API to register functions:
static JSValue js_add(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
int a, b;
JS_ToInt32(ctx, &a, argv[0]);
JS_ToInt32(ctx, &b, argv[1]);
return JS_NewInt32(ctx, a + b);
}
static int math_init(JSContext *ctx, JSValue global) {
JSValue math = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, math, "add",
JS_NewCFunction(ctx, js_add, "add", 2));
JS_SetPropertyStr(ctx, global, "math", math);
return 0;
}
Working with Arguments
// Get string argument
const char *str = JS_ToCString(ctx, argv[0]);
// ... use str ...
JS_FreeCString(ctx, str);
// Get integer
int32_t num;
JS_ToInt32(ctx, &num, argv[0]);
// Get float
double d;
JS_ToFloat64(ctx, &d, argv[0]);
// Check argument count
if (argc < 2) {
return JS_ThrowTypeError(ctx, "Expected 2 arguments");
}
Returning Values
// String
return JS_NewString(ctx, "hello");
// Integer
return JS_NewInt32(ctx, 42);
// Float
return JS_NewFloat64(ctx, 3.14);
// Boolean
return JS_NewBool(ctx, 1);
// Null
return JS_NULL;
// Object
JSValue obj = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, obj, "name", JS_NewString(ctx, "Alice"));
JS_SetPropertyStr(ctx, obj, "age", JS_NewInt32(ctx, 30));
return obj;
// Array
JSValue arr = JS_NewArray(ctx);
JS_SetPropertyUint32(ctx, arr, 0, JS_NewString(ctx, "a"));
JS_SetPropertyUint32(ctx, arr, 1, JS_NewString(ctx, "b"));
return arr;
// Error
return JS_ThrowTypeError(ctx, "Something went wrong: %s", detail);
Linking External Libraries
If your extension uses an external library, link it at compile time:
# Example: extension using libpq (PostgreSQL)
gcc -shared -fPIC -O2 -I/usr/include/js-cgi -o pg.so pg.c -lpq
The main js-cgi binary exports all required symbols, so extensions resolve them at runtime without additional linking.
Shutdown Function
The shutdown function is called after script execution, before the runtime is freed. Use it to clean up resources:
static int my_ext_shutdown(void) {
// Close connections, free memory, etc.
return 0;
}