Skip to content

Comments

feat: replace homepage frameworks with fun rotating "n p m x" picks#1616

Draft
serhalp wants to merge 3 commits intomainfrom
feat/hourly-npmx-picks
Draft

feat: replace homepage frameworks with fun rotating "n p m x" picks#1616
serhalp wants to merge 3 commits intomainfrom
feat/hourly-npmx-picks

Conversation

@serhalp
Copy link
Member

@serhalp serhalp commented Feb 24, 2026

🔗 Linked issue

😶 discussed on Discord

🧭 Context

  • The hardcoded framework package quick list has an unclear purpose. More and more folks are requesting to add their link. It isn't a scalable feature.
  • The below is a subtle homage to the OG npmjs.com (they used to show a random "definition" of "npm" on page load).
  • It's a fun little easter egg that may lead curious users to wonder how the packages are selected... encouraging them to look at our repo, learn about our atproto features, and join our community 😈.
  • It's somewhat of a metaphor for npmx being borne out of the ecosystem.

📚 Description

Show 4 randomly-selected packages whose names contain the letters n, p, m, x, with the matching letter highlighted in bold + accent colour (one distinct colour per letter when no user accent is set).

image Screenshot 2026-02-23 at 19 34 45

Selection algorithm:

  1. Fetch top 500 popular packages from the Algolia search index (empty query, default popularity ranking). Algolia doesn't support filtering by name substring (and doesn't know about our social likes), so we filter these results down after the fact (not deprecated, >=10k downloads/30d, modified <2yrs).
  2. For each letter (n, p, m, x):
    1. Take 30 random candidates whose name contains that letter and check their social like count.
    2. If there are candidates with >=5 community likes, keep only those; otherwise, keep all.
    3. Remove any remaining candidates already assigned to another letter in "n p m x".
    4. Randomly pick one remaining candidate.
    5. If there are no remaining candidates, pick the hardcoded default for this letter (nuxt, pnpm, module-replacements, oxfmt).

Results are cached for 1 hour via with SWR, so all users see the same picks for about an hour, and no user ever experiences a cache miss (and Algolia/constellation slowness).

Show 4 randomly-selected packages whose names contain the letters n, p, m, X, with the matching
letter highlighted in bold + accent colour (one distinct colour per letter when no user accent is
set).

Selection algorithm:
1. Fetch top 500 popular packages from the Algolia search index (empty query, default popularity
   ranking). Algolia doesn't support filtering by name substring (and doesn't know about our social
   likes), so we filter these results down after the fact (not deprecated, >=10k downloads/30d,
   modified <2yrs).
2. For each letter (n, p, m, x)
  1. Take 30 random candidates whose name contains that letter and check their social like count.
  2. If there are candidates with >=5 community likes, keep only those; otherwise, keep all.
  3. Randomly pick one remaining candidate.
  4. If there are no remaining candidates, pick the hardcoded default for this letter (nuxt, pnpm,
     module-replacements, oxfmt).

Results are cached for 1 hour via with SWR, so all users see the same picks for about an hour, and
no user ever experiences a cache miss (and Algolia/constellation slowness).
@vercel
Copy link

vercel bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Feb 24, 2026 2:30am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 24, 2026 2:30am
npmx-lunaria Ignored Ignored Feb 24, 2026 2:30am

Request Review

@serhalp serhalp changed the title feat: replace framework pkgs with random "n p m x" picks feat: replace homepage framework packages with fun rotating "n p m x" picks Feb 24, 2026
@serhalp serhalp changed the title feat: replace homepage framework packages with fun rotating "n p m x" picks feat: replace homepage frameworks with fun rotating "n p m x" picks Feb 24, 2026
@github-actions
Copy link

github-actions bot commented Feb 24, 2026

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
lunaria/files/ar-EG.json Localization changed, will be marked as complete.
lunaria/files/az-AZ.json Localization changed, will be marked as complete.
lunaria/files/bg-BG.json Localization changed, will be marked as complete.
lunaria/files/bn-IN.json Localization changed, will be marked as complete.
lunaria/files/cs-CZ.json Localization changed, will be marked as complete.
lunaria/files/de-DE.json Localization changed, will be marked as complete.
lunaria/files/en-GB.json Localization changed, will be marked as complete.
lunaria/files/en-US.json Source changed, localizations will be marked as outdated.
lunaria/files/es-419.json Localization changed, will be marked as complete.
lunaria/files/es-ES.json Localization changed, will be marked as complete.
lunaria/files/fr-FR.json Localization changed, will be marked as complete.
lunaria/files/hi-IN.json Localization changed, will be marked as complete.
lunaria/files/hu-HU.json Localization changed, will be marked as complete.
lunaria/files/id-ID.json Localization changed, will be marked as complete.
lunaria/files/it-IT.json Localization changed, will be marked as complete.
lunaria/files/ja-JP.json Localization changed, will be marked as complete.
lunaria/files/mr-IN.json Localization changed, will be marked as complete.
lunaria/files/nb-NO.json Localization changed, will be marked as complete.
lunaria/files/ne-NP.json Localization changed, will be marked as complete.
lunaria/files/pl-PL.json Localization changed, will be marked as complete.
lunaria/files/pt-BR.json Localization changed, will be marked as complete.
lunaria/files/ru-RU.json Localization changed, will be marked as complete.
lunaria/files/ta-IN.json Localization changed, will be marked as complete.
lunaria/files/te-IN.json Localization changed, will be marked as complete.
lunaria/files/uk-UA.json Localization changed, will be marked as complete.
lunaria/files/zh-CN.json Localization changed, will be marked as complete.
lunaria/files/zh-TW.json Localization changed, will be marked as complete.
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

}

// Algolia npm-search API - return mock popular packages
if (host.endsWith('-dsn.algolia.net') && pathname.endsWith('/query')) {

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High

'
-dsn.algolia.net
' may be preceded by an arbitrary host name.

Copilot Autofix

AI about 20 hours ago

General fix: when matching a host, don’t rely on arbitrary suffix checks that permit uncontrolled subdomains. Instead, either (a) match against an explicit allowlist of exact hostnames, or (b) if you must use suffix logic, ensure the parent domain is exactly the expected one (e.g., *.algolia.net where the registrable domain is validated).

Best fix here without changing intended functionality: replace host.endsWith('-dsn.algolia.net') with an explicit whitelist of Algolia DSN hosts actually used by the app/tests. If you can’t rely on that knowledge here, a safe compromise is to accept only hosts whose registrable domain is algolia.net and whose immediate label before that ends with -dsn (or matches expected patterns). Staying minimal and not assuming extra project context, the clearest improvement that addresses CodeQL’s complaint is to require that the host is either exactly algolia.net (for safety) or has algolia.net as its parent domain:

  • Split host on ..
  • Ensure the last two labels are exactly ['algolia', 'net'].
  • Optionally, ensure there is at least one label before that and that it ends with -dsn.
  • Keep the rest of the logic intact.

Within modules/runtime/server/cache.ts, around line 244, we’ll compute const hostParts = host.split('.'), check that hostParts.length >= 3, verify hostParts.slice(-2).join('.') === 'algolia.net', and check that the immediate subdomain ends with -dsn. Then we use this boolean instead of the broad host.endsWith('-dsn.algolia.net') condition. No new imports are needed; all functionality can be implemented with built-in string methods.

Suggested changeset 1
modules/runtime/server/cache.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/modules/runtime/server/cache.ts b/modules/runtime/server/cache.ts
--- a/modules/runtime/server/cache.ts
+++ b/modules/runtime/server/cache.ts
@@ -241,7 +241,13 @@
   }
 
   // Algolia npm-search API - return mock popular packages
-  if (host.endsWith('-dsn.algolia.net') && pathname.endsWith('/query')) {
+  const algoliaHostParts = host.split('.')
+  const isAlgoliaDsnHost =
+    algoliaHostParts.length >= 3 &&
+    algoliaHostParts.slice(-2).join('.') === 'algolia.net' &&
+    algoliaHostParts[algoliaHostParts.length - 3].endsWith('-dsn')
+
+  if (isAlgoliaDsnHost && pathname.endsWith('/query')) {
     return {
       data: {
         hits: [
EOF
@@ -241,7 +241,13 @@
}

// Algolia npm-search API - return mock popular packages
if (host.endsWith('-dsn.algolia.net') && pathname.endsWith('/query')) {
const algoliaHostParts = host.split('.')
const isAlgoliaDsnHost =
algoliaHostParts.length >= 3 &&
algoliaHostParts.slice(-2).join('.') === 'algolia.net' &&
algoliaHostParts[algoliaHostParts.length - 3].endsWith('-dsn')

if (isAlgoliaDsnHost && pathname.endsWith('/query')) {
return {
data: {
hits: [
Copilot is powered by AI and may make mistakes. Always verify output.
@codecov
Copy link

codecov bot commented Feb 24, 2026

Codecov Report

❌ Patch coverage is 0% with 11 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
app/pages/index.vue 0.00% 10 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@graphieros
Copy link
Contributor

Since 4 packages are displayed, I think the original width of the wrapper could be restored (#1591 reduced max-w to wrap the long list)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants