182 comments

[ 3.6 ms ] story [ 127 ms ] thread
Kevin from the Deno team here - happy to answer any questions you have today!
I’m curious what this means in regards to this existing “use Deno to also publish to npm” workflow (https://deno.com/blog/dnt-oak) which I was considering using, and then the JSR announcement happened.

The thing I like about the blog post and the mentioned dnt tool is it takes care of lots of bullshit you otherwise need to figure out when publishing to npm.

What is the relationship between JSR and DNT in this case? Should one be used over the other? Together?

If I want to publish a module so it’s available for Deno and NPM, what is the recommended approach now?

As JSR develops, you can expect to see more features like those of dnt start to show up in the npm compatibility layer. We've also been exploring how to create a good DX around simultaneously publishing JSR modules to npm, so publishers can control their namespace there as well. We definitely know it's a usage pattern folks are interested in.

In the immediate term, dnt is still a very strong choice for people that want to develop modules in TypeScript using Deno, and then publish them to npm. In the fullness of time, I expect that JSR will provide a pretty complete solution to this problem as well.

I am not a user of Deno, but I acknowledged that HTTPS module imports were one of the original selling points of Deno compared to Node/NPM. Was it revised at some point that a package repository is needed? What's the back story?
HTTPS imports will continue to work and be supported in Deno. However, as we observed their usage in the wild, a couple problems became clear:

1.) Duplicated dependencies - projects would often download multiple versions of the same dependency, because there was no deduplication happening based on semantic versions.

2.) Disappearing dependencies - under some circumstances, an HTTPS import URL would be unavailable, causing code dependent on these modules to break.

A central package repository could solve for both of these problems. We considered (and very nearly chose) to just use npm, but it introduced functional and UX problems we wanted to solve for users. So we set about building JSR, and did so in a way that wasn't tightly coupled to Deno (JSR didn't need to be - it is useful in the context of any JavaScript runtime).

This is a solid reason to build it. Makes sense to me.
> a couple problems became clear: 1.) Duplicated dependencies

I’m sorry but that did not come up when designing the system? It’s the whole principle of semver ranges that deno decided to do away with. I cannot believe nobody saw this as a drawback. It’s the first thing I thought when I saw hardcoded versions.

In case you are not aware (maybe you are) node has experimental support for http imports. I personally think this feature is a disaster for many reasons, but if you want to use it in your toy apps it is there in node.
> I acknowledged that HTTPS module imports were one of the original selling points of Deno compared to Node/NPM

I am imagine most Deno users considered this a weird wart or foot gun, the rest of deno looks interesting.

Man, if only they'd debuted with this, but, hindsight and all. My only thing is that TypeScript has no spec, standard, or competing implementations so it seems a bit risky to treat it as a first-class citizen as far as publishing packages goes.
The TypeScript syntax is very stable - there is no spec, but it's very stable. The type system on the other hand: not very stable, and yeah I agree - that would be risky to rely on.

However as JSR only relies on the syntax, not on the type system, it's no problem :)

Is not the 'slow types' problem[0] a type system dependency, or am I missing something?

[0]https://jsr.io/docs/about-slow-types

It does not use TSC / do type analysis. This is purely syntax analysis.

The "slow types" restriction specifically exists because JSR does not use TSC or do type analysis :)

Consumers don't touch any ts, outside the (technically optional) .d.ts

Thecwayvi understand it in theory, the typescript version a library uses could disappear forever the day after a library had been published to JSR and all would be fine.

Until someone wants to maintain the code of course, but then you'd have the exact same problem with the elusive typescript version if the repository only took compile output.

What JSR would break (or make a deliberately difficult path?) is using some private fork of typescript that isn't even available on the day of publication.

The benefit of JSR, as far as I understand it, is offering an easy path to publication that makes type parts not an extra that some might skip but a reliable default. It does make me wonder however, how long-term reliable the finding will be: JSR promises more service than registries before, while also being more tool agnostic. I'm not sure if it's true, but my impression was that in many cases, the registry was primarily bankrolled by however the tooling was funded, e.g. effectively becoming a marketing expense to selling tooling expertise. JSR appears particularly removed from any of that and it's bad enough when the repository fades away in terms of static hosting, it's worse if it also leaves your library without a build process.

I'm still surprised by so many projects betting on TS.

ES will introduce types at some point and all this effort around TS will again divide the JS community.

Is that a good reason to write in raw JS today?

I'm not betting anything on TypeScript. It was released 11 years ago, and it's incredibly valuable to me. If it takes another 5 years for ES to implement first-class types, and it's a good implementation, I'll start using ES for new work at that time. What's lost here? TS won't just stop compiling.

I'm not referring to end users like yourself but projects like Deno who have invested a fair amount of resources into supporting TS.
Oh, yes, that makes more sense.

Still, native typing is not even on the horizon for ES, so I think my point stands for businesses that are heavily invested in supporting TS. TS is what we need today, it doesn't make sense to ignore it because theoretically it could be obsolete in 5-10 years.

Technically ES did introduce types already: in the "lost version" ES4. Typescript's syntax was partially inspired from ES4 and while not "compatible" with the previous type system there is more compatibility in the syntax than you might expect.

The Type Annotations [0] proposal that TC-39 (in charge of ES standardization) has kept in front of them (at Stage 1 so far) takes the "Python approach" of standardizing the syntax but not the type system still leaving that to external tools (in part because of the lessons from the mistakes of ES4 and the type system that was standardized but never really adopted). The proposal basically assumes that most types will (still) be in the Typescript type system (and would still need Typescript or compatible tool to type check) and maximal (but not complete) compatibility with Typescript syntax is a goal of the proposal.

But you don't don't have to take my word on that, the FAQ at the bottom of that Type Annotations proposal is well written and quite detailed.

[0] https://github.com/tc39/proposal-type-annotations

I've been complaining about the death of ES4 since it was killed back in 07 or 08.

The proposal might have compat types with TS but what about all the other TS features like generics. Now more than ever, it seems risky to bet the house on TS.

TC-39 isn't "betting the house on TS": the proposal is generic and intentionally doesn't define types it defines syntax for annotations of types. It's basically "type information may appear between a colon after an identifier and an equals sign or other terminator" and "type is a statement keyword with type information following it' and "interface is a block keyword with type information inside that block". That's pretty much the entirety of the proposal: it doesn't state what that type information is (just parsing limits on what it could not be so that it doesn't blow up language parsing), what it is used for (other than that the general idea at runtime is that type annotations are comments just like existing jsdoc comments), or who uses it. While the proposal is trying to stay syntax compatible (again, not types compatible because the proposal doesn't include types) with Typescript it also points out existing overlaps with Closure and Flow type syntaxes and is built so that both of those can use the new syntax for their own, different type systems.

This is why it is a Python-inspired proposal: Python didn't define types, Python defined "here's some places in the language that are effectively ignored like comments that type systems are expected to use". Python has made them more than "just" comments in later proposals and TC-39 holds the right to do that for ES/JS in the future but the current proposal stops about where Python's first proposal did at "types are just fancy inline comments for compilers like (but not limited to) Typescript/Closure/Flow".

Again the proposal itself is a really good read for specifics of where it looks like Typescript and what parts of Typescript are intentionally not supported. For instance as a good reminder there are only three (!) features of Typescript left that aren't ES-standardized and generate code: enums, namespaces, and class parameter properties. Of those, both enums and namespaces have been marked deprecated for a long time and many lint rules exist to help eliminate them from your code if you are still using them.

I used ActionScript 3 back in the noughties and was so excited for ES4 in the browser. Adobe open-sourcing Tamarin made it seem it was a done deal (I didn't follow the politics). I was so disappointed when it was killed. It did mean that when I tried Angular 2 the best part of a decade later I was delighted with how familiar TypeScript felt.
If and when it does, why do you think it wouldn't just be a subset of TS? Why reinvent the wheel when you have a perfectly good one that's beed around for 12 years and is proven in production?
I get their stance on TypeScript "exported types must be explicit and not use inference", but I feel like this is going to cause a lot of friction in adoption.
1. You can bypass slow types checks during publishing. You will just get a lower JSR score.

2. TypeScript is shipping features in the next release (TS 5.5) has quick-fixes in the editor to make it super easy to add explicit types :) The TS feature is called `isolatedDeclarations`

I would just like to add some of my findings around the developer friction with isolated declarations.

The whole reason isolated declarations is taking so long is because we also worry about making the requirements of this too onerous. To this end with isolated declarations, you do have to specify most types, but not all. For expression where we can reasonably infer the type from local context, we do so.

This should ease some of the developer friction. Not all of it. Some things will not be inferrable, but things like primitive types, object literals, tuples will be so it should make it easier.

We also worked on a code fixer that adds explicit type annotations where this new flag would raise an error, again to help people migrate if they choose to do so.

To be clear for people reading wondering what this is about, this is only a hard recommendation for the public API types. The reason for it, is by adding explicit types to the boundary of your package, the package becomes way faster for users to type check because every user's machine doesn't need to do all the inference work and type check internal types or packages not related to the types. Additionally, it makes the published code more resilient to changes to TypeScript's inference in the future because it's not doing inference. It also becomes way easier to generate documentation for the package (also, the ability to generate .d.ts or bundled .d.ts files without a type checker becomes easier).

Right now, the publish command errors and asks you to fix the issues or bypass it entirely via `--allow-slow-types`. In the future there will be a `--fix` flag to write the explicit types for you.

Glad the pinnacle of programming is still there: left-pad
The entire source code:

    export function leftPad(str, len, ch) {
      return new Array(len - str.length).fill(ch || ch === 0 ? ch : ' ').join('') + str
    }
that actually seems really inefficient, it could avoid the array allocation and fill call by just moving the length check...
You could also just use `String.prototype.padStart()` [1], and you get a nice optimized C++ implementation :D

[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Pass, not a Rust implementation /s
Is it even AI scale if not performed on the GPU?
No, you're right. The White House says no more C/C++ it's your civic duty.
While funny, worth remembering that .padStart didn't exist when left-pad was first published :)
It's not even needed anymore: "HN".padStart(10); That's it.
Seems like the logical way to do it. Though, honestly, unless you specifically need to do lots of different kinds of padding, something like this makes way more sense:

const bespokePad = (x) => ('000000' + x).slice(-6)

I wanna see you write it from memory. Now do it without bugs fixed in 2013 in that package.
I think this time it won't spread as much because most people just ask their LLM for a leftpad function.
What are the chances that the LLM suggests the NPM package?
What's the best way to include a binary blob (wasm binary) in your package? For NPM I've been using a bundler (esbuild's `binary` loader) but I'm not sure of the best way to do that in a modern, jsr-friendly way.
If you‘re using emscripten, check out the SINGLE_FILE option, that embeds the WASM as a base64-encoded string into the JS.
Everything just has to sound cool these days because yolo

    yarn dlx jsr add @oak/oak
That does get to be a mouthful! But if you wanted, you can of course do:

yarn global add jsr

so that subsequent installs could just be:

jsr add @oak/oak

I still find `npm install @oak/oak` better.
> JSR isn't a replacement for the npm registry; it's a superset of npm.

I first read this line to mean that JSR contains every package on NPM, and was just doing some post-processing on them. But it does seem to be its own registry. Maybe the intent of the line was to communicate that you can install packages from both?

I think this is mostly right - "superset" in that JSR modules can depend on npm modules, and projects using npm can use the npm registry and JSR together. The bottom line we'd want to communicate is that JSR is additive to npm, and the two can be used at the same time.
The usage of the "superset" expression is very confusing to me and I bet to many others.

I would recommend to use other terms such as "Additive" or "Complementary" to describe JSR.

That's not really what "superset" means, so I think you might want to change the wording.
> I think this is mostly right - "superset" in that JSR modules can depend on npm modules

This is not what most people would think when a registry is said to be a “superset” of another.

JSR is almost literally a subset of npm in features. Npm allows publishing of anything, JSR only actual TS/ESM. Whether those modules have dependencies doesn’t expand the set IMHO
One thing I'm not sure about when reading their policy is what'll happen in another kik situation. If someone were to claim the scope Microsoft (for example) that had no relation to Microsoft and then Microsoft came along and wanted the scope what would happen?

If MS got the scope what would happen to the packages in that scope, or if MS didn't get it what is done to clearly communicate that this is an unofficial account (presumably) asking on MS' behalf? In this early on period I'd expect a lot of people to claim the scopes of notable companies and these companies might take issue with that if they choose to use jsr down the line.

We do intend to take a more editorial approach to scopes, and assign scopes to users in a way we think is more intuitive for end users of JSR. We have reserved some obvious scope names already, but in the future, we'd likely entertain requests to reassign ownership of scopes for the benefit of the broader user community (as in the case of a brand owner requesting ownership of their brand name).

So in the case that a user published "@cocacola/foo", previously published versions of "@cocacola/foo" would remain available indefinitely (unless they were found to be malicious), but we would likely be willing to assign ownership of the "@cocacola" scope to a representative from that brand/company if they asked for it and we could verify their identity. The original author of "@cocacola/foo" would need to publish the module going forward under a different scope.

In the example of "@cocacola/foo" would it allow for the "@cocacola/foo" package to be updated with new versions by the new owners? Or would the foo package essentially be archived and read-only from this point on?
The new scope owner would be able to update "@cocacola/foo" and publish new versions (previous versions would be unaffected).
> but in the future, we'd likely entertain requests to reassign ownership of scopes for the benefit of the broader user community (as in the case of a brand owner requesting ownership of their brand name).

So you're basically committing to repeating the Kik drama? For reference: https://en.wikipedia.org/wiki/Kik_Messenger#Open-source_modu...

It would be great to find a way of structuring these registries/repositories in a way so there wouldn't be any name collisions, and also avoid the built-in support for companies to take names away from individuals.

Thankfully JSR won't be capable of a left-pad situation where packages can be unpublished - published packages are immutable[1].

As for the potential for disagreements over whether or not a scope should be transferred, that is a big reason why we want to figure out community involvement in governance sooner rather than later. We are gathering potential volunteers who want to discuss becoming a community moderator - if anyone would be potentially interested, they can sign up to join that conversation[2].

[1] https://jsr.io/docs/immutability [2] https://jsr.io/go/moderator

There are only two ways to do it:

1. Use UUIDs.

2. Use some pre-existing centralized name registry (e.g. domain names).

I don't know why the JS ecosystem is so resistant to either solution. Both are proven options (#1 used by COM, #2 used by Java). They do mean longer package names, but surely that's a small price to pay for a resilient future-proof solution? And besides, who really cares about long dependency names and why?

(comment deleted)
I'd suggest they require a DNS entry to use a scope and all scopes have a one to one relationship with a domain.
What happens if the domain registration expires? Scope takeover?
Domain expiration is rare, and virtually only happens when the related projects are dead anyways; freeze existing packages (no more updates until the original key for the domain comes back online, with manual override by registry administrators for edge cases) and have a reasonable waiting period (a couple of months to a year) before allowing the new owner to use the namespace (with different project names within).
How do you deal with individuals that may not have a domain? What do you tie their scope to?
This is a joke: GitHub account name. If it is good enough for Golang, …
Doesn't this get us right back to where we started from? Where you don't want users to be able to register arbitrary names :)
Correct. That's a big part of why it was a joke. (It's also a contributing factor in why I wonder if golang itself is in fact a prank.)
Not if the system is built for DNS first. If a company/individual/organisation has a domain name, they are expected to use the domain. It is much less of an issue if someone has kik.users.registry.com when the company publishes packages under kik.com.
Subdomains on a service that will do authentication for cheap or free?

Your packages could be published for lucacasonato.imbroke.com

Use URN instead of just a domain name. A domain name (with some schema) is obviously a subset, but anyone who doesn't want or need one can then use uuid: URNs.

The XML ecosystem did it that way for namespaces, and I think that it is still the most flexible and the most future-proof approach, since you can always add new schemas as needed.

Love this idea, defer the IP problems to DNS where everyone already has expectations set.
This is great!

I very much like the added TypeScript support (which the standard NPM registry does not have.) and that it's open-source.

I'll see about getting my packages uploaded onto here, too -- just having built-in support for TypeScript makes packaging x100 easier.

Regarding NPM, it's absolutely insane that we've been depending upon a closed-source, monolithic nightmare with poor UX for so long. You can't even use comments in package.json -- overall, it feels like a holdover we haven't figured out how to replace.

Furthermore, it's scary that Microsoft, through GitHub, now hold so much power over the JavaScript industry -- enshittification will most likely ensue.

I'm not sure jsr fixes comments in package.json, and I wouldn't blame npm for the miss feature.

package.json is used at runtime by node.js and strictly parsed as json. Npm or just could strip comments on publish, but local development of the package would break.

Not sure if bun or demo use pjson at runtime or if they allow comments.

I think it's interesting that JavaScript projects at the package root despite most projects compiling to dist nowadays. I'd like tool to generate the whole package root, including a transpiled or generated package.json

> I'd like tool to generate the whole package root, including a transpiled or generated package.json

That's exactly what JSR does. We generate the package.json ourselves.

> package.json is used at runtime by node.js and strictly parsed as json

It still constitutes an unfriendly developer experience -- people have had to make workarounds to document what complicated scripts do, and why they use certain dependencies, etc. I understand how JSON.parse works, but this still isn't the best behaviour.

Note that this is just one example of unfriendly UX in NPM as a whole -- this isn't the hill I'm dying on, so to speak, I'm just giving an example of how I believe the industry is being held back by tools which just haven't kept up with demand.

This is a terrible name. JSR already stands for "Java Specification Request" which is basically an RFC or standard for Java (not JavaScript).

This is going to make Google searches even more difficult. Searching for Java jobs and getting JavaScript jobs is already bad enough. This name is just making a big mess with semantic name collision in our mental namespaces.

Just make it "RfJS" maybe for "Registry for JavaScript" (if that doesn't class). I know Firefox had a few naming rounds going through Phoenix and Firebird IIRC first.

No opinion on the actual project itself.

JSR is also a company that produces synthetic rubber, a semiconductors company, a pharma company, a journal, and a company that produces merch for rock musicians. All of these rank higher on Google for JSR than Java Specification Requests.

Given that you're going to have a hard time pulling up the JSR you're looking for without adding qualifiers anyway, I don't think this name choice is going to substantially change anything.

> JSR isn't a replacement for the npm registry

Shame.

> JSR: The JavaScript Registry

Then on the website:

> Made for TypeScript & ESM

> JSR is designed for TypeScript

> You publish TypeScript source

Seems the <title> tag needs an update to reflect what this really is :)

and the name... I suggested TSR (the TypeScript Registry)
The project is called "The JavaScript Registry"

> The JavaScript Registry (JSR) is a modern package registry for JavaScript and TypeScript.

https://jsr.io/docs/introduction

Yeah, this is what I'm complaining about.

If you're building something that is designed for TypeScript, made for TypeScript and publishes TypeScript sources, then I find it really hard to understand why you'd name it "The JavaScript Registry".

Presumably because they don't want to imply that it won't work if you don't use TypeScript.
I don't think it will, unless you use the npm compatibility layer.
An npm compatibility layer doesn't sound like it has anything to do with TypeScript? I'm assuming that you can use JavaScript anywhere you can also use TypeScript.
The jsr registry serves typescript, so you have to use typescript tooling, maybe tsc buxy could be esbuild, demo, w/e.

The npm compatibility layer compiles typescript to JavaScript on the registry so you can use it without any typescript tooling.

Ah! Looks like it does indeed:

> Unlike with native JSR imports, you are not directly importing TypeScript code. Instead JSR transpiles the TypeScript code to JavaScript before it is installed into your node_modules directory. This generally means that your editor experience will suffer, because “Go to definition” and other features will link to transpiled JavaScript code, or to generated .d.ts files.

https://jsr.io/docs/npm-compatibility#limitations

That said, I'm assuming that if you use Deno, you can write plain JS as well (i.e. the TS library will still work with JS). So yes, you'll need to use the npm compatibility layer if you don't use Deno, but those are the only supported use case.

So I think they still don't want to imply that you have to use TypeScript if you use JSR in the runtimes it's targeting.

Any valid JavaScript is automatically also valid TypeScript, though.
because TS doesn't exist without JS?
(comment deleted)
This is probably a marketing bug on our part. While we did want to design for TypeScript from the outset, you can definitely happily write and publish plain JavaScript code on JSR. We probably need to do a better job explaining and featuring this.
(comment deleted)
Seeing Bun/Node/Deno support reminds me of a question: are there any good resources on writing JS that works in all 3 and the browser? For example if I wanted to write a simple filesystem API that would work the same across bunodeno?
You can't, and for good reason. Deno has slightly different API's than Node. Bun also has subtle differences between their STL implementation and Node's, but afaik, they're getting there.
I think maybe I was unclear. I'm talking about writing libraries that abstract across these differences and provide a single API, as sibling describes. I already know it's possible. I made a simple filesystem abstraction here[0] and a very simple HTTP library that uses it here[1]. They both work in Node/Deno and the browser. Unfortunately I ran into issues with Bun's slice implementation[2], but that should be fixed eventually.

My overall question is that I suspect there's a much better way of detecting and using the different backends, and I'm wondering what techniques are out there.

[0]: https://github.com/waygate-io/fs-js

[1]: https://github.com/waygate-io/http-js

[2]: https://github.com/oven-sh/bun/issues/7057

I think you'd have to write a facade package with a consistent API that mapped to the node/deno/bun equivalents, since they're each quite different.

Best bet is to use the Node fs package and rely on the deno/bun compatibility layer.

More generally, Deno pushes for Web Platform APIs, and as more are proposed and implemented the runtime-specific APIs will become fewer and fewer.

Yeah this is basically what I'm trying to do (see response to sibling). Unfortunately I consider Node's APIs the worst of the 3. No shade on them it's just an older design. I really like Deno's approach since my code also needs to work in the browser. Writing HTTP handlers that take Request[0] and return Response[1] is a beautiful bit of symmetry with frontend code and feels like cheating.

[0]: https://developer.mozilla.org/en-US/docs/Web/API/Request

[1]: https://developer.mozilla.org/en-US/docs/Web/API/Response

If you use Node 20+ then there's a lot more compatibility with web platform APIs, so it is possible to write code that works on Node, browser and WinterCG-compatible runtimes like Deno. Obviously you'll need to avoid using Node builtins.
Bun’s goal is to be an actual superset of node, so you can write just for node and expect it to work in bun.

As for deno, I think if you publish ESM you’re mostly covered since deno can install from the npm registry.

if it is made for typescript, shouldn't it be tsr?
As a long-time front-end developer, I'm not seeing a strong value proposition here to justify the further fragmentation another package registry is going to cause.

> You publish TypeScript source, and JSR handles generating API docs, .d.ts files, and transpiling your code for cross-runtime compatibility.

This sounds like another building service I don't control. There's already too much magic in publishing transpiled TypeScript packages but at least the current tools let me control exactly what artifacts get pushed out.

> web-standard ECMAScript modules

The "web standard" part is meaningless considering that most production websites will bundle the files together as part of their build/optimization process for size and loading speed, leaving only a giant chunk(s) that resembles nothing like the original ES modules.

> JSR isn't a replacement for the npm registry; it's a superset of npm.

A super of npm means that the packages created for this repository will not work in the npm ecosystem. What is this if not a "replacement" of the npm registry and further fragmentation of the JS ecosystem?

>JSR modules can be used with any JavaScript package manager, and in any project with a node_modules folder.

This makes the already complicated module resolution logic in most bundler/packaging tools even more complicated as they must account for the intricacies of another package manager. A house of cards being stacked on another house of cards...

> Module authors can count on great editor support from strongly typed modules, without the need to transpile and distribute typings manually.

The website seems to insist that distributing typings is a complicated process when it is just the .d.ts files bundled with the published package and an additional entry in the package.json file.

> Easy publishing with a single command - the CLI will walk you through the rest

It seems like every benefit that this project offers can be fixed in the current ecosystem by better client-side tooling that enforces standards during the publishing process while keeping full backward compatibility with over a decade of packages published and without fragmenting the ecosystem.

Another comment in the thread makes a very good point:

> Man, if only they'd debuted with this, but, hindsight and all. My only thing is that TypeScript has no spec, standard, or competing implementations so it seems a bit risky to treat it as a first-class citizen as far as publishing packages goes.

Add to that the frequent compilation issues that happen as TypeScript adds or removes types in the global imports, changes language conventions, and updates compilation checks whenever a new version gets released.

Look at the comment under the comment you mention - those issues are about the TS type system, NOT the TS syntax. TS syntax is highly stable, and is the only thing JSR relies on :)
How is that true if the npm compatibility layer serves compiled js and .d.ts files?
Both `.d.ts` and `.js` files can be created without understanding the TS type system (this is what JSR does). Creating both of those is just a TS syntax transform - one that is highly stable (esbuild and friends all do it already).
I know .is can be emitted as an ast transformation, but I'm surprised by d.ts files. Even inferred types?
We do some basic inference, but that's why we require explicit return types in the public API, see https://jsr.io/docs/about-slow-types . TypeScript itself will ship with a new `isolatedDeclarations` option in the next release which is the same thing. The point is that no inference other than simple literal inference needs to be done to get the type of the public API. That way generating .d.ts files can be done by any tool by merely looking at the syntax without type inference.
> The "web standard" part is meaningless considering that most production websites will bundle the files together as part of their build/optimization process for size and loading speed, leaving only a giant chunk(s) that resembles nothing like the original ES modules.

I mean code itself will be non readable, but in modern build ouput chunks are using regular esm modules, at least for Vite, Rollup, Parcel and so on. Webpack might be different story, but still.

> This makes the already complicated module resolution logic in most bundler/packaging tools even more complicated as they must account for the intricacies of another package manager. A house of cards being stacked on another house of cards...

JSR is a registry, not a package manager. npm allows you to add multiple registries and JSR provides an npm API endpoint. From npm's point of view packages coming from JSR are the same as those coming from the npm registry itself. The only difference is where the package is coming from, but all semver resolution, etc are done as usual by whatever package manager you use.

Disclaimer: I wrote a good portion of the npm tarball generation code.

I'm really curious how you handle typescript versions and options that affect emit
- `isolatedModules: true` is required - Decorators: standard spec decorators only - JSX: tsconfig options are lifted into JSX pragma comments on publish (configuration goes into code so that consumers can emit based on these options still)

TypeScript has very very few options that affect emit, and effectively everyone has settled on one subset now.

> TypeScript has very very few options that affect emit, and effectively everyone has settled on one subset now.

Very confused by this statement. There's lots of options that affect emit. And what do you mean a subset has been settled on? Take any 2 TypeScript projects and I'd wager there's a high chance they have options that emit code differently.

Config aside TS emits .d.ts that is not always backwards compatible with older TS versions. Does jsr support typeVersions for such situations?

> This sounds like another building service I don't control. There's already too much magic in publishing transpiled TypeScript packages but at least the current tools let me control exactly what artifacts get pushed out.

For tools that natively support TypeScript, you can directly consume the TypeScript source code. No transpilation necessary. The complexities are only introduced (well not really introduced, just moved somewhere else) for tools that do not natively support JS.

> The "web standard" part is meaningless considering that most production websites will bundle the files together as part of their build/optimization process for size and loading speed, leaving only a giant chunk(s) that resembles nothing like the original ES modules.

That doesn't matter. If everyone settles on one standard, even if you do post process, tools become simpler. The idea is not that you must ship your code unbundled to the browser. It is to reduce the amount of complexity incurred in the process from source -> dist. I am sure you agree that the lower the difference between source and dist, the easier debugging and developement in general are :)

> A super of npm means that the packages created for this repository will not work in the npm ecosystem. What is this if not a "replacement" of the npm registry and further fragmentation of the JS ecosystem?

They do work in npm. In npm packages you can depend on JSR packages, and on JSR you can depend on npm packages.

> This makes the already complicated module resolution logic in most bundler/packaging tools even more complicated as they must account for the intricacies of another package manager. A house of cards being stacked on another house of cards...

Nope! JSR is not a package manager. JSR is only a runtime. When you install into a node_modules folder, the layout in this folder is the same between JSR and npm packages. So any downstream tool does not even need to know JSR is in the loop - it just works. This is one of the key points to ensure easy adoption of JSR by anyone.

> It seems like every benefit that this project offers can be fixed in the current ecosystem by better client-side tooling that enforces standards during the publishing process while keeping full backward compatibility with over a decade of packages published and without fragmenting the ecosystem.

I understand this.

The problem is the following: npm is owned by GitHub/Microsoft, and GitHub/Microsoft does not care about npm. To GitHub, npm is a cost center. npm has no dedicated product manager at GitHub. Last I heard, npm has _two_ dedicated engineers working on it. The last real new feature npm shipped was package provenance (last year). It took npm 5 years to ship the file browser (because GitHub/Microsoft did not care about npm, and thus effectively froze all feature dev work on npm).

If we want innovation in the space, we need to do it in a way that a) keeps backwards compat with npm (which JSR does, both ways), but b) we need to move away from npm in the long term. JSR is an attempt at this (while trying to stay within the constraints posed).

Thank you. I stand corrected on some points. The messy ecosystem of npm, yarn, pnpm, JS, TS, ES, and ESM has all cost me a good amount of white hair.

A good source of my confusion came with not immediately recognizing that a new package registry does not require a new package manager to work with. I think the "jsr" commands in the code samples and the jsr.json make it feel like there's a new package manager in play here. If the team has made it so that npm works with "jsr" and vice versa, kudos to them because that's a big win for the users.

As far as I can tell, the "npx jsr add @luca/flag" command given for using JSR with Node itself calls "npm i @luca/flag@npm:@jsr/luca__flag" and is equivalent. You get a normal node_modules+package.json+package-lock.json output that the npm command is fully able to work with.

It looks like JSR re-publishes all of its packages translated into JS + .d.ts files onto npm under the @jsr scope. Regular npm packages have no trouble depending on JSR packages because they just depend on the npm-published version of them.

yup, we added the `jsr` tool mostly so that you don't have to be aware of the @jsr scope that's used under the hood. We're not publishing to npm though, but rather mapping the @jsr scope to the JSR registry in the project's .npmrc file.
CORRECTION:

An employee of GitHub has reached out to me to inform me that there are more than 2 engineers people working on npm full time, including multiple full time support staff and full time staff to review possible malware. The engineering team working on npm is "substantially larger than 2". Additionally they informed me that a new product manager for npm will be starting tomorrow.

Even though I made no claim to the contrary, I would like to emphasise that there are obviously current product managers at GitHub that product manage npm as _one of_ the projects they manage.

In addition to some of the other answers already:

1. The focus on typescript and typings distribution is useful/interesting for the "promise" that all packages in the registry have typings. If you can trust that every package in JSR has types then you avoid/simplify that current cycle of: npm install a package, find out it doesn't have types, try to install a package of the same name with @types/ in front of it, find out it doesn't exist, look for an alternative library with types or write your own types or give up and `declare module "thatpackagename"` any-type it.

2. Just because "most" production websites do bundling doesn't mean that needs to still be the status quo in 2024. Bundling is increasingly a "premature optimization" and you might not need it. It can be worth investigating if your site/app works well enough with just ESM delivered straight to browsers in 2024 versus what you "gain" from bundling. You might be surprised at how well current browsers can load ESM and the optimizations they already do for you. But even if you do still need bundling ESM is a huge advantage over other formats (especially CommonJS) and every current bundler produces better, more optimized output the more ESM you provide. Their outputs also increasingly look like ESM, and if that isn't the current default it is the "soon" default. esbuild in particular (which also backs some of vite's tools and others) has an ESM bundle output that is delightful, isn't yet the default ("soon") but is a simple "format" switch away. Those ESM bundles are great in today's browsers. ESM matters a lot to bundlers, bundlers are increasingly ESM in their internals and output. Browsers are great with ESM. The "web-standard" part is pretty meaningful today and getting stronger.

Where can one read up on [2.]?
I got into some details on how I do light-weight apps (especially during development) without bundling in a nearby comment as well: https://news.ycombinator.com/item?id=39564986

To resummarize: sometimes it is fine to prune and ship node_modules to your web server, you can use import maps to handle "node-style 'bare' imports", and you may only need to "spot bundle" to ESM a single dependent library or two that doesn't ship ESM that will directly work in the browser (which should be a decreasing list).

After that it's a matter of using your browser's Dev Tools to tell you what your performance looks like and where your bottlenecks are. That can tell you a lot to help you determine if you really need to bundle more of your site/app or if you can spot-bundle/adjust an import map for just a specific sub-graph. With HTTP/2 and HTTP/3 the "per-file" "penalty" is a lot less, though still somewhat exists. Even some properly configured HTTP/1.1 servers it is not always as bad as history tells us it was (properly configured HTTP/1.1 did support some connection sharing). About the only remaining thing a bundle might buy you on HTTP/2+ is maybe better behavior with compression on the bundle as a single whole, but even that isn't a given because Brotli's dictionary was designed to work at the whole connection level and that's often the default compression in HTTP/2+. But again, your conditions may vary based on your real code and the specific servers and browsers you need to support, so leverage your browser Dev Tools and check the real numbers.

You’re 110% on point.

Publishing is too complicated? Let’s add something else: https://xkcd.com/927/

This is all node and npm’s fault, they’re sinking billions of dollars of developer time worldwide and they should be called out on it.

Kind of jarring now that the standard library documentation on the Deno website just redirects to JSR.

Speaking of which, the new deno.com site looks like a marketing website designed by an AI. Lacks all of the pragmatic unix-y character that Deno originally had, and is just filled with meaningless numbers and metrics and way too much blank space. It's like they're selling a product... which I guess is what Deno is now. VC funding will do that to you.

I hate modern day landing pages. Even those single page websites where some animation plays while scrolling down were more interesting.
Oh boy. Reading the title ("JSR: The JavaScript Registry" at the time of writing) I had high hopes this might be what I'm looking for: Dependency management for JavaScript in the browser. It's something entirely different.

I tried to look for something lately that:

1. Makes it easy to download specified versions of JS libs.

2. Exposes these libs as proper ES6 modules.

I get why (2) is a bit tricky, it'd be fantastic but more of a nice to have anyway.

I don't really get why there's no popular tool for (1) yet. Sure, I can just fetch the files I want from a CDN and commit them to a vendor directory, or I can write a little script that downloads them from a CDN. Or hey, I can fetch frontend dependencies via NPM and have a script that builds/copies the stuff. But that's a bit much labour for something that I thought would be a relatively common requirement.

Am I in the minority to occasionally want to build web apps _without_ bundlers? Being able to skip the build step is very powerful both for keeping things simple and turnaround times fast, IMHO.

esm.sh is a decent solution for this. It's not totally ideal... but it'll turn NPM modules into ES6-compatible URLs no problem.
Oooh, esm.sh looks pretty close to what I'm looking for, thanks!
You can just use npm and ship node_modules on your website. It's probably "huge" so you probably want to clean out dev dependencies first (`npm prune --omit=dev` is one way to clean that) and you might find it useful to search for big binaries to filter out and redundant directories that you don't need (libraries that still include all of UMD and CommonJS and ESM builds even though you only need one), and there may still be libraries that don't directly load in the browser and you need to spot bundle with a tool like esbuild to a vendor directory.

Mostly the only other glue you need after that is an import map.

I find this flow useful (ship an optionally pruned node_modules, spot build specific vendor libraries, add import map), especially for lightweight development/testing, and so I did document it specifically from start to finish for one of my libraries (which is sort of a "framework"), it includes a vendor build one-liner:

https://worldmaker.net/butterfloat/#/getting-started?id=setu...

(The Example section after the Dev Environment one shows the import map at the top of the example HTML if you are looking for that. I forgot that's where it was when re-reading this.)

When would I want to use JSR? The "Why JSR?" section simply says JSR is a superset of NPM that can be used with any javascript package manager. But NPM itself can also be used with any javascript package manager as far as I know. It seems like the benefit they're trying to add is strict type support for packages, but the fact that it's a superset of NPM means all existing packages that lack types are still supported by JSR. So, what benefit is so great that it's worth segmenting the ecosystem?

Side rant: not at all a fan of the decision to punish libraries ("slow types") that use type inference by reducing their score.

> not at all a fan of the decision to punish libraries ("slow types") that use type inference by reducing their score.

Do you feel that using type inference doesn't actually reduce performance, or just that it's not a big enough problem to warrant a reduced score?

Type inference in dependencies does not reduce performance for users, but just for JSR. Here they’re going “the Google way” by adding some random metric that benefits them, not users. Once generated, .d.ts files do not contain inference.
The performance of type inference matters for runtimes which work with TypeScript files directly, rather then using the transpiled .js + .d.ts files like Deno. This has several benefits in that we can jump to the source on "go to definition" rather than some random .d.ts file among other things. Deno uses the original TS files for type checking as well and that's why inference also affects type checking performance in some runtimes.

By ensuring explicit return types in the public API, the generation of .d.ts files is turned into a mere syntax transform, rather than requiring the tsc compiler. This is something TypeScript will ship with in the next release itself. They're adding a new `isolatedDeclarations` option which is the same thing.

In terms of "when you would use it" - it doesn't necessarily solve a different problem than npm. I think it arguably solves the same problem more effectively (though it is certainly early days and npm still does a lot of things JSR doesn't). I also don't know that we're segmenting the ecosystem, as JSR packages interoperate with npm packages, and you can use one from the other. JSR will end up being additive to npm.

For module authors, we're hoping JSR will be helpful in the following ways:

1.) You can develop and publish TypeScript source, and let JSR handle transpilation and generating .d.ts files for runtimes that don't natively support TypeScript. Especially nice if you are using Deno or Bun (that do natively support TypeScript), and don't have tsc in your workflow otherwise.

2.) JSR generates API docs for you on your package page based on your source code and comments.

3.) JSR has a great DX around publishing packages from GitHub Actions using OIDC (no juggling tokens)

4.) JSR automatically provides provenance for published versions of packages

For module consumers, it helps too:

1.) Compatible with both Deno and existing npm-based projects

2.) Package info and docs provided centralized on the jsr.io site

3.) Quality scores that encourage authors to make their packages fast and well documented

4.) Access to TypeScript source for packages (not just transpiled output)

Absolutely more to be done, but we're hoping that JSR will provide enough extra value to both module authors and consumers that both will prefer to use it when possible.

Sounds like a good value prop. One thing that I'm hoping is to reduce dependency on a single private company, npm inc., in publishing and distributing modules. But you didn't list this as a value prop. (I notice other posters are upset at the 'fragmentation' of module distribution, but I see this as a strength, not a weakness.)
(comment deleted)
After reading the "Why JSR?" article I still don't understand why this exists.

It looks like it has a few niceties but also some limitations. To catch on you need to convince some significant segment of both package publishers and consumers to switch to it. That means there needs to be some killer feature they just can't get with the existing registries, and I'm not seeing anything like that.

> After reading the "Why JSR?" article I still don't understand why this exists

The cynical answer: they realized it's actually advantageous to have a centralized package registry instead of importing from random urls, and as a for-profit VC backed company they'd rather not have their entire ecosystem depend on the Microsoft-owned NPM.

The optimistic answer: having a second registry makes the entire JS/TS ecosystem less fragile by not having a single point of failure.