npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xenterprises/nuxt-x-affiliate

v0.2.0

Published

A drop-in Nuxt layer for Amazon, Walmart, and multi-network affiliate sites.

Readme

@xenterprises/nuxt-x-affiliate

A drop-in Nuxt layer for Amazon, Walmart, and multi-network affiliate sites.

Hand a consuming site a single extends: ['@xenterprises/nuxt-x-affiliate'] line and it gets navbar, footer, FTC-compliant disclosure, product pages, category pages, search, deals, best-of, contact, disclosure, privacy, terms, and home — all driven by app.config.ts overrides and runtimeConfig env vars.

Built for the 40–50 affiliate sites the team runs. Add the layer, set your network tags, override what you want. Everything else works.


Quick start

// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@xenterprises/nuxt-x-affiliate'],
  // ... your other config
});
// app.config.ts
export default defineAppConfig({
  xAffiliate: {
    name: 'My Picky Site',
    tagline: 'Only the best deals make the cut.',
    domain: 'mypickysite.com',
    defaultNetwork: 'amazon', // 'amazon' | 'walmart' | 'multi'
    contactEmail: '[email protected]',
    navbar: {
      links: [
        { label: 'Home', to: '/', icon: 'i-lucide-home' },
        { label: 'Deals', to: '/deals', icon: 'i-lucide-tag' },
        // ...
      ],
    },
    footer: { /* ... */ },
    disclosure: { /* ... */ },
    productCard: { /* ... */ },
  },
});
# env vars
NUXT_PUBLIC_AMAZON_TAG=yourtag-20
NUXT_PUBLIC_WALMART_AFFILIATE_ID=abc123
NUXT_PUBLIC_AFFILIATE_API_ENDPOINT=https://api.yoursite.com

That's it. Run nuxi dev and the site has a homepage, deals page, product pages, category pages, search, contact, legal pages, FTC disclosure — all working against the bundled mock data until you wire up a real API.


Public API

Composables

| Composable | What it does | | --- | --- | | useAffiliate() | Core data API. Returns { list, get, featured, deals, related, categories, category, categoryProducts }. Each call returns the standard useAsyncData shape ({ data, pending, error, refresh }). | | useAffiliateConfig() | Resolved config with sensible fallbacks. Returns { config, productCard, productGrid, disclosure, navbar, footer, seo }. | | useAffiliateContent() | v0.2.0 — XAC content API. Returns { config, getMerchant, hasTag, taggedUrl, taggedLink, formatPrice, trackClick }. | | useAffiliateLink() | Build affiliate-tagged URLs. { build(url, network), forProduct(product) }. | | useAffiliateTracker() | Fire-and-forget click tracking. Uses sendBeacon when available. |

Components

All components are auto-imported. Naming follows the team convention: X/<Namespace>/<Component><X<Namespace><Component>>.

XAffNavbar
XAffFooter
XAffFooterLegal   # bottom-bar legal footer with FTC disclosure band
XAffDisclosure
XAffSearchBar
XAffPrice
XAffStarRating
XAffNetworkBadge
XAffBuyButton
XAffProductCard
XAffProductGrid
XAffProductDetail
XAffCategoryCard
XAffCategoryGrid
XAffCategoryList
XAffHomeHero
XAffHomeFeaturedProducts
XAffHomeTopDeals
XAffHomeCategories
XAffHomeHowItWorks
XAffHomeNewsletter
XAffHomeFAQ
XAffHomeDisclosure

Pages

The layer ships the following pages. Consuming sites can override any of them by creating a file with the same path in their own app/pages/.

/                   Home (hero, featured, deals, categories, FAQ)
/categories         All categories
/categories/[slug]  Category detail
/product/[slug]     Product detail
/search             Search results
/deals              Today's top deals
/best/[slug]        Best-of roundup
/about              About
/contact            Contact form
/disclosure         FTC affiliate disclosure
/privacy            Privacy policy
/terms              Terms of service

Layouts

layouts/default.vue    Navbar + slot + Footer (the default for every page)
layouts/blank.vue      Slot only — for custom layouts in consuming sites

Types

import type {
  Product,
  ProductListResponse,
  Category,
  AffiliateNetwork,
  ProductCardConfig,
  ProductGridConfig,
  AffiliateDisclosure,
  Money,
  XAffiliateConfig,
} from '#affiliate/types'; // (also available from the global auto-imports)

Configuration reference

app.config.tsxAffiliate

export default defineAppConfig({
  xAffiliate: {
    // Identity
    name: 'Best Picks',
    tagline: 'Hand-picked deals, updated daily.',
    domain: 'bestpicks.example.com',
    contactEmail: '[email protected]',
    logo: '/logos/light.webp',
    logoDark: '/logos/dark.webp',

    // Default network when a product has none specified
    defaultNetwork: 'amazon' as AffiliateNetwork,

    // Top-level navigation
    navbar: {
      links: [
        { label: 'Home', to: '/', icon: 'i-lucide-home' },
        { label: 'Deals', to: '/deals', icon: 'i-lucide-tag' },
        // ...
      ],
    },

    // Footer columns + body copy
    footer: {
      body: 'As an Amazon Associate and Walmart Affiliate, we earn from qualifying purchases.',
      columns: [
        { headerLabel: 'Shop', links: [{ label: 'Deals', to: '/deals' }] },
        // ...
      ],
      socials: [
        { name: 'Twitter', url: 'https://twitter.com/...', icon: 'i-simple-icons-twitter' },
      ],
    },

    // FTC disclosure (mandatory)
    disclosure: {
      shortText: 'Short banner text shown above the fold.',
      longText: 'Full disclosure shown in /disclosure and the footer.',
      dismissable: true,
    },

    // Product card defaults
    productCard: {
      showImage: true,
      showRating: true,
      showPrime: true,
      showFreeShipping: true,
      showBadges: true,
      showNetwork: true,
      showStrike: true,
      imageAspect: 'video', // 'square' | 'video' | 'wide'
      variant: 'default',   // 'default' | 'compact' | 'horizontal' | 'list'
      ctaLabel: 'View Deal',
      ctaConfirm: false,    // set true to show a confirm modal before navigating
    },

    // Product grid defaults
    productGrid: {
      cols: { base: 1, sm: 2, md: 3, lg: 4 },
      gap: 'normal', // 'tight' | 'normal' | 'loose'
      emptyText: 'No products found.',
      loadingText: 'Loading products…',
    },

    // SEO defaults
    seo: {
      title: 'Best Picks — Hand-picked deals',
      description: 'Hand-picked products from Amazon, Walmart, and more.',
      ogImage: '/og.png',
    },
  },
});

runtimeConfig.public — env vars

| Env var | Purpose | | --- | --- | | NUXT_PUBLIC_AFFILIATE_API_ENDPOINT | Backend API base. Empty = use bundled mock data. | | NUXT_PUBLIC_AMAZON_TAG | Your Amazon Associates tag (e.g. yoursite-20). | | NUXT_PUBLIC_WALMART_AFFILIATE_ID | Your Walmart affiliate ID. | | NUXT_PUBLIC_TARGET_AFFILIATE_ID | Target affiliate ID. | | NUXT_PUBLIC_BESTBUY_AFFILIATE_ID | Best Buy affiliate ID. | | NUXT_PUBLIC_DEFAULT_NETWORK | Default network. | | NUXT_PUBLIC_AFFILIATE_TRACK_ENDPOINT | Optional click-tracking beacon. |


Backend API contract

The data composable hits these endpoints (replace with your own backend):

GET  /public/products
     ?category=<slug>
     &network=<amazon|walmart|...>
     &search=<q>
     &sort=<price-asc|price-desc|rating|newest>
     &page=<n>
     &pageSize=<n>
     → ProductListResponse

GET  /public/products/:slug          → Product | null
GET  /public/products/featured?limit → Product[]
GET  /public/products/deals?limit    → Product[]
GET  /public/products/:slug/related? → Product[]

GET  /public/categories              → Category[]
GET  /public/categories/:slug        → Category | null
GET  /public/categories/:slug/products → ProductListResponse

Type shapes

interface Product {
  id: string;
  slug: string;
  externalId: string;   // ASIN, Walmart item id, etc.
  network: 'amazon' | 'walmart' | 'target' | 'bestbuy' | 'ebay' | 'etsy' | 'custom';
  title: string;
  description?: string;
  brand?: string;
  image: string;
  images?: string[];
  price: { amount: number; currency: string };
  listPrice?: { amount: number; currency: string };
  rating?: number;       // 0-5
  reviewCount?: number;
  inStock?: boolean;
  affiliateUrl: string;  // base URL — useAffiliateLink() tags it
  url?: string;          // un-tagged fallback
  categories?: string[];
  features?: string[];
  prime?: boolean;
  freeShipping?: boolean;
  badges?: Array<{ label: string; color?: string; variant?: string; icon?: string }>;
  updatedAt?: string;
}

interface Category {
  slug: string;
  name: string;
  description?: string;
  image?: string;
  icon?: string;
  parentSlug?: string;
  productCount?: number;
}

Customization patterns

Override a page

<!-- app/pages/index.vue in your consuming site -->
<template>
  <div>
    <XAffHomeHero
      :primary-cta="{ label: 'My Custom CTA', to: '/deals' }"
    />
    <XAffHomeCategories />
    <!-- ... -->
  </div>
</template>

Build a custom page using primitives

<script setup lang="ts">
const { list } = useAffiliate();
const { data: results } = list({ category: 'kitchen', sort: 'rating' });
</script>

<template>
  <UContainer class="py-12">
    <h1>Top kitchen picks</h1>
    <XAffProductGrid :items="results?.items ?? []" />
  </UContainer>
</template>

Use only specific components

<script setup>
import { getNetworkMeta, formatPrice } from '@xenterprises/nuxt-x-affiliate/app/types/networks';
</script>

Build a custom Buy button

<script setup lang="ts">
const { forProduct } = useAffiliateLink();
const product = ref(/* ... */);
const finalUrl = computed(() => forProduct(product.value));
</script>

Compliance (FTC)

This layer is designed to make FTC compliance easy and consistent across all 40–50 consuming sites:

  • In-content article disclosure<XACArticleDisclosure /> belongs at the top of every review / article page (FTC 16 CFR §255.5 — "in close proximity" to the claim). Optional Published/Updated timestamps.
  • Disclosure banner<XAffDisclosure /> renders a short banner at the top of every page by default. Dismissable is configurable. Toggle per-site via xAffiliate.disclosure.banner: boolean (default true). See Hiding the top disclosure banner.
  • Disclosure page/disclosure ships with full FTC text out of the box.
  • Legal footer<XAffFooterLegal /> is the dedicated bottom-bar affiliate legal footer (FTC disclosure band + X Enterprises, LLC copyright + network chips + 5 off-site legal links to x.enterprises/legal/{slug}). Single source of truth for the copyright — the marketing footer's columns deliberately render no copyright of their own, and the redundant bottom-of-page <XFooterXLegal /> is omitted, so there's no drift.
  • Copyright override — every consuming site renders the same Copyright ©2016-{year} X Enterprises, LLC. All Rights Reserved. line by default. Override per-site via the copyrightHolder config or the <XAffFooterLegal copyright="..." /> prop.
  • Off-site legal home — legal links point to https://x.enterprises/legal/{slug} by default (the canonical x.enterprises legal hub). Override xAffiliate.legal.baseUrl per site to redirect to a local legal page.
  • Buy button<XAffBuyButton /> can be configured to show a confirm modal (productCard.ctaConfirm: true) before navigating to the retailer.
  • Click tracking — every Buy button click can be sent to NUXT_PUBLIC_AFFILIATE_TRACK_ENDPOINT via useAffiliateTracker().

Override xAffiliate.disclosure in your app.config.ts to customize the text per site.


Local development

npm install
npm run dev            # starts playground on http://localhost:3000
npm run dev:prepare    # regenerates .nuxt types
npm run build          # builds playground

The playground runs against bundled mock data — no backend required.


Architecture

app/
├── app.config.ts            # defaults (overridable per consuming site)
├── app.vue                  # UApp + NuxtLayout + NuxtPage
├── error.vue                # 404 / error page
├── layouts/
│   ├── default.vue          # Navbar + slot + Footer
│   └── blank.vue            # slot only
├── pages/                   # home, deals, search, product, category, legal...
├── components/
│   └── X/Aff/               # the public component surface
│       ├── *.vue            # primitives
│       └── Home/*.vue       # home page sections
├── composables/
│   ├── useAffiliate.ts      # data API
│   ├── useAffiliateConfig.ts
│   ├── useAffiliateLink.ts  # URL tagging
│   └── useAffiliateTracker.ts
├── types/                   # shared TypeScript types
└── utils/mockData.ts        # bundled fallback data

Playground

The repo ships a .playground/ that demos the layer wired up with @xenterprises/nuxt-x-marketing for a polished landing-page feel.

cd .playground
npm run dev

The playground extends both layers:

// .playground/nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    ['..', { install: true }],                            // the affiliate layer
    ['@xenterprises/nuxt-x-marketing', { install: true }], // marketing chrome
  ],
  modules: ['nuxt-shiki'],
});

It combines xAffiliate (products, disclosure, navbar/footer) and xMarketing (hero, sections, features, FAQ, CTA, footer columns) in a single app.config.ts, and uses the marketing layer's transparent navbar

  • footer as the site chrome.

Pages in the playground

| Route | Description | | --- | --- | | / | Marketing hero + affiliate home sections (categories, featured, deals, how-it-works, newsletter, FAQ, disclosure) | | /deals | Marketing hero + all deals grid | | /search | Search input + results grid | | /categories | All categories (grid) | | /categories/[slug] | Category detail with filters and sort | | /product/[slug] | Product detail with related | | /best/[slug] | Marketing hero + best-of grid | | /about | Marketing hero + editorial standards + 4-step process + stats + FAQ + CTA | | /blog | Featured post promo + post grid + newsletter CTA | | /blog/[slug] | Blog post detail (placeholder) | | /reviews/[slug] | Canonical review-page mockup — shows all 6 XAC* components wired together (XACArticleDisclosure + XACStarRating + XACBuyButton + XACAffiliateLink + XACProsCons) | | /xac | XAC smoke test — exercises every variant of every XAC* component for visual QA | | /contact | Contact form | | /disclosure | FTC affiliate disclosure | | /privacy | Privacy policy | | /terms | Terms of service |

The marketing navbar uses <XMarkLayoutNavbar> (full path X/Mark/Layout/Navbar.vue) — the marketing layer's own app.vue uses XMarkNavbar and XMarkFooter, which don't actually exist. Use the XMarkLayout* names (and XFooter / XFooterXLegal for the simpler components in X/Footer/).


Versioning

This layer follows semver. Consuming sites should pin to a minor version:

{
  "dependencies": {
    "@xenterprises/nuxt-x-affiliate": "^0.2.0"
  }
}

v0.2.0 — XAC* component family for review sites

v0.2.0 ships a parallel component API aimed specifically at niche review sites (coffee-mug-warmer reviews, ergonomic chair reviews, etc.) that are affiliates of Amazon, Walmart, etc. The new XAC* family wraps the existing useAffiliateLink / useAffiliateTracker plumbing with a cleaner review-site API: smart defaults, half-star ratings, FTC disclosures baked in, multi-merchant support, and slot-based links for custom designs.

The X/Aff/* family is still around for the broader affiliate product pages — they're not being removed.

Install

// nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    ['@xenterprises/nuxt-x-marketing', { install: true }],
    ['@xenterprises/nuxt-x-schema', { install: true }],
    ['@xenterprises/nuxt-x-affiliate', { install: true }],
  ],
})
// app.config.ts
export default defineAppConfig({
  xAffiliateContent: {
    currency: 'USD',
    disclosure: {
      text: 'Affiliate link. We may earn a commission at no cost to you.',
      position: 'above', // 'above' | 'below' | 'none'
    },
    merchants: {
      amazon: { tagValue: 'yourtag-20' },
      walmart: { tagValue: 'TODO' },
    },
  },
})

Components

All six XAC* components are auto-imported. See docs/XAC.md for the full reference and migration guide.

| Component | Use for | | --- | --- | | <XACStarRating> | Half-star ratings with proper aria. | | <XACBuyButton> | The moneymaker — image, price, CTA, disclosure, tagging, tracking. | | <XACAffiliateLink> | Slot-based link when you want full visual control. | | <XACProsCons> | 1- or 2-column pros / cons, auto-collapses if one side is empty. | | <XACDisclosure> | FTC disclosure text in 4 visual variants. | | <XACArticleDisclosure> | FTC 16 CFR §255.5 in-content snippet — sits at the top of every review / article page, in-flow with the article typography. Optional Published/Updated timestamps. |

Composable

const { taggedUrl, formatPrice, getMerchant, hasTag, trackClick } =
  useAffiliateContent();
  • taggedUrl(merchant, url) — appends the configured tag, idempotent.
  • formatPrice(amount) — Intl-formatted with the configured currency / locale.
  • getMerchant(id) / hasTag(id) — display metadata + tag presence.
  • trackClick(payload) — pushes affiliate_click to window.dataLayer if GTM / GA4 is loaded. Harmless noop otherwise.

Testing

npm test                # 93/93 should pass
npm run test:watch      # watch mode

Tests live in tests/ and use Vitest + happy-dom + @vue/test-utils.

Migration: review pages

If your consuming site has app/pages/reviews/[slug].vue with inline markup for stars, buy buttons, and disclosures, the migration is two diffs. See the full guide at docs/XAC.md (or open the migration section in the playground's internal docs).