JavaScript Transform
Source: 05_javascript_transform
An HTTP server that applies a JavaScript function to every incoming JSON payload and returns the transformed result. The JS engine is declared as a model so the function is compiled once at startup and reused across all concurrent requests.
Running
melodium run 05_javascript_transform/Compo.toml$ curl -X POST http://127.0.0.1:8080/transform \
-H "Content-Type: application/json" \
-d '{"name":"Alice","score":82}'
{"name":"Alice","score":82,"grade":"B","note":"processed by Mélodium JS engine"}How it works
The Transformer model embeds the JavaScript function directly in the source:
model Transformer() : JavaScriptEngine {
code = "
function transform(input) {
var grade = 'F';
if (input.score >= 90) grade = 'A';
…
return { name: input.name, score: input.score, grade: grade, … };
}
"
}The JS engine compiles this function once when the model is instantiated. startup starts the HttpServer and registers the /transform connection:
treatment main(const port: u16 = 8080)
model server: HttpServer(host=|from_ipv4(|localhost_ipv4()), port=port)
{
startup()
start[http_server=server]()
logReady: logInfoMessage(label="server", message="JavaScript transform service ready")
startup.trigger -> start.trigger
startup.trigger -> logReady.trigger
connection[http_server=server](method=|post(), route="/transform")
status: emit<HttpStatus>(value=|ok())
headers: emit<StringMap>(value=|map([]))
bodyTrigger: trigger<byte>()
connection.data -> bodyTrigger.stream,start --> status.trigger,emit -> connection.status
bodyTrigger.start --------> headers.trigger,emit -> connection.headers
jsTransform()
connection.data -> jsTransform.data,data -> connection.data
}Every request then calls process[engine=engine](code="transform(value)") which invokes the compiled function with the parsed JSON value.
The jsTransform sub-treatment
treatment jsTransform()
model engine: Transformer()
input data: Stream<byte>
output data: Stream<byte>
{
decode()
toJson()
unwrap<Json>()
process[engine=engine](code="transform(value)")
unwrapOr<Json>(default=|null())
toString<Json>()
encode()
Self.data -> decode.data,text -> toJson.text,json -> unwrap.option,value -> process.value,result -> unwrapOr.option,value -> toString.value,into -> encode.text,data -> Self.data
}Two different unwrap strategies are used deliberately:
unwrap<Json>()beforeprocess: malformed JSON is a hard error; there is nothing useful to pass to the JS functionunwrapOr<Json>(default=|null())afterprocess: a JS runtime error produces a gracefulnullresponse rather than crashing the request
The jsTransform sub-treatment declares its own model engine: Transformer(), which means the model instance is shared with main’s engine through Mélodium’s model propagation. The function is not recompiled per request.
Dependencies
[dependencies]
std = "0.10.1" # core flows, logging, data structures
http = "0.10.1" # HTTP server and client
net = "0.10.1" # IP address helpers
json = "0.10.1" # JSON parsing and serialisation
encoding = "0.10.1" # UTF-8 encode / decode
javascript = "0.10.1" # embedded JavaScript engine