Closed guest271314 closed 7 months ago
Why is that something you need to know?
node
is not shipped with a built-in WebSocket server implementation. QuickJS and txiki.js are not shipped with a built-in HTTP(S) server. txiki.js does not implement TextEncoderStream()
or TextDecoderStream()
. Bun does not implement Node.js worker_threads
. Node.js implements CommonJS as the default module loader, Deno, QuickJS, txiki.js do not. Bun has a mechanism for using CommonJS and Ecmascript Modules in the same file. The last time I checked Node.js is still debating that implementation. And, of course, no two JavaScript runtimes implement STDIO the same, as STDIO is not defined in ECMA-262. And so forth.
So when we are testing the JavaScript runtimes and want to spawn a child process to use a different JavaScript runtime - using the same script - that information will be useful to know what features can and cannot be used in the given runtime agnostic script.
Ultimately I think we can use something like web-platform-tests dashboard]https://wpt.fyi/results/?label=experimental&label=master&aligned) have to programmatically list and visually display which JavaScript runtimes support which features
navigator.userAgent
is supported in all runtimes.
navigator.userAgent
is supported in all runtimes.
Didn't think of that. Not supported in QuickJS or txiki.js. Though I haven't gotten any confirmation those JavaScript runtimes are excluded from consideration here. Have not testes Hermes, Graal, et al.
Seems like, just like on the browser for decades, it’d be an antipattern to use the user agent for that, and you should feature test instead.
Seems like, just like on the browser for decades, it’d be an antipattern to use the user agent for that, and you should feature test instead.
I did think about spoofing the browser user agent.
navigator.userAgent
is indeed listed over here https://common-min-api.proposal.wintercg.org/.
The idea is to not have to feature test. Perhaps an immutable, readonly string or object.
Feature tests are the best practice, for a ton of reasons, and i don’t see that changing.
When you feature test what is returned from the function, from where?
That depends on the feature - but generally you test whether a feature is available before you use it, and that way, you can handle any platform that supports the features you need.
That depends on the feature - but generally you test whether a feature is available before you use it, and that way, you can handle any platform that supports the features you need.
Yes, but what features to test? And once you get results, ostensibly given a feature test we use try..catch..finally
, what are you returning from the function from where (as in from where in the runtime are we getting the runtime identifier) in the given JavaScript runtime?
You can see where this is going... If everybody is running their own feature test then there's no common way to do that right now.
There doesn't exist a generic, universal answer, nor can there ever. You have to name a specific feature, and then I could suggest how to feature-test that specific thing, and it will be unique and different for each thing. That's how the entire JS ecosystem does things (now, and for the past decade or two).
Let's say we want to use qjs
to read STDIN, node
for Ed25519 support, and deno
for a WebSocket server - using the same script.
We feature test qjs
for something defined on globalThis
, yet qjs
lacks navigator.userAgent
, which we probably shouldn't be using anyway for this case.
There doesn't exist a generic, universal answer, nor can there ever. You have to name a specific feature, and then I could suggest how to feature-test that specific thing, and it will be unique and different for each thing. That's how the entire JS ecosystem does things (now, and for the past decade or two).
I know nothing like a common JavaScript runtime identifier exists now, besides navigator.userAgent
, inherited from the browser. Thus this issue.
The
nor can there ever.
is not how I think.
Somebody in the wild once claimed something like npm
can't be eliminated from JavaScript. bun install
provides a means to install Node.js-specific packages without npm
on the machine. I have implemented multiple workarounds for what specification authors and implementers have decided to not support. So there is no "ever" in programming that I can point to.
That's how the entire JS ecosystem does things (now, and for the past decade or two).
Right. Isn't the point of this effort to cease that JavaScript runtime fiefdom-like behaviour?
No, wintercg has nothing to do with feature testing, and even among browsers (which wintercg is emulating) feature testing remains eternally required and recommended.
I know Workerd maintainers refuse to support serving an uploaded ReadableStream
to a subsequent request in the server. In Deno we can serve an uploaded ReadableStream
to a subsequent request, using Response
. I don't think Node.js has any built-in server implementation that supports sending a WHATWG Fetch Standard Response()
from the server. That would be a rather elaborate feature test, though possible.
I think it is reasonable for a common JavaScript runtime identifier to be written out. You don't think navigator.userAgent
is appropriate, recommend feature testing, yet proffer no common API or means to do so yourself, or on behalf of the stakholders you represent. So we are back to square one.
Such a runtime identifier wouldn’t reliably tell you what features are there, because bugs can happen, which is why it hasn’t been reliable in browsers to do so since forever. Feature testing remains the only reliable approach., no matter how complex the test is.
I think you are missing the part about after feature testing what you return from the function, from where in the global runtime.
The common runtime identifier alone will tell you what runtime your code is running in at that given moment.
So we feature test, then return exactly what from the function? A user-defined string or object, that is unique to that user who decided what to feature test for?
AFAIK there is nothing like https://wpt.fyi/ for the stakeholders in this effort and repository, which there can be, so users' in the field will have at least a canonical source to read and derive feature tests based on the data therein.
AFAIK there is nothing like https://wpt.fyi/ for the stakeholders in this effort and repository
WPT Report uploads are not limited to just browser vendors.
https://wpt.fyi/results/?label=master&label=stable&product=node.js&product=deno
@panva That is is relevant to client usage. Node.js doesn't have a built-in WebSocket server. (Inspector is not really intended to be used as a server).
@panva an There needs to be something like that for this repository. See https://github.com/wintercg/proposal-common-minimum-api/issues/62#issuecomment-1849040092.
I'm pretty sure WHATWG Fetch Standard and W3C ServiceWorker standard don't prohibit serving data from one POST
or QUERY
request to a subsequent request, see https://github.com/cloudflare/workerd/issues/423, https://gist.github.com/guest271314/4847c70be203ee7cd4c8a6d6a9bca1d3.
I'm not following neither your line of argumentation not actually understand how it relates to the original post and title.
Very simple: Specify a common JavaScript runtime identifier. Read-only. We simply can't run the same code written out in https://common-min-api.proposal.wintercg.org/ in the JavaScript runtimes the stakeholders who created this repository maintain.
When we are reliably able to get an immutable, read-only string from the given runtime, we can write source code include those checks in the source code to do take a certain course in the source code.
E.g.,
if (globalThis.runtime === "workerd") {
// We cannot serve an uploaded ReadableStream to a subsequent request
// without having to try to make that request and handle errors
}
I'm not following neither your line of argumentation not actually understand how it relates to the original post and title.
Another example. This single JavaScript file https://github.com/guest271314/offscreen-webrtc/blob/main/background.js is the source for a ServiceWorker
, an offscreen document
, and an arbitrary Web page the script is injected into from the browser extension.
The idea is to be able to use the same JavaScript source file across multiple JavaScript runtimes, with the ability to just retrieve a string from the global object to switch, branch code in the same script - without real-time feature testing, just using the runtime identifier string.
You just keep on describing navigator.userAgent
from the common minimal API.
https://common-min-api.proposal.wintercg.org/#requirements-for-navigatoruseragent
The globalThis.navigator.userAgent property is provided such that application code can reliably identify the runtime within which it is running.
Alright. I originally didn't think of that. Your colleague suggested feature testing which lead me to re-open the issue, because then the question arises what we are testing for, and what we return from the function or set as the value of a property, because although WPT might display results of Node.js or Deno, clearly that is only for client usage, not all usage, because Node.js doen't have a built-in WebSocket server. Therefore I think, either way, WinterCG should either include an asterisk next to WebSocket for Node.js or include details about what is being tested, because those results are not reflecting Node.js WebSocket server implementaton that does not exist.
The point about feature testing is that the entire original question indicates a probably-wrong approach. You (almost) never need to know which runtime you're in. You only need to know the capabilities of the current runtime. Which can change in the next version. Some of the capabilities may even be dependent on available hardware. So don't engage in a race to update your code to predict the capabilities based on auxiliary information (runtime name/ hardware), but instead check at runtime whether a feature works, and if it doesn't, use another feature.
Like when some of the runtimes provide a websocket server library under different names, just try and load one opportunistically. If it fails, try the next one. If all of them fail, load a TCP server library and your own implementation of the websocket protocol.
One of the very few cases where a runtime identifier could be useful is for generating error reports. And for runtimes that have some way of identifying themselves (e.g. process.versions
in node) you can even feature-test for exactly that.
A runtime identifier will be useful for me for the reasons I described above.
If you are going to suggest feature testing then you had better be actually constantly testing multiple JavaScript runtimes so you can recommend what to test and from where to return what.
So far the only features that have been specifically mentioned to test are the one I pointed out that are different between the JavaScript runtimes I continuously test.
We are not talking about libraries, just the JavaScript runtimes, which I fetch the latest version of every day or so. Libraries are a totally different issue. That brings CommonJS into the discussion, which is a Node.js-specific implementation that Bun somehow figured out a way to support in the same script as Ecmascript Modules are used - before Node.js has.
We are not just talking about the node
executable itself. That's all I write to my machine. The same with deno
, bun
, qjs
, tjs
, etc.
There are over 20 JavaScript runtimes or interpreters other than node
on this list alone https://gist.github.com/guest271314/bd292fc33e1b30dede0643a283fadc6a.
node
does not have a monopoly on JavaScript runtimes circa 2023.
Since I read and dig through commits, blame, issues, PR, release notes, and test the nightly releases of JavaScript runtimes and browsers constantly I kind of know what runtimes support what. It would be simple to just get the value of globalThis.runtime
, which navigator.userAgent
returns in some cases.
txiki.js does not have a navigator.userAgent
from the last time I built, sometime within the past couple weeks or so, has an open issue to support Common Minimum API.
QuickJS doesn't have navigator.userAgent
either. Granted there's no signal from Bellard to support this proposal, QuickJS is found in WebAssembly/WASI environments far more than any other JavaScript runtime, at 915 KB after strip
. node
v22.0.0-nightly202312091ba508d51b is still 83.3 MB after strip
.
Just for completeness here the txiki.js issue to support Minimum Common Web Platform API https://github.com/saghul/txiki.js/issues/418.
There are over 20 JavaScript runtimes or interpreters other than node on this list alone
Do we already have tools to make it easy to run a piece of code in all of them and compare (error) output? That way I could try to build a demo for how to get a runtume identifier based on feature testing.
For reference, unjs/std-env has a standard simple runtime
export that follows wintercg runtime-key convention.
The rudiments of a synchronous function to return the name of the runtime the script is running in https://gist.github.com/guest271314/13ae833d27395f3db7d3b683b02cc611