JavaScript didn't have a lot of great options for this kind of statically-declared metaprogramming, which is why the "use X"; syntax has become so popular in various spaces. It's definitely not my favourite approach, but I don't think there's any clear "best" solution here.
I suspect that's why they need to transform the function into individual step functions, and for that they need to do static analysis. And if they're doing static analysis, then all the concerns detailed here and elsewhere apply and you're basically just picking your favourite of a bunch of syntaxes with different tradeoffs. I don't like magic strings, but at least they clearly indicate that magic is happening, which is useful for the developer reading the code.
If I recall correctly, other solutions in this space work by persisting & memoizing the results of the steps as they succeed, so the whole thing can be rerun and anything already completed uses the memoized result.
We don't have workflow and steps tho, like async await, just functions all the way down.
Disclaimer: I'm the CEO of resonate
You can do that manually by defining a bunch of explicit steps, their outputs and inputs, and how to transform the data around. And you could use generators to create a little DSL around that. But I think the idea here is to transform arbitrary functions into durable pipelines.
Personally, I'd prefer the more explicit DSL-based approach, but I can see why alternatives like this would appeal to some people.
These alternatives just hide the implementation and make debugging and debugging expanding/configuring unavailable
Disclaimer: I'm the CEO of Resonate
[0] https://ts-ast-viewer.com/#code/GYVwdgxgLglg9mABMOcAUBKRBvAU...
It's similar to the problem that `require` had in browsers, and the reason that the `import` syntax was chosen instead. In NodeJS. `require` is just a normal function, so I can do anything with it that I could do with a normal function, like reassigning it or wrapping it or overwriting it or whatever. But in browsers (and in bundlers), all imports need to be statically known before executing the program, so we need to search for all calls to `require` statically. This doesn't work in JavaScript - there are just too many ways to dynamically call a function with arguments that are unknown at runtime or in such a way that the calling code is obscured.
That's why the `import` syntax was introduced as an alternative to `require` that had rules that made it statically analysable.
You'd end up with a similar situation here. You want to know statically which functions are being decorated, but JavaScript is a deeply dynamic language. So either you end up having a magic macro-like function that can only be called in certain places and gets transpiled out of the code so it doesn't appear at runtime, or you have a real function but you recognise that you won't be able to statically find all uses of that function and will need to set down certain rules about what uses can do with that function.
Either way, you're going to be doing some magic metaprogramming that the developer needs to be aware of. The benefit of the "use X" syntax is that it looks more magical, and therefore better indicates to the reader that magic is happening in this place. Although I agree that it's not my personal preference.
> I'm trying to find how they implemented the "use workflow" thing, and before I could find it I already found https://github.com/vercel/workflow/blob/main/packages/core/s...
> Telemetry is part of the core.
> Yuck.
At least understand what you're looking at before getting the ick.