Bixby Developer Center

Build Conversational Experiences
Guides
References

JavaScript Runtime Version 2

Bixby provides two JavaScript runtime systems your capsule's action implementations can execute on:

  • Version 1 is built on Mozilla Rhino, a legacy engine with limited support for language features introduced after ECMAScript 5.1 (2011).
  • Version 2 is built on Google V8, the engine that powers Node.js. This offers compatibility with more modern JavaScript through the ES 2020 (11th Edition) specification, and provides a faster, more secure foundation for your capsule's execution.

Capsules will continue executing on V1 unless your capsule configuration specifically enables V2 through a runtime flag. The new runtime system is not backward compatible. If you wish to transition an existing capsule from V1 to V2, the capsule's code will need to be modified.

Enabling JavaScript Runtime Version 2

Note

The Javascript Runtime selection is "all or nothing": all your capsule's JavaScript will run on the same runtime system. There is no way to specify the engine on a per-function or per-file basis.

The JavaScript runtime version is set by the js-runtime-version key in your capsule.bxb file:

capsule {
runtime-version (7) {
js-runtime-version (2)
}
}

If this key is not specified, the runtime version will default to the version set by the current runtime-version of the capsule. (Currently, this will always be Version 1.)

API Differences

There are several differences between JavaScript Runtime V1 and V2 to be aware of, both when migrating code manually between runtime versions and when reviewing examples in Bixby documentation.

Caution

Bixby's documentation is still being updated with examples for JavaScript Runtime V2, so some examples may still be written in the V1 style. Most sample code for Bixby is also written to V1's API. Translating between the two styles is simple, but you'll need to watch for this difference!

Importing Modules

In JavaScript Runtime V1, modules (both ones contained in your capsule and Bixby's JavaScript API modules) are imported using the non-standard require() function:

var http = require('http');

In V2, you should use the standard module import syntax:

import http from 'http';

import niftyModule from './lib/nifty';

You cannot import a constant directly, but you can set a constant from an imported module's constant after import:

import niftyModule from './lib/nifty';

const FOOBAR = niftyModule.FOOBAR;
Caution

You cannot require or import modules dynamically based on the contents of a variable. Imported modules must be declared statically.

This will work:

// correct
import http from 'http';

Both of these will cause errors:

// will not work
const http = require(filename);

// will not work
import http from filename;

Function Arguments

Suppose your capsule has an action to find a Thing:

action (FindThing) {
type (Search)
collect {
input (arg1) {
type (Arg1)
}
input (arg2) {
type (Arg2)
}
}
output (Thing)
}

In V1, functions receive one or more parameters that correspond to the input keys in the action.

function findThing (arg1, arg2, $vivContext) {
return main.findThing(arg1, arg2, $vivContext);
}

In V2, functions instead receive a single parameter, an object with key/value pairs whose keys correspond to the input keys in the action. You can use destructuring assignment to assign variables from these keys.

function findThing (input) {
const { arg1, arg2, $vivContext } = input;
return main.findThing(arg1, arg2, $vivContext);
}
Note

JavaScript's destructuring assignment from object keys is based on order, not name. The keys will be in the order of input keys in the collect block, with the $vivContext key always being last.

Exporting Functions

In V1, functions are exported using CommonJS's module.exports method:

const main = require('./lib/main.js');

module.exports.function = function findThing (arg1, arg2, $vivContext) {
return main.findThing(arg1, arg2, $vivContext);
}

In V2, functions are exported using the standard export syntax. You can specify the handler function as a default export:

import main from './lib/main.js';

export default function (input) {
const { arg1, arg2, $vivContext } = input;
return main.findThing(arg1, arg2, $vivContext);
}
Caution

While the standard JS export allows other formats, such as export const or arrow function notation, only export default or export function endpoint-name will work for V2 function exports.

This is the matching endpoint:

endpoints {
action-endpoints {
action-endpoint (FindThing) {
local-endpoint (FindThing.js)
accepted-inputs (arg1, arg2, $vivContext)
}
}
}

Rather than using the default export, you can give a function a name, and specify it in the local-endpoint key.

import main from './lib/main.js';

export function findThing(input) {
const { arg1, arg2, $vivContext } = input;
return main.findThing(arg1, arg2, $vivContext);
}

The findThing function must be specified:

endpoints {
action-endpoints {
action-endpoint (FindThing) {
local-endpoint (FindThing.js::findThing)
accepted-inputs (arg1, arg2, $vivContext)
}
}
}

A single JavaScript file can export more than one named function, but can only export one default function.

Bixby API Library

Both runtime versions include a set of internal modules to provide new functions.

Versions 1 and 2 both include the following modules:

  • base64
  • config
  • console
  • fail
  • http
  • md5
  • secret
  • textLib
  • transaction
  • types

The only difference between runtime versions for these modules is the importing method.

// JS Runtime V1 import
var http = require('http');

// JS Runtime V2 import
import http from 'http';

V2 does not include the following modules:

  • dates
  • soap

Replacing the dates Module

Most of the functionality from V1's dates module is designed for formatting dates and times in the user's timezone. In V2, dates can be handled by JavaScript's Date object in combination with the built-in Intl.DateTimeFormat API.

// define options for DateTimeFormat
const options = {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
timeZone: $vivContext.timezone
};

// instantiate a new formatter
const formatter = new Intl.DateTimeFormat(
$vivContext.locale,
options
);

// format a date object
const date = new Date();
const formattedDate = formatter.format(date);

Replacing the soap Module

If your capsule needs to communicate with a web service via SOAP, you can use template literals to create the XML payload and send it to the remote server using http.postUrl(). By sending it with the xmljs format parameter, the returned XML will be translated into JSON.

import http from 'http';
import config from 'config';

export function handler (input) {
const options = {
format: 'xmljs',
returnHeaders: true
};

const soapMessage = `
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ord="http://...">
<soapenv:Header/>
<soapenv:Body>
<ord:order>
<ord:firstName>${recipient.firstName}</ord:firstName>
<ord:lastName>${recipient.lastName}</ord:lastName>
<ord:address>${recipient.address}</ord:address>
<ord:deliveryDate>${deliveryDateStr}</ord:deliveryDate>
<ord:product>
<ord:sku>${product.sku}</ord:sku>
<ord:quantity>1</ord:quantity>
</ord:product>
<ord:totalAmount>${charges}</ord:totalAmount>
</ord:orders>
</soapenv:Body>
</soapenv:Envelope>`;

const response = http.postUrl(
config.get('remote.url'),
soapMessage,
options
);

return response;
}

JavaScript Language Notes

  • JavaScript Runtime Version 1 supports all of JavaScript 5.1 and a limited number of features from JavaScript ES2015.
  • JavaScript Runtime Version 2 fully supports JavaScript ES2020, with the exception of promises and async/await, which are not supported.

Loop Construction

JavaScript 1.6's for each...in loop construction is not supported by V2; instead, use for...in:

const foo = { a: 10, b: 20 };

// DO NOT USE the unsupported for each...in style
for each (var x in foo) {
console.log(x);
}

// use the supported for...in style
for (let key in foo) {
let x = foo[key];
console.log(x);
}
Caution

Existing for each loops in your capsule will not be automatically migrated between V1 and V2.

ESLint Validation

Runtime Version 2 uses ESLint for detecting validation errors.

Bixby Developer Studio will not read .eslintignore or .eslintrc files. You can, however, use configuration comments for disabling rule warnings in a file. You can disable:

  • A specific line: // eslint-disable-next-line
  • Specific rules on a specific line: // eslint-disable-next-line quotes
  • A block or an entire file: /* eslint-disable quotes

This could be useful if your capsule includes an external library that will not otherwise pass validation.

Migrating to JavaScript Runtime Version 2

Bixby Developer Studio can assist you in migrating from JavaScript Runtime Version 1 to Version 2. You'll see a message in the Warnings & Errors pane of the editor in your capsule.bxb file indicating that the V2 runtime is available and suggesting an upgrade with the Quick Fix option.

Warnings panel showing JavaScript Runtime migration prompt

Click Migrate Capsule Code… to begin the migration process. Migrate to Latest JS Runtime… will also appear as an option in Bixby Studio's context menu when you right-click on the capsule in the file sidebar.

When the migration is complete, Bixby Studio will display a results page showing:

  • A list of successfully migrated files
  • A list of any files that could not be automatically migrated
  • The location of the backup file

If you have files that cannot be automatically migrated, such as ones using the V1 dates API, you can use the API Differences section of this document as a guide for making the necessary changes.