Javascript Extensibility Example
Javascript Extensibility Example
This documentation shows how to use extensibility scenarios that are experimentally deployed
in update 9. The APIs described here are minimal and will evolve in the future. They are
manually deployed, which currently prevents installation in the Cloud. They are usable only in
an Early adopter program context.
This article explains how to use asynchronous 'node.js' API in JavaScript bundles.
This article shows how to implement a bundle that allows 4GL code to call JavaScript zip and
unzip functions.
As explained in the previous article the first step is to create a bundle for the extension. This
bundle has the following structure:
xa1-zlib/
package.json
lib/
zlib-helper._js
"name": "xa1-zlib",
"version": "1.0.0",
"private": true,
"sage": {
"x3": {
"extensions": {
"4gl-apis": [{
"module": "./lib/zlib-helper"
}]
The 4gl-apis extension key indicates that the package contains a 4GL API extension. The
module gives the relative path to the JavaScript modules that implements the extension.
Asynchronous JavaScript
This is because 'node.js' makes heavy use of asynchronous APIs and SAFE X3 uses a special tool
called streamline.js for asynchronous programming. This tool uses the ._js extension in place of
the usual .js extension.
In 'node.js', any function that performs I/O (reads or write files, communicates over a network)
must be asynchronous. Asynchronous functions do not return their result directly, as a normal
synchronous function; instead, they return it through a callback. Here is a typical example:
var fs = require('fs');
console.log("before read");
console.log("read completed");
});
read in progress
read completed
file contents = Quand le ciel bas et lourd pèse comme un couvercle ...
As this little example demonstrates, callbacks impose a special structure to the code. It often
forces the developer to decompose its processes into very small functions and the order in
which these functions are called can be surprising at first (the fact that "read in progress ..." is
printed before "read completed" in the example above).
var fs = require('streamline-fs');
console.log("before read");
console.log("read completed");
The callback has been replaced by _ and the result of the contents of the file is now returned
by
'Streamline.js' is a public Open Source project. You can find documentation on its GitHub
README page, as well as tutorial and a FAQ.
Next, you can create our zlib-helper._js JavaScript module. Here is the code:
exports.zip(text, _) {
// our input is text - convert it to a node.js Buffer
return zipped;
exports.unzip(data, _) {
return text;
Note: SAFE X3 uses 'streamline.js' in fast mode. This option produces more efficient code, but
it requires a bit of extra care when calling asynchronous functions. The rule is the following:
If you call an asynchronous function that is implemented with 'streamline.js', you must pass _
as callback.
If you call an asynchronous function that belongs to the core 'node.js' API, or to a third party
package that was not implemented with 'streamline.js', then you must pass ~_ as callback.
In the example above, zlib.zip and zlib.unzip are native 'node.js' APIs. So you call them with ~_.
Deployment is easy: you just need to add your extension directory under the node_modules
directory of the SAFE X3 'node.js' server, and restart the 'node.js' server.
Do not forget to restart the 'node.js' server everytime you make a change to your bundle.
Otherwise it will not pick up the latest code.
Now you can write a small 4GL API that delegates its work to these asynchronous functions:
Funprog ZIP(TEXT)
Clbfile TEXT
"xa1-zlib/lib/zlib-helper", # MODULE
"zip", # FUNCTION
End fromBase64(RESBODY)
Funprog UNZIP(DATA)
Blbfile DATA
"xa1-zlib/lib/zlib-helper", # MODULE
"unzip", # FUNCTION
End RESBODY
Now you can use this 4GL API to zip and unzip texts:
Subprog TESTZIP()
End
Links
node.js API
streamline.js