forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import type { JsrPackageMeta, JsrPackageInfo } from '#shared/types/jsr'
2
3const JSR_REGISTRY = 'https://jsr.io'
4
5/**
6 * Check if a scoped npm package exists on JSR with the same name.
7 *
8 * This only works for scoped packages (@scope/name) since:
9 * 1. JSR only has scoped packages
10 * 2. We can only authoritatively match when names are identical
11 *
12 * Unscoped npm packages (e.g., "hono") may exist on JSR under a different
13 * name (e.g., "@hono/hono"), but we don't attempt to guess these mappings.
14 *
15 * @param npmPackageName - The npm package name (e.g., "@hono/hono")
16 * @returns JsrPackageInfo with existence status and metadata
17 */
18export const fetchJsrPackageInfo = defineCachedFunction(
19 async (npmPackageName: string): Promise<JsrPackageInfo> => {
20 // Only check scoped packages - we can't authoritatively map unscoped names
21 if (!npmPackageName.startsWith('@')) {
22 return { exists: false }
23 }
24
25 // Parse scope and name from @scope/name format
26 const match = npmPackageName.match(/^@([^/]+)\/(.+)$/)
27 if (!match) {
28 return { exists: false }
29 }
30
31 const [, scope, name] = match
32
33 try {
34 // Fetch JSR package metadata
35 const meta = await $fetch<JsrPackageMeta>(`${JSR_REGISTRY}/@${scope}/${name}/meta.json`, {
36 // Short timeout since this is a nice-to-have feature
37 timeout: 3000,
38 })
39
40 // Find latest non-yanked version
41 const versions = Object.entries(meta.versions)
42 .filter(([, v]) => !v.yanked)
43 .map(([version]) => version)
44
45 versions.sort()
46 const latestVersion = versions[versions.length - 1]
47
48 return {
49 exists: true,
50 scope: meta.scope,
51 name: meta.name,
52 url: `${JSR_REGISTRY}/@${meta.scope}/${meta.name}`,
53 latestVersion,
54 }
55 } catch {
56 // Package doesn't exist on JSR or API error
57 return { exists: false }
58 }
59 },
60 {
61 maxAge: 60 * 60 * 24, // 1 day
62 swr: true,
63 name: 'jsr-package-info',
64 getKey: (name: string) => name,
65 },
66)