Adding support for JavaScript expressions

The recent issues with JSONata have highlighted a long-standing mismatch between the projects. JSONata is focussed on the strict subset of what the JSON format supports. We were early adopters and straight-away took advantage of it for working with more general JavaScript Objects.

The recent fixes they have applied are very much skewed to their own focus (rightly so, no complaints here from that perspective), but have exposed the mismatch.

Thinking about the future, we want to avoid these mismatches impacting our end users who shouldn't need to know or care about the subtle distinctions between pure JSON compatible objects and everything else JavaScript can throw at you.

So I'm currently contemplating a future where we move on from JSONata.... :open_mouth:
That isn't an overnight change for sure, but I want to think about a path to get us there.

In the comments on the current JSONata issue, the idea of allowing general JavaScript expressions in the change node resurfaced (I'm not sure of their identify on the forum - no matching username I can find.. apologies uncle-fed for not tagging you)

First up - this is not a new idea. It has come up before from others and had amount of discussion, but not got further than that.

Some threads from the past:

What has further triggered me to write this post is some of the additional leg work uncle-fed did, in particular, linking to Bonsai — Safe Expressions for Rules, Filters, and Templates

At first glance, that looks very interesting for us. It's a currently maintained library for exactly the sort of purpose we are looking for. It is extensible, and, whilst not a complete sandbox, it does provide some protections against unsafe expressions.

So now I'm playing the 'what if' game.

Such as:

  • What if we can spot if an expression is jsonata or not - and pass it to the corresponding evaluation engine.
  • Then we wouldn't need to add a new expression type for end users to get confused by
  • We could deprecate JSONata usage and begin a migration to a new thing

Its late on a Friday afternoon. I don't know if this is sensible or not, but wanted to get it shared here for others to comment.

Several years ago I pointed to Jsonata support built in to eg a change node and asked if support for other [interpreted] languages could be added.

The suggestion was shot down on the basis that I was suggesting compiling a Bash interpreter into Node-red, which I was not, merely passing data and code identified as Bash/Python/jsonata/whatever syntax to the OS' Bash/Python/jsonata/whatever interpreter to run.

It seems to be similar to what you are proposing for Jsonata. Is there a command line jsonata interpreter?

I don't know how this would work on WIndows but on Linux it is comparable to the everyday behaviour of pipelines. A command | a different command | another command and has the advantage that it's down to the maintainer of the OS command line tool to resolve security problems.

That isn't what I'm proposing here; no piping to external commands or similar.

Simply that the expression would either be evaluated by jsonata or whatever alternative js library we pick (bonsai definitely looks fit for purpose). I like the idea of it being somewhat transparent to the user, but it all depends on whether we can identify the appropriate library to use given an expression. The fallback would be to add a new expression type the user had to explicitly pick. That would be easier to develop, but forces the user to make a choice.

Anyway, this is all a Friday afternoon thought experiment based on the reality of where we find ourselves with recent jsonata changes.

Honestly, that would be my design choice. Bonsai is effectively a new syntax being introduced and I would think it clearer to everyone if that is kept clear.

And, going back to the link you quoted that I started, I still think that introducing a simpler, shorter JavaScript method gets many of the advantages but doesn't introduce yet another "language". Instead, it may introduce more people to JavaScript code where they can go on to re-use that knowledge in Function nodes should they ever need to move into more complex processing.

Anyway, as you say - probably one to come back to next week.

I strongly support the idea of replacing JSONata with standard JS (with proper sandboxing).
I always found the syntax differences confusing, and doing all my logic in JS in function nodes rather than JSONata expressions.

For specialized JSON processing we can perhaps provide a dedicated JSONata node or extend the existing JSON node.

Personally I would not mind replacing it as long as exisiting implemenations & configurations can stay "as is". I have used JSONata in many flows where I had no performace requirements and lazy me, I would not love to go through them all and re-write (I understand from the above that support will stay)

I thought JSONata was (is) rather handy, straight forward and streamlined. But is javascript then the best possible choice as a replacement or successor?

Maybe I overdid it as in the example below, maybe all that could be replaced with a javascript one-liner but it gave me a good overview and it works as expected & good enough

Ideally via a plugin solution so that others could add their own mini-languages to the options of the change node.

Adding Bonasi to the core and deprecating JSONata will bloat-up the core while both are still supported so that things that actually caused this situation (an update of JSONata) could easily occur again but with Bonasi. But of course, that will never happen because Bonsai != JSONata.

Hence I would suggest extracting JSONata into a plugin, provide internal APIs for plugins to utilise to become new "scripting" languages inside of NR. The first plugin would be JSONata for those that have heavy JSONata flows. This would also ensure that everything is covered for JSONata to be a plugin so that there is a clear pathway for others to create other scripting language plugins (such as JS sanded-boxed engines).

The other advantage of this approach would be a slimming down of the NR core and thus reducing the surface area for other update worries. The less there is in the core of NR, the less it will break on an update.

This idea only makes sense if one considers NR to be a kind of Kernel (a'la Linux) instead of a visual Javascript execution engine, that currently is.

Already long time ago there was several Python nodes created and released. Was that in line somehow with your thinking?

No.

That's just a node, I mean:

Being able to add to dropdown list in the change (for example) node via a plugin installed via the palette manager.

Each of the option listed there could be installed via a plugin - as required. If I don't need a timestamp anywhere in my flows, I won't install the timestamp-dropdown-plugin (for example).

The point being that the NR core could be slimmed down, the responsibility for handling stuff expressions passed to a separate plugin that can be maintained outside of the core NR. And folks can choose what they want to use in their flows - many here ain't big fans for JSONata, so they can have their JSONata-free NR if they so choose.

I do rather like and approve of the idea of moving JSONata into a plugin and providing a generic "expression" entry which then gave the option to choose from any installed expression plugins. I don't think it would be a good idea to keep extending the dropdown list.

The expression option is already known and clear so, to me at least, it makes more sense to put the expression language selection probably as a separate drop-down on the right-hand-side of the expression input box (similar to flow/global selectors when you have multiple storage plugins configured).

The plugin concept is well understood, already used elsewhere in Node-RED and would indeed reduce fixed dependencies in the future. So helping to avoid similar future problems. At the same time opening up opportunities for further innovation without impacting the core.

Whether such a change would be feasible in a reasonable time frame I couldn't begin to guess.

If we're comparing "remove all JSONata from NR and replace it with Bonsai" versus "convert JSONata to a plugin and create internal APIs for other plugins" - I think it would be a head-to-head race in the amount of effort required! Especially if external effort of those needing to port their JSONata code in their flows is added.

The long term benefits of the approaches would then need to be thrown onto the scales to figure out which approach would actually be better. If that is metric that should be considered.

Either way, thanks for your support on this one!

I had exactly the same thought about stripping JSONata out and making it a plugin. In particular, I see a path forward to support JSONata legacy and JSONata current. Legacy would be backward compatible, but the user would accept the CVEs, whereas current would be the latest version available. I'm not saying we would go that way, but it would be a possibility by the nature of it being pluggable.

As for Bonsai, most definitely! a sensible evaluator is something I've wished for. I really dislike JSONata. I once wrote my own evaluator for simplifying dashboard payloads, but never finished it or posted it.

And as already mentioned, the benefit of being pluggable is that it opens the doorway for other evaluators.

Win-win.