Bixby provides two JavaScript runtime systems your capsule's action implementations can execute on:
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.
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.)
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.
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!
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;
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;
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);
}
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.
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);
}
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.
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
dates
ModuleMost 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);
soap
ModuleIf 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;
}
async
/await
, which are not supported.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);
}
Existing for each
loops in your capsule will not be automatically migrated between V1 and V2.
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:
// eslint-disable-next-line
// eslint-disable-next-line quotes
/* eslint-disable quotes
This could be useful if your capsule includes an external library that will not otherwise pass validation.
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.
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:
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.