Appearance
JavaScript script
Runs your own JavaScript (Google V8 engine) for every message that reaches the component. Use it for transformations and small integrations when you prefer JavaScript over C#; async/await, fetch, setTimeout / setInterval and console are available.
Pins
| Pin | Kind | Types | Description |
|---|---|---|---|
| Input | input | any | The data from the previous component. Its type is the type of the input connection. |
| Output | output | any | Whatever the script passes to services.Next. The template creates an object of the output connection's type. |
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
Script | code (JavaScript) | empty | The user script: an async function execute(metaData, inputData) plus any functions and classes you need. Empty means the generated template is used. |
Behaviour
Before your code the component generates a JavaScript class for every type of the flow (same class and property names as in the flow, e.g.
inputData.Name), the classesFlowMetaData,NoInputData,AnyInputDataandViaFileInfo, and the globalservices. This part is hidden in the editor; your code starts after the/*viaducta.javascript.code*/marker, and the line numbers of errors and of the debugger are the lines of your script.The script is loaded once when the flow starts (top-level code runs then); for every message the component calls
execute(metaData, inputData), whereinputDataalready is an instance of the input connection's class (NoInputDatawhen nothing is connected). Global variables keep their values between messages. Messages are processed one at a time per component.The generated template:
javascriptasync function execute(metaData, inputData) { const outputData = new CustomerDto(); services.Next(metaData, outputData); }CustomerDtois the output connection's type; without an output connection the template callsservices.Next(metaData, inputData).servicesis the component's flow service (IFlowService) with the C# member names, the same services a C# or Python script has:services.Log,services.Messaging,services.Settings,services.Component,services.Flow,services.Profiler,services.Batches,services.Storage,services.Repositoryand the rest. .NET methods that return aTaskreturn a promise (awaitit).services.Next(metaData, obj)sendsobjto the components connected toOutput;objmust be an instance of one of the generated flow classes (plain JavaScript objects can't be sent). It can be called zero, one or many times.services.Next(metaData, obj, connectionId)sends to one connection only;await services.NextAsync(metaData, obj)waits for the delivery.Frequently used services:
services.Log.Information("...")(alsoDebug,Warning,Error,Fatal) writes to the flow log.services.Messaging.Information("...")(alsoWarning,Error,Trace) writes to the designer console;UpdateStatusMessage("..."),UpdateProgress(0.5)(0 to 1),StopProgress()andClear()update the component.services.Settings.Get("Package", "Name")reads an expression setting.services.Component.Id/.Name;services.Profiler.UpdateRecordsProduced(services.Component.Id, 1).services.Batches.Begin(metaData, "label")opens a batch: send the records withbatch.MetaData, callbatch.Complete()andbatch.Dispose()in afinallyblock; disposing withoutComplete()fails the batch (see the example).await services.HandleExceptionAsync(metaData, inputData, error)hands a caught error to the flow's error handling and carries on.
List and array properties are JavaScript arrays: index,
length,for...of,map/filter,push,pop,splice,x[i] = v,length = 0and assigning a whole array (order.Lines = [...]) are written through to the .NET list. Items of flow types are instances of their generated class. Date properties are returned as JavaScriptDateobjects. Decimal properties are numbers (about 17 significant digits); assign a string (order.Amount = "12345678901234.123") to set an exact decimal.console.log/info/debug/tablewrite information messages,console.warnwarnings,console.errorerrors,console.tracetrace messages to the designer console;alert(...)is an information message.The component waits until
executeis done. For timer-based scripts callwait()to keep the message open andend()when you are done.fetch(url, { method, headers, body })works like in a browser: the response hasstatus,ok,statusText,url,redirected,headers.get(name),text(),json(),bytes()andarrayBuffer(). An HTTP error status is a response withok == false; only network errors reject. A string body is sent asapplication/jsonunless aContent-Typeheader says otherwise, an object body is sent as JSON, aUint8Array/ArrayBufferas bytes.Other helpers:
sleep(ms)(blocks the thread) and the host objectcrypto.An error that leaves
executegoes to the flow's error handling asJavaScriptScriptException: the message isTypeError: ..., the stack trace lists the functions of your script with their lines, and the error line is marked in the editor.In debug mode breakpoints and stepping work in the script editor: the debugger stops on the breakpoint lines and shows the local variables. Stepping into the generated classes or the runtime is skipped.
Notes
- There is no Node.js / browser runtime:
require,import, DOM,Blob,FormDataare not available. - The template is generated from the connections that exist when it is created; connect first, then open the editor (or clear the script).
- Flow objects the script creates are released when the JavaScript garbage collector collects them.
Example
Input connection type Customer, output connection type CustomerDto:
javascript
/*viaducta.javascript.code*/
let counter = 0;
async function execute(metaData, inputData) {
if (!inputData.Email) return; // filter: nothing is sent on
const outputData = new CustomerDto();
outputData.Name = inputData.Name.toUpperCase();
outputData.Email = inputData.Email.toLowerCase();
outputData.Tags = inputData.Tags.filter(tag => tag !== "");
counter++;
services.Messaging.UpdateStatusMessage(`Mapped customer ${counter}: ${outputData.Email}`);
services.Next(metaData, outputData);
}A batch per file:
javascript
async function execute(metaData, inputData) {
const batch = services.Batches.Begin(metaData, inputData.FileName);
try {
for (const line of inputData.Lines) {
const row = new Row();
row.Text = line;
services.Next(batch.MetaData, row);
}
batch.Complete();
} finally {
batch.Dispose();
}
}Http listener → Json deserializer → JavaScript script → Json serializer → ...