Published on June 4, 2023.
Over the past few months, I've been working on a project called Aloe.js, and I'm excited to finally announce and give a preview of it.
Aloe is a JavaScript interpreter. It's designed for running JavaScript applications outside of a Web browser. It can be run from the command-line on macOS, Windows, and Linux-based computers.
I developed Aloe for two reasons:
1. So that I can develop applications using the same language that I use in my NetSuite development work. (NetSuite's programming language, SuiteScript, is based on JavaScript).
2. So that I can develop embedded IoT applications using JavaScript.
Perhaps the best way to explain what Aloe is and what it does is to demonstrate it. Here's a short video that shows Aloe running through a variety of tests.
Click the image to view a larger version.
In that example, I'm running Aloe from the macOS terminal command line, and passing it the name of the JavaScript file that I want it to run. (The "--allow-all" is a command line argument that indicates to Aloe that I'm giving it permission to do things like read and write files, make cURL calls, and so on.) It's a basic example, but I think it does a good job of showing some of the things that Aloe is capable of. If you'd like to see the JavaScript file that is being run in the video, see below.
Another example is shown here. In this example, Aloe is integrating with a NetSuite instance using SuiteTalk REST. The app uses a SuiteQL query to retrieve information about employees, and the query results are displayed. Again, if you're interested in seeing what the JavaScript file looks like, see below.
Click the image to view a larger version.
A "real world" example of Aloe - an app that I've developed for one of my clients, who has been using it for a few months now - integrates with NetSuite via SuiteAPI. The app uses SuiteQL to get a list of recent sales orders. It then downloads each of the sales order PDFs, and then uploads the PDFs to an Amazon S3 bucket.
Aloe is cross-platform, with compiled binaries for macOS, Windows, and Linux. To use Aloe, you simply download it, and tell it what JavaScript file to run. There's nothing else to download, nothing to compile.
Aloe uses Duktape, an embeddable Javascript engine. This makes it ideal for apps that need to run on devices that have minimal resources.
Aloe supports JavaScript modules based on the CommonJS modules specification.
It includes a basic filesystem API for reading and writing files. It also includes extensions to simplify NetSuite and S3 integration. In the future, I'm planning to add extensions for connecting to relational databases (SQLite, MySQL, SQL Server, etc), for generating Excel spreadsheets, connecting to Phidgets, MQTT, and more.
Aloe also provides an interactive mode that's similar to Node's Read-Eval-Print-Loop (REPL) shell.
I developed Aloe using Xojo, a cross-platform rapid app development tool. And Aloe uses several of the popular MBS Xojo plugins.
Speaking of Node, one of the questions that I've been getting is essentially, "Why don't you just use Node?"
I developed Aloe because I wanted a way to run JavaScript-based apps without the overheard and complexity of Node. It's really as simple as that.
That said, Aloe isn't fully on par with Node in terms of functionality. In the current version, Aloe doesn't support network / Web applications. In other words, you can't use it to host JavaScript-based Web apps. (At least not yet. I'm working on that.)
Also, Aloe uses Duktape as its JavaScript engine, while Node uses the V8 JavaScript engine. V8 is more powerful than Duktape, and it supports all of the most recent JavaScript specs. Duktape supports ECMAScript E5/E5.1, and partially supports ECMAScript 2015 (E6) as well as ECMAScript 2016 (E7). (For details, click here.) But the advantage of Duktape is that it has been specifically designed "with a focus on portability and compact footprint."
I started privately beta testing Aloe a few weeks ago, and so far the feedback has been very positive. The developers that have been testing it have primarily used it to develop batch processing applications. For example, one developer is using it to extract orders from an ecommerce system and upload them into their order processing system. Another developer is using Aloe to pull inventory data from their ERP system and upload the data to an FTP server.
I'm planning to begin publicly beta testing Aloe in a few weeks. If you're interested in participating in the beta, please signup for my newsletter. I'll be announcing the beta there first.
One more thing: I'll be providing Aloe.js free of charge. I'll provide basic, email-based technical support for Aloe, but will be charging for priority support, consulting, customizations, and so on.
writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln( 'Aloe.JS Demo'); writeln( '------------------------------------------------------------------------------------------'); writeln(); // Beep / Prompt Example beep(); var name = prompt('What is your name? '); writeln( ' Hello, ' + name + '!'); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); // Loop w/ write, writeln, and sleep example. for ( var i = 3; i > 0; i-- ) { write( i.toString() + '... ' ); sleep( 1000 * 1.0 ); } writeln(); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); // Get the start time. var t1 = performance.now(); // Example of the Aloe constant. writeln('Aloe Information:') writeln( JSON.stringify( aloe, null, 2 ) ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); // Use shell to get environment variables. var env = shell('env'); writeln('Environment Variables:'); writeln( env ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); // Example of reading and writing a file. writeln( 'Reading file (content-test.txt)...' ); var fileContents = readFile('content-test.txt'); writeln(); writeln('The file\'s contents:'); writeln(); writeln( fileContents ); writeln(); writeln(); writeln(); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); var d = new Date(); fileContents = "Updated " + d.toString() + " " + fileContents; writeln( 'Writing an updated version of the file (content-test-copy.txt)...' ); var writeStatus = writeFile('content-test-copy.txt', fileContents); writeln( writeStatus ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); // Simple shell example. writeln('Files in Downloads folder:') var ls = shell( 'ls ~/Downloads' ); writeln( ls ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); // cURL example, with JSON-encoding, etc. var response = shell('curl -s "https://swapi.dev/api/people/1"') var responseJSON = JSON.parse( response ); writeln('Star Wars API Call Response:'); writeln( JSON.stringify( responseJSON, null, 2 ) ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); // Local function example. // Random string generated using a function defined in the document // Source: https://modernweb.com/45-javascript-tips-tricks-practices/ function generateRandomAlphaNum(len) { var rdmString = ""; for( ; rdmString.length < len; rdmString += Math.random().toString(36).substr(2)); return rdmString.substr(0, len); } writeln('Random Strings:'); writeln( '• ' + generateRandomAlphaNum(64) ); writeln( '• ' + generateRandomAlphaNum(64) ); writeln( '• ' + generateRandomAlphaNum(64) ); writeln( '• ' + generateRandomAlphaNum(64) ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); writeln('Regular Expression Tests:'); const pattern = /^[https?]+:\/\/((w{3}\.)?[\w+]+)\.[\w+]+$/i; var url = 'https://www.aloejs.com'; writeln( '• ' + url + ' is valid: ' + pattern.test(url) ); url = 'https://aloejs.com'; writeln( '• ' + url + ' is valid: ' + pattern.test(url) ); url = 'https://aloejs'; writeln( '• ' + url + ' is valid: ' + pattern.test(url) ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); writeln( 'Module Loading Example #1' ); var foo = require( 'foo.js' ); foo.hello(); var bar = require( 'bar.js' ); bar.hello(); writeln( bar.text ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); writeln( 'Module Loading Example #2 (Moment.js)' ); var moment = require( 'moment.min.js' ); writeln( moment().format('MMMM Do YYYY, h:mm:ss a') ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); writeln( 'Encryption Tests' ); writeln(); var key = 'Some Key'; var data = 'Some Data'; writeln( 'Key: ' + key ); writeln( 'Data: ' + data ); writeln(); writeln( 'SHA256 w/ Base64 Encoding: ' ); writeln( base64Encode( sha256( key, data ) ) ); writeln(); writeln( 'SHA512 w/ Base64 Encoding: ' ); writeln( base64Encode( sha512( key, data ) ) ); writeln(); key = 'The quick brown fox jumps over the lazy dog.'; writeln( 'Key: ' + key ); writeln(); writeln( 'MD5 w/ Base64 Encoding: ' ); writeln( base64Encode(md5( key, data ) ) ); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln(); writeln('Process Complete'); writeln(); var t2 = performance.now(); var elapsed = ( t2 - t1 ) / 1000; writeln('Elapsed Time: ' + elapsed + ' seconds' ); writeln();
var t1 = performance.now(); writeln(); writeln(); writeln(); writeln( '------------------------------------------------------------------------------------------'); writeln( 'Aloe - SuiteTalk REST - SuiteQL Query Example' ); writeln( '------------------------------------------------------------------------------------------'); writeln(); var ns = require( 'netsuite-creds-ironforge-suitetalk.js' ); var query = <<<EOQ SELECT ID, COALESCE( FirstName, '' ) AS FirstName, COALESCE( LastName, '' ) AS LastName, Email FROM Employee WHERE ( IsInactive = 'F' ) AND ( Email LIKE '%test.com' ) ORDER BY LastName, FirstName EOQ; var payload = { q: query } var nsConnectRequest = { accountNumber: ns.accountNumber, consumerKey: ns.consumerKey, consumerSecret: ns.consumerSecret, tokenID: ns.tokenID, tokenSecret: ns.tokenSecret, method: 'POST', url: ns.suiteTalkURL + '/services/rest/query/v1/suiteql', connectionTimeout: 10, timeout: 10, payload: JSON.stringify( payload ) } var nsConnectResponse = nsConnect( JSON.stringify( nsConnectRequest, null, 5 ) ); nsConnectResponse = JSON.parse( nsConnectResponse ); var t2 = performance.now(); var elapsed = ( t2 - t1 ) / 1000; writeln( 'Retrieved ' + nsConnectResponse.count + ' records in ' + elapsed.toFixed(2) + ' seconds. ' ); var records = nsConnectResponse.items; for (var i = 0; i < records.length; i++) { var record = records[i]; writeln( record.lastname + ', ' + record.firstname + ' (Employee ID ' + record.id + ')' ); }
Hello, I'm Tim Dietrich. I develop custom software for businesses that are running on NetSuite, including mobile apps, Web portals, Web APIs, and more.
I'm the developer of several popular NetSuite open source solutions, including the SuiteQL Query Tool, SuiteAPI, and more.
I founded SuiteStep, a NetSuite development studio, to provide custom software and AI solutions - and continue pushing the boundaries of what's possible on the NetSuite platform.
Copyright © 2025 Tim Dietrich.